Android RIL 动态切换 4G 模块适配

简介: Android RIL 动态切换 4G 模块适配

这个补丁是我加在全志A40 4.4上面的做模块动态适配的 , 毋庸置疑 后面的Android 版本也是适用的 , 比如rockchip 也是没问题的 只是我懒得搞了而已。


调试4G模块非常简单 只是说早期模块经常切换 , 换了还得换固件 , 固件其实只是更换了so库而已。因为kernel driver 已经对vid/pid进行了适配。


这个补丁是直接在hal 层rild 做的vid/pid 然后选择so库文件的。

这个补丁主要修改了 Android RIL (Radio Interface Layer) 的部分代码,以支持动态切换不同的 3G/4G 模块。

修改的文件

  • android/hardware/ril/include/runtime/runtime.h
  • android/hardware/ril/rild/Android.mk
  • android/hardware/ril/rild/rild.c
  • android/hardware/ril/runtime-ril-port/Android.mk
  • android/hardware/ril/runtime-ril-port/runtime_port.c

文件修改说明

runtime.h

这是一个新文件,定义了一些函数和变量,用于启动 uevent 监视器,获取当前的 3G 端口设备,数据和类型。

Android.mk(在 rild 目录下)

在这个 makefile 文件中,添加了 libruntime-ril-portLOCAL_SHARED_LIBRARIES,这样 rild 就可以链接到这个新的库。

rild.c

这个文件中的修改主要是在 rild 的主函数中,添加了对 3G 模块类型的检测,并根据检测到的模块类型,动态地设置 RIL 库的路径。此外,还添加了对 uevent 的监视,当检测到 3G 模块的变化时,会重新启动 rild。

Android.mk(在 runtime-ril-port 目录下)

这是一个新文件,用于编译 runtime_port.c,生成 libruntime-ril-port 库。

runtime_port.c

这是一个新文件,实现了在 runtime.h 中定义的函数。这些函数主要用于检测当前的 3G 模块类型,以及启动对 uevent 的监视。

如何适配

  1. 将这个补丁应用到你的 Android 源码树中。
  2. 根据你的 3G 模块的特性,修改 runtime_port.c 中的 modem_3g_device_table
  3. 编译并刷入你的 Android 系统。
  4. 当你的设备启动时,rild 会自动检测当前的 3G 模块类型,并加载相应的 RIL 库。

diff --git a/android/hardware/ril/include/runtime/runtime.h b/android/hardware/ril/include/runtime/runtime.h
new file mode 100755
index 0000000000..c381b4f2f8
--- /dev/null
+++ b/android/hardware/ril/include/runtime/runtime.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2011-2013 Freescale Semiconductor, Inc.
+ */
+
+#ifndef RUNTIME_H
+#define RUNTIME_H
+
+extern int start_uevent_monitor(void);
+extern const char *runtime_3g_port_device(void);
+extern const char *runtime_3g_port_data(void);
+extern int runtime_3g_port_type(void);
+enum {
+ ME909S_MODEM = 0,
+ U9300C_MODEM,
+ EC20_MODEM,
+ EM8000_MODEM,
+ UNKNOWN_MODEM,
+};
+
+extern int current_modem_type;
+
+#endif
diff --git a/android/hardware/ril/rild/Android.mk b/android/hardware/ril/rild/Android.mk
index d8ca6d368d..83c0a1a130 100755
--- a/android/hardware/ril/rild/Android.mk
+++ b/android/hardware/ril/rild/Android.mk
@@ -13,7 +13,8 @@ LOCAL_SHARED_LIBRARIES := \
  liblog \
  libcutils \
  libril \
- libdl
+ libdl  \
+ libruntime-ril-port
 
 # temporary hack for broken vendor rils
 #LOCAL_WHOLE_STATIC_LIBRARIES := \
diff --git a/android/hardware/ril/rild/rild.c b/android/hardware/ril/rild/rild.c
index 4a555f275c..e79cd3cb71 100755
--- a/android/hardware/ril/rild/rild.c
+++ b/android/hardware/ril/rild/rild.c
@@ -36,10 +36,19 @@
 #include "hardware/qemu_pipe.h"
 
 #include "radio_monitor.h"
-
+#include <runtime/runtime.h>
 #define LIB_PATH_PROPERTY   "rild.libpath"
 #define LIB_ARGS_PROPERTY   "rild.libargs"
 #define MAX_LIB_ARGS        16
+#define MAX_POLL_DEVICE_CNT 8
+
+
+
+
+#define REFERENCE_RIL_ME909S_PATH "/system/lib/libreference_me909s-ril.so"
+#define REFERENCE_RIL_U9300C_PATH "/system/lib/libreference_u9300c-ril.so"
+#define REFERENCE_RIL_EC20_PATH "/system/lib/libreference_ec20-ril.so"
+#define REFERENCE_RIL_EM8000_PATH "/system/lib/libreference_em8000-ril.so"
 
 static void usage(const char *argv0)
 {
@@ -64,6 +73,7 @@ static struct RIL_Env s_rilEnv = {
     RIL_onUnsolicitedResponse,
     RIL_requestTimedCallback
 };
+static int s_poll_device_cnt = 0;
 
 extern void RIL_startEventLoop();
 
@@ -109,44 +119,21 @@ int main(int argc, char **argv)
     char libPath[PROPERTY_VALUE_MAX];
     unsigned char hasLibArgs = 0;
     int ret = 0;
+ int modem_type = UNKNOWN_MODEM;
     int i;
  char platform[PROPERTY_VALUE_MAX] = {0};
 
  //is telephony platform?
  if(property_get("ro.sw.embeded.telephony", platform, NULL)){
    if(strcmp(platform, "true")) {
-     RLOGI("platform: wifi-only");
+     RLOGI("platform: wifi-only --1");
      radio_monitor();
    }else{
-     RLOGI("platform: telephony");
+     RLOGI("platform: telephony --2");
    }
  }
 
-#if 0
-    umask(S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IWOTH | S_IXOTH);
-    for (i = 1; i < argc ;) {
-        if (0 == strcmp(argv[i], "-l") && (argc - i > 1)) {
-            rilLibPath = argv[i + 1];
-            i += 2;
-        } else if (0 == strcmp(argv[i], "--")) {
-            i++;
-            hasLibArgs = 1;
-            break;
-        } else {
-            usage(argv[0]);
-        }
-    }
 
-    if (rilLibPath == NULL) {
-        if ( 0 == property_get(LIB_PATH_PROPERTY, libPath, NULL)) {
-            // No lib sepcified on the command line, and nothing set in props.
-            // Assume "no-ril" case.
-            goto done;
-        } else {
-            rilLibPath = libPath;
-        }
-    }
-#else
  ret = property_get(LIB_PATH_PROPERTY, libPath, NULL);
  if (ret <= 0) {
    /* nothing to do */
@@ -174,9 +161,62 @@ int main(int argc, char **argv)
    RLOGD("err: get rilLibPath failed\n");
    goto done;
  }
+ ///$shh$20180906$add$Dynamic switching module$
+  //Wait for device ready.
+   // if (rilLibPath == NULL) {
+   while(UNKNOWN_MODEM == modem_type){
+       modem_type = runtime_3g_port_type();
+       ALOGD("111 -Couldn't find proper modem, retrying...");
+       s_poll_device_cnt++;
+       if (s_poll_device_cnt > MAX_POLL_DEVICE_CNT){
+       /*
+       *Maybe no device right now, start to monitor
+       *hotplug event later.
+       */
+       start_uevent_monitor();
+       goto done;
+       }
+       sleep(5);
+   }
+    //}
 
-#endif
-    RLOGD("\nrilLibPath = %s\n\n", rilLibPath);
+ start_uevent_monitor();
+
+    switch (modem_type){
+   case ME909S_MODEM:
+   {
+     rilLibPath = REFERENCE_RIL_ME909S_PATH;
+     break;
+   }
+   case U9300C_MODEM:
+   {
+      rilLibPath = REFERENCE_RIL_U9300C_PATH;
+      break;
+   }
+   case EC20_MODEM:
+   {
+      rilLibPath = REFERENCE_RIL_EC20_PATH;
+      break;
+   }
+   case EM8000_MODEM:
+   {
+      rilLibPath = REFERENCE_RIL_EM8000_PATH;
+      printf("");
+      break;
+   }
+
+   if (rilLibPath == NULL) {
+     if ( 0 == property_get(LIB_PATH_PROPERTY, libPath, NULL)) {
+       // No lib sepcified on the command line, and nothing set in props.
+       // Assume "no-ril" case.
+       goto done;
+     } else {
+       rilLibPath = libPath;
+     }
+   }
+ }
+ RLOGD("shh->rilLibPath:%s,modem_type=%d",rilLibPath,modem_type);
+ ///$shh$20180906$add$Dynamic switching module$
 
     /* special override when in the emulator */
 #if 0 //ignore this by SW
diff --git a/android/hardware/ril/runtime-ril-port/Android.mk b/android/hardware/ril/runtime-ril-port/Android.mk
new file mode 100755
index 0000000000..fdf21f4844
--- /dev/null
+++ b/android/hardware/ril/runtime-ril-port/Android.mk
@@ -0,0 +1,19 @@
+# Copyright 2006 The Android Open Source Project
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+    runtime_port.c
+
+LOCAL_SHARED_LIBRARIES := \
+    libutils \
+    libcutils
+
+LOCAL_CFLAGS :=
+
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE:= libruntime-ril-port
+
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/android/hardware/ril/runtime-ril-port/runtime_port.c b/android/hardware/ril/runtime-ril-port/runtime_port.c
new file mode 100755
index 0000000000..f7682c7df1
--- /dev/null
+++ b/android/hardware/ril/runtime-ril-port/runtime_port.c
@@ -0,0 +1,452 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Copyright 2011-2015 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#define LOG_TAG "RIL"
+#include <utils/Log.h>
+
+#include <pthread.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/poll.h>
+#include <linux/netlink.h>
+
+#include <signal.h>
+#include <unistd.h>
+#include <runtime/runtime.h>
+
+int current_modem_type = UNKNOWN_MODEM;
+
+#define FAKE_PORT "/dev/ttyFAKEPort"
+/* Rild need a fake port to pass continue init job,
+ * return a fake port make it runable.
+ * Or the system will enter 15s in early suspend.
+ */
+
+struct modem_3g_device {
+ const char *idVendor;
+ const char *idProduct;
+ const char *deviceport; /* sending AT command */
+ const char *dataport; /* sending 3g data */
+ const char *name;
+ const int   type;
+};
+
+#define PATH_SIZE 1024
+#define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))
+static const char *USB_DIR_BASE = "/sys/bus/usb/devices/";
+
+static struct modem_3g_device modem_3g_device_table[] = {
+
+ {
+   .name           = "ME909S_MODEM",
+   .idVendor       = "12d1",
+   .idProduct      = "15c1",
+   .deviceport     = "/dev/ttyUSB0",
+   .dataport       = "/dev/ttyUSB2",
+   .type           = ME909S_MODEM,
+  },
+
+
+ {
+   .name     = "U9300C_MODEM",
+   .idVendor   = "1c9e",
+   .idProduct    = "9b3c",
+   .deviceport = "/dev/ttyUSB1",
+   .dataport   = "/dev/ttyUSB2",
+   .type     = U9300C_MODEM,
+ },
+ {
+   .name     = "EC20_MODEM",
+   .idVendor   = "2c7c",
+   .idProduct    = "0125",
+   .deviceport = "/dev/ttyUSB0",
+   .dataport   = "/dev/ttyUSB2",
+   .type     = EC20_MODEM,
+ },
+ {
+   .name   = "EM8000_MODEM",
+   .idVendor = "19d2",
+   .idProduct  = "0532",
+   .deviceport     = "/dev/ttyUSB0",
+   .dataport = "/dev/ttyUSB2",
+   .type   = EM8000_MODEM,
+ },
+};
+
+/* -------------------------------------------------------------- */
+
+#define DEBUG_UEVENT 0
+#define UEVENT_PARAMS_MAX 32
+
+enum uevent_action { action_add, action_remove, action_change };
+
+struct uevent {
+    char *path;
+    enum uevent_action action;
+    char *subsystem;
+    char *param[UEVENT_PARAMS_MAX];
+    unsigned int seqnum;
+};
+
+static void dump_uevent(struct uevent *event);
+
+int readfile(char *path, char *content, size_t size)
+{
+ int ret;
+ FILE *f;
+ f = fopen(path, "r");
+ if (f == NULL)
+   return -1;
+
+ ret = fread(content, 1, size, f);
+ fclose(f);
+ return ret;
+}
+
+int is_device_equal(struct modem_3g_device *device,
+        const char *idv, const char *idp)
+{
+ long pvid = 0xffff, ppid = 0xffff;
+ long t_vid, t_pid;
+ if (device == NULL)
+   return 0;
+ t_vid = strtol(device->idVendor, NULL, 16);
+ t_pid = strtol(device->idProduct, NULL, 16);
+ pvid = strtol(idv, NULL, 16);
+ ppid = strtol(idp, NULL, 16);
+
+ return (t_vid == pvid && t_pid == ppid);
+}
+
+struct modem_3g_device *
+find_devices_in_table(const char *idvendor, const char *idproduct)
+{
+ int i;
+ int size = ARRAY_SIZE(modem_3g_device_table);
+ struct modem_3g_device *device;
+
+ for (i = 0; i < size; i++) {
+   device = &modem_3g_device_table[i];
+
+   if (is_device_equal(device, idvendor, idproduct)) {
+     ALOGI("Runtime 3G port found matched device with "
+          "Name:%s idVendor:%s idProduct:%s",
+          device->name, device->idVendor, device->idProduct);
+
+     return device;
+   }
+ }
+
+ return NULL;
+}
+
+struct modem_3g_device *find_matched_device(void)
+{
+ struct dirent *dent;
+ DIR *usbdir;
+ struct modem_3g_device *device = NULL;
+ char *path, *path2;
+ char idvendor[64];
+ char idproduct[64];
+ int ret, i;
+
+ path = malloc(PATH_SIZE);
+    if (!path)
+        return NULL;
+
+ path2 = malloc(PATH_SIZE);
+    if (!path2) {
+        free(path);
+        return NULL;
+    }
+
+ usbdir = opendir(USB_DIR_BASE);
+ if (usbdir == NULL) {
+   free(path);
+   free(path2);
+   return NULL;
+ }
+
+ memset(path, 0, PATH_SIZE);
+ memset(path2, 0, PATH_SIZE);
+
+ while ((dent = readdir(usbdir)) != NULL) {
+   if (strcmp(dent->d_name, ".") == 0
+       || strcmp(dent->d_name, "..") == 0)
+     continue;
+   memset(idvendor, 0, sizeof(idvendor));
+   memset(idproduct, 0, sizeof(idproduct));
+   path = strcpy(path, USB_DIR_BASE);
+   path = strcat(path, dent->d_name);
+   strcpy(path2, path);
+   path = strcat(path, "/idVendor");
+   path2 = strcat(path2, "/idProduct");
+
+   ret = readfile(path, idvendor, 4);
+   if (ret <= 0)
+     continue;
+   ret = readfile(path2, idproduct, 4);
+   if (ret <= 0)
+     continue;
+   device = find_devices_in_table(idvendor, idproduct);
+   if (device != NULL)
+     goto out;
+ }
+
+ if (device == NULL)
+   ALOGI("Runtime 3G can't find supported modem");
+out:
+ closedir(usbdir);
+ free(path);
+ free(path2);
+
+ return device;
+}
+
+
+const char *runtime_3g_port_device(void)
+{
+ struct modem_3g_device *device;
+ device = find_matched_device();
+ if (device == NULL)
+   return FAKE_PORT;
+
+ /* Set gobal modem type. */
+ current_modem_type = device->type;
+
+ ALOGI("Current modem type = %d", current_modem_type);
+
+ return device->deviceport;
+}
+
+const char *runtime_3g_port_data(void)
+{
+ struct modem_3g_device *device;
+
+ device = find_matched_device();
+ if (device == NULL)
+   return FAKE_PORT;
+ return device->dataport;
+}
+
+int runtime_3g_port_type(void)
+{
+       struct modem_3g_device *device;
+       int type = UNKNOWN_MODEM;
+       if (UNKNOWN_MODEM == current_modem_type){
+               if (NULL != (device = find_matched_device())){
+                       /* Set gobal modem type. */
+                       type = device->type;
+               }
+       }else{
+               type = current_modem_type;
+       }
+
+       ALOGI("Current modem type = %d", type);
+
+       return type;
+}
+
+static void free_uevent(struct uevent *event)
+{
+    int i;
+    free(event->path);
+    free(event->subsystem);
+    for (i = 0; i < UEVENT_PARAMS_MAX; i++) {
+     if (!event->param[i])
+       break;
+     free(event->param[i]);
+    }
+    free(event);
+}
+
+static int dispatch_uevent(struct uevent *event)
+{
+ /* if it's a usb tty event in our table. make the rild reboot. */
+ int i;
+ int ret;
+ for (i = 0; i < UEVENT_PARAMS_MAX; i++) {
+   if (!event->param[i])
+     break;
+   if (strncmp(event->param[i], "PRODUCT=", 8) == 0) {
+     char vbuf[5], pbuf[5];
+     ret = sscanf(event->param[i],
+            "PRODUCT=%4s/%4s/", vbuf, pbuf);
+     if (ret < 0)
+       return -1;
+     if (find_devices_in_table(vbuf, pbuf))
+       alarm(1);
+     /* Restart in 1 second, since USB usually have
+      * many devices, this avoid rild restart too
+      * many times. */
+   }
+ }
+ return 0;
+}
+
+int process_uevent_message(int sock)
+{
+ char buffer[64 * 1024];
+ char *s = buffer, *p;
+ char *end;
+ int count, param_idx = 0, ret;
+ struct uevent *event;
+ count = recv(sock, buffer, sizeof(buffer), 0);
+ if (count < 0) {
+   ALOGE("Error receiving uevent (%s)", strerror(errno));
+   return -errno;
+ }
+ event = malloc(sizeof(struct uevent));
+ if (!event) {
+   ALOGE("Error allcating memroy (%s)", strerror(errno));
+   return -errno;
+ }
+ memset(event, 0, sizeof(struct uevent));
+
+ end = s + count;
+
+ for (p = s; *p != '@'; p++)
+   ;
+ p++;
+ event->path = strdup(p);
+ s += strlen(s) + 1;
+
+ while (s < end) {
+   if (!strncmp(s, "ACTION=", strlen("ACTION="))) {
+     char *a = s + strlen("ACTION=");
+     if (!strcmp(a, "add"))
+       event->action = action_add;
+     else if (!strcmp(a, "change"))
+       event->action = action_change;
+     else if (!strcmp(a, "remove"))
+       event->action = action_remove;
+   } else if (!strncmp(s, "SEQNUM=", strlen("SEQNUM=")))
+     event->seqnum = atoi(s + strlen("SEQNUM="));
+   else if (!strncmp(s, "SUBSYSTEM=", strlen("SUBSYSTEM=")))
+     event->subsystem = strdup(s + strlen("SUBSYSTEM="));
+   else
+     event->param[param_idx++] = strdup(s);
+   s += strlen(s) + 1;
+ }
+
+ ret = dispatch_uevent(event);
+#if DEBUG_UEVENT
+ dump_uevent(event);
+#endif
+ free_uevent(event);
+ return ret;
+}
+
+static void dump_uevent(struct uevent *event)
+{
+    int i;
+
+    ALOGD("[UEVENT] Sq: %u S: %s A: %d P: %s",
+       event->seqnum, event->subsystem, event->action, event->path);
+    for (i = 0; i < UEVENT_PARAMS_MAX; i++) {
+     if (!event->param[i])
+       break;
+     ALOGD("%s", event->param[i]);
+    }
+}
+
+void restart_rild(int p)
+{
+ ALOGI("3G Modem changed,RILD will restart...");
+ exit(-1);
+}
+
+void *usb_tty_monitor_thread(void *arg)
+{
+ struct sockaddr_nl nladdr;
+ struct pollfd pollfds[2];
+ int uevent_sock;
+ int ret, max = 0;
+ int uevent_sz = 64 * 1024;
+ int timeout = -1;
+ struct sigaction timeoutsigact;
+
+ ALOGI("3G modem monitor thread is start");
+
+ timeoutsigact.sa_handler = restart_rild;
+ sigemptyset(&timeoutsigact.sa_mask);
+ sigaddset(&timeoutsigact.sa_mask, SIGALRM);
+ sigaction(SIGALRM, &timeoutsigact, 0);
+
+ memset(&nladdr, 0, sizeof(nladdr));
+ nladdr.nl_family = AF_NETLINK;
+ nladdr.nl_pid = getpid();
+ nladdr.nl_groups = 0xffffffff;
+
+ uevent_sock = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);
+ if (uevent_sock < 0) {
+   ALOGE(" Netlink socket faild, usb monitor exiting...");
+   return NULL;
+ }
+
+ if (setsockopt(uevent_sock, SOL_SOCKET, SO_RCVBUFFORCE, &uevent_sz,
+          sizeof(uevent_sz)) < 0) {
+   ALOGE("Unable to set uevent socket options: %s", strerror(errno));
+   return NULL;
+ }
+
+ if (bind(uevent_sock, (struct sockaddr *) &nladdr, sizeof(nladdr)) < 0) {
+      ALOGE("Unable to bind uevent socket: %s", strerror(errno));
+      return NULL;
+ }
+ pollfds[0].fd = uevent_sock;
+ pollfds[0].events = POLLIN;
+
+ ret = fcntl(uevent_sock,F_SETFL, O_NONBLOCK);
+ if (ret < 0)
+   ALOGE("Error on fcntl:%s", strerror(errno));
+
+ while (1) {
+   ret = poll(pollfds, 1, timeout);
+
+   switch (ret) {
+   case 0:
+     ALOGD("poll timeout");
+     continue;
+   case -1:
+     ALOGD("poll error:%s", strerror(errno));
+     break;
+
+   default:
+     if (pollfds[0].revents & POLLIN)
+       process_uevent_message(uevent_sock);
+   }
+ }
+
+ close(uevent_sock);
+}
+
+int start_uevent_monitor(void)
+{
+ pthread_t pth_uevent_monitor;
+ return pthread_create(&pth_uevent_monitor, NULL,
+           usb_tty_monitor_thread, NULL);
+}
+
+


相关文章
|
11天前
|
Android开发
Android MediaTek 平台增加UART接口的红外模块支持,支持NEC红外遥控
Android MediaTek 平台增加UART接口的红外模块支持,支持NEC红外遥控
20 0
|
10天前
|
Android开发
Android热补丁动态修复实践,腾讯&字节&网易&华为Android面试题分享
Android热补丁动态修复实践,腾讯&字节&网易&华为Android面试题分享
|
11天前
|
XML Java Android开发
Android控件动态使用 (转)
Android控件动态使用 (转)
11 1
|
11天前
|
Android开发
Android 集成vendor下的模块
Android 集成vendor下的模块
15 0
|
11天前
|
XML Android开发 数据格式
android 9 Systemui 动态隐藏导航栏
android 9 Systemui 动态隐藏导航栏
13 0
|
11天前
|
Shell Android开发 开发者
Android系统 自定义动态修改init.custom.rc
Android系统 自定义动态修改init.custom.rc
42 0
|
11天前
|
测试技术 Android开发 开发者
RK3568 Android系统客制化动态替换ro任意属性
RK3568 Android系统客制化动态替换ro任意属性
40 1
|
11天前
|
存储 Linux Android开发
RK3568 Android/Linux 系统动态更换 U-Boot/Kernel Logo
RK3568 Android/Linux 系统动态更换 U-Boot/Kernel Logo
100 0
|
11天前
|
Android开发
Android 支持 ap6236 wifi 模块补丁
Android 支持 ap6236 wifi 模块补丁
24 0
|
11天前
|
Android开发
Android 动态修改参数配置
Android 动态修改参数配置
15 0