标签

 android 

相关的文章:

The Keyword -

How Android keeps you and your devices safe

Android bot head in the center of a collage of icons indicating security, headphones and keys as well as a notification saying “tracker traveling with you”.

AI生成摘要 Google将在今年夏天推出更新版的Find My Device,支持离线设备和没有定位功能的设备。新版Find My Device将利用全球超过10亿的Android设备,通过蓝牙接近度来帮助用户定位遗失物品,同时保证用户隐私安全。用户可以通过连接Tile、Chipolo和Pebblebee等蓝牙跟踪器,将日常物品如钥匙、钱包或行李变得可追踪。此外,Pixel Buds、Sony和JBL等耳机也将加入Find My Device生态系统。

相关推荐 去reddit讨论

道轲 -

Android系统启动流程剖析

Android 手机从长按开机到应用的启动阶段都经历了些什么呢?Android 启动过程涉及一系列操作,首先是启动 ROM,接着是引导加载程序、内核启动、init、Zygote 和 SystemServer 创建等过程。整个过程涉及到虚拟机的启动、Binder 线程池的创建以及各项系统服务启动等过程。熟悉 APP 启动过程还可以帮助我们打破性能优化瓶颈,助力于启动性能的提升。下面是启动过程涉及到的关键进程和服务: 本文基于 Android 13 最新代码来分析 Android 系统及应用启动过程。 Init 进程启动 init 进程在 Android 系统中扮演着非常重要的角色,它不仅仅是系统的第一个用户进程(pid 为 1),还负责整个系统的启动和进程的管理。当 Android 设备启动时,Bootloader 会初始化硬件设备,并在硬件自检之后将控制权交给 Kernel,Kernel 会加载 Android 系统的内核。而 init 进程的启动过程就发生在内核启动后,接着调用 system/core/init/main.cpp 来创建 init 进程,在此期间读取 init.rc 文件,启动 zygote 进程、servicemanager 等,最后完成初始化工作。下面来结合源码具体分析 init 启动后都做了些什么: system/core/init/main.cpp 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 using namespace android::init; int main(int argc, char** argv) { #if __has_feature(address_sanitizer) __asan_set_error_report_callback(AsanReportCallback); #elif __has_feature(hwaddress_sanitizer) __hwasan_set_error_report_callback(AsanReportCallback); #endif // Boost prio which will be restored later setpriority(PRIO_PROCESS, 0, -20); if (!strcmp(basename(argv[0]), "ueventd")) { return ueventd_main(argc, argv); } if (argc > 1) { if (!strcmp(argv[1], "subcontext")) { android::base::InitLogging(argv, &android::base::KernelLogger); const BuiltinFunctionMap& function_map = GetBuiltinFunctionMap(); return SubcontextMain(argc, argv, &function_map); } if (!strcmp(argv[1], "selinux_setup")) { return SetupSelinux(argv); } if (!strcmp(argv[1], "second_stage")) { return SecondStageMain(argc, argv); } } return FirstStageMain(argc, argv); } 内核启动后会调用到这里的 main 函数,主要执行了以下几个操作: ueventd_main:该函数是 ueventd 守护进程的主要函数,通过 netlink scoket 响应内核生成的 uevent 事件。它设置信号处理程序,创建用于侦听传入 uevent 的套接字,然后进入无限循环以处理 uevent 事件。 它其实也算是 init 进程,不过具体细节本文不再深究,后面会单独拎出来讲一讲。 SubcontextMain:subcontext 进程的主要处理函数,负责为 init 进程创建和管理子进程,并通过 socket 与 init 进行通信。 SetupSelinux:该函数会尝试加载在init.rc文件中定义的SELinux策略文件。如果文件存在,则在系统启动时加载所需的SELinux策略,并执行其他相关的SELinux设置。这些设置包括设置SELinux上下文,为设备中的所有进程和授权的服务分配安全策略和权限等。 FirstStageMain:init 执行的第一阶段,主要做一些初始化文件系统、kernel 日志以及为第二阶段设定一些环境变量。 SecondStageMain:init 执行的第二阶段,主要负责加载属性文件、启动属性服务、加载 init.rc 文件并创建和启动子进程。 下面我们着重分析一下 init.rc 加载及子进程启动过程。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 int SecondStageMain(int argc, char** argv) { if (REBOOT_BOOTLOADER_ON_PANIC) { InstallRebootSignalHandlers(); } ...... // 加载属性文件 PropertyInit(); ...... // 启动属性服务 StartPropertyService(&property_fd); // Make the time that init stages started available for bootstat to log. RecordStageBoottimes(start_time); ...... ActionManager& am = ActionManager::GetInstance(); ServiceList& sm = ServiceList::GetInstance(); // 加载解析 init.rc LoadBootScripts(am, sm); } init.rc 文件的解析是在 LoadBootScripts() 中完成的,详见下面的实现: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 static void LoadBootScripts(ActionManager& action_manager, ServiceList& service_list) { Parser parser = CreateParser(action_manager, service_list); std::string bootscript = GetProperty("ro.boot.init_rc", ""); if (bootscript.empty()) { parser.ParseConfig("/system/etc/init/hw/init.rc"); if (!parser.ParseConfig("/system/etc/init")) { late_import_paths.emplace_back("/system/etc/init"); } // late_import is available only in Q and earlier release. As we don't // have system_ext in those versions, skip late_import for system_ext. parser.ParseConfig("/system_ext/etc/init"); if (!parser.ParseConfig("/vendor/etc/init")) { late_import_paths.emplace_back("/vendor/etc/init"); } if (!parser.ParseConfig("/odm/etc/init")) { late_import_paths.emplace_back("/odm/etc/init"); } if (!parser.ParseConfig("/product/etc/init")) { late_import_paths.emplace_back("/product/etc/init"); } } else { parser.ParseConfig(bootscript); } } Init 进程依次解析不同路径下谷歌内置 system、SoC 厂商 vendor 以及 odm 厂商的 init.rc 文件并执行启动相关进程。init.rc 本质上只是一个语法文件,至于具体的 Service 和 Action 解析逻辑是在 ServiceParser 和 ActionParser 中处理,至于最终执行逻辑则是由解析处理器将所有 Service 和 Action 封装到 ServiceList 和 ActionList 单例对象中集中处理的。这里以 init.zygote64_32.rc 中一段启动 zygote 的 rc 代码为例: Note:system/core/rootdir/init.rc 中通过 import 动态引入了基于不同平台的 zygote 进程启动配置,以下仅以常见的 init.zygote64_32.rc 平台为例。 service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server --socket- name=zygote class main priority -20 user root group root readproc reserved_disk socket zygote stream 660 root system socket usap_pool_primary stream 660 root system onrestart exec_background - system system -- /system/bin/vdc volume abort_fuse onrestart write /sys/power/state on onrestart restart audioserver onrestart restart cameraserver onrestart restart media onrestart restart media.tuner onrestart restart netd onrestart restart wificond task_profiles ProcessCapacityHigh MaxPerformance critical window=${zygote.critical_window.minute:-off} target=zygote-fatal Service 是一个服务类型的程序,以 service 开头,由 init 进程启动,其中每一个 service ,在启动时会通过 fork 方式生成子进程。其中,zygote 是进程的名字,/system/bin/app_process 是执行程序的路径,它是通过编译生成的,其包含了 main() 函数在内的一系列代码。在编译过程中,编译器会将源代码编译成目标文件,并将目标文件链接成可执行文件。在链接过程中,编译器会将不同的目标文件链接到一起,形成一个统一的可执行文件。在这个过程中, app_process 可执行文件会链接到 app_main.cpp 文件中定义的 main() 函数,因此运行 app_process 可执行文件时,系统会自动调用 app_main.cpp 文件中的 main() 函数。 后面几项则是传递给执行程序的参数,其中 --start-system-server 表示在 Zygote 进程启动后需要启动 System Server 进程。Zygote 进程是使用 Socket 来进行跨进程通信的,所以会创建一个名为 zygote 的 socket,660 表示访问权限 rw-rw----,表示文件拥有者和同一群组用户具有读写权限。 了解更多关于 init.rc 语法约定参考:Android启动初始化 init.rc 详解 接着 zygote 进程通过以下代码被启动: system/core/init/service.cpp 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 Result<void> Service::Start() { ... pid_t pid = -1; if (namespaces_.flags) { pid = clone(nullptr, nullptr, namespaces_.flags | SIGCHLD, nullptr); } else { pid = fork(); } if (pid == 0) { umask(077); cgroups_activated.CloseWriteFd(); setsid_finished.CloseReadFd(); RunService(descriptors, std::move(cgroups_activated), std::move(setsid_finished)); _exit(127); } else { cgroups_activated.CloseReadFd(); setsid_finished.CloseWriteFd(); } if (pid < 0) { pid_ = 0; return ErrnoError() << "Failed to fork"; } ... } 可以看到最终是通过 clone 或者 fork 的方式来创建一个进程,并返回 pid,其实最终它们都是通过系统调用基于父进程孵化出一个子进程,并与当前进程共享地址空间和文件描述符等资源。 zygote 进程 通过上面分析我们知道,zygote 进程在启动后会执行到 app_main.cpp 的 main 函数中: frameworks/base/cmds/app_process/app_main.cpp 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 #if defined(__LP64__) static const char ABI_LIST_PROPERTY[] = "ro.product.cpu.abilist64"; static const char ZYGOTE_NICE_NAME[] = "zygote64"; #else static const char ABI_LIST_PROPERTY[] = "ro.product.cpu.abilist32"; static const char ZYGOTE_NICE_NAME[] = "zygote"; #endif int main(int argc, char* const argv[]) { AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv)); ... // Parse runtime arguments. Stop at first unrecognized option. bool zygote = false; bool startSystemServer = false; bool application = false; String8 niceName; String8 className; ++i; // Skip unused "parent dir" argument. while (i < argc) { const char* arg = argv[i++]; // 通过 init.rc 中约定的 `--zygote` 来判断是zygote进程 if (strcmp(arg, "--zygote") == 0) { zygote = true; niceName = ZYGOTE_NICE_NAME; } else if (strcmp(arg, "--start-system-server") == 0) { startSystemServer = true; } else if (strcmp(arg, "--application") == 0) { application = true; } else if (strncmp(arg, "--nice-name=", 12) == 0) { niceName.setTo(arg + 12); } else if (strncmp(arg, "--", 2) != 0) { className.setTo(arg); break; } else { --i; break; } } ... if (!niceName.isEmpty()) { runtime.setArgv0(niceName.string(), true /* setProcName */); } if (zygote) { // 进入 AppRunTime 中继续执行,准备穿越到 Java 世界 runtime.start("com.android.internal.os.ZygoteInit", args, zygote); } else if (className) { runtime.start("com.android.internal.os.RuntimeInit", args, zygote); } else { fprintf(stderr, "Error: no class name or --zygote supplied.\n"); app_usage(); LOG_ALWAYS_FATAL("app_process: no class name or --zygote supplied."); } } 通过上述代码我们可以发现,zygote 进程通过 --zygote 参数标记,如果是 zygote 进程,最终会执行到 AppRuntime.start 中: frameworks/base/core/jni/AndroidRuntime.cpp 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 /* * Start the Android runtime. This involves starting the virtual machine * and calling the "static void main(String[] args)" method in the class * named by "className". * * Passes the main function two arguments, the class name and the specified * options string. */ void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote) { ALOGD(">>>>>> START %s uid %d <<<<<<\n", className != NULL ? className : "(unknown)", getuid()); static const String8 startSystemServer("start-system-server"); // Whether this is the primary zygote, meaning the zygote which will fork system server. bool primary_zygote = false; ... /* start the virtual machine */ JniInvocation jni_invocation; jni_invocation.Init(NULL); JNIEnv* env; if (startVm(&mJavaVM, &env, zygote, primary_zygote) != 0) { return; } onVmCreated(env); /* * Register android functions. */ if (startReg(env) < 0) { ALOGE("Unable to register all android natives\n"); return; } ... /* * Start VM. This thread becomes the main thread of the VM, and will * not return until the VM exits. */ char* slashClassName = toSlashClassName(className != NULL ? className : ""); jclass startClass = env->FindClass(slashClassName); if (startClass == NULL) { ALOGE("JavaVM unable to locate class '%s'\n", slashClassName); /* keep going */ } else { jmethodID startMeth = env->GetStaticMethodID(startClass, "main", "([Ljava/lang/String;)V"); if (startMeth == NULL) { ALOGE("JavaVM unable to find main() in '%s'\n", className); /* keep going */ } else { env->CallStaticVoidMethod(startClass, startMeth, strArray); #if 0 if (env->ExceptionCheck()) threadExitUncaughtException(env); #endif } } free(slashClassName); ALOGD("Shutting down VM\n"); if (mJavaVM->DetachCurrentThread() != JNI_OK) ALOGW("Warning: unable to detach main thread\n"); if (mJavaVM->DestroyJavaVM() != 0) ALOGW("Warning: VM did not shut down cleanly\n"); } 通过这部分代码我们了解到,jni 层的 AndroidRuntime 的 start 函数主要完成了三件事: 启动 zygote 进程的虚拟机 完成 JNI 方法注册(在 VM 上注册 Android 本地方法,只为了能够让 native 调用到 Java 层方法) 调用 Java 层的 com.android.internal.os.ZygoteInit#main 方法 至此,终于来到了我们熟悉的 Java 世界了,继续 zygote 的初始化逻辑: com.android.internal.os.ZygoteInit 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 /** * This is the entry point for a Zygote process. It creates the Zygote server, loads resources, * and handles other tasks related to preparing the process for forking into applications. * * This process is started with a nice value of -20 (highest priority). All paths that flow * into new processes are required to either set the priority to the default value or terminate * before executing any non-system code. The native side of this occurs in SpecializeCommon, * while the Java Language priority is changed in ZygoteInit.handleSystemServerProcess, * ZygoteConnection.handleChildProc, and Zygote.childMain. * * @param argv Command line arguments used to specify the Zygote's configuration. */ @UnsupportedAppUsage public static void main(String[] argv) { ZygoteServer zygoteServer = null; Runnable caller; try { ... boolean startSystemServer = false; String zygoteSocketName = "zygote"; boolean enableLazyPreload = false; for (int i = 1; i < argv.length; i++) { if ("start-system-server".equals(argv[i])) { startSystemServer = true; } else if ("--enable-lazy-preload".equals(argv[i])) { enableLazyPreload = true; } else if (argv[i].startsWith(ABI_LIST_ARG)) { abiList = argv[i].substring(ABI_LIST_ARG.length()); } else if (argv[i].startsWith(SOCKET_NAME_ARG)) { zygoteSocketName = argv[i].substring(SOCKET_NAME_ARG.length()); } else { throw new RuntimeException("Unknown command line argument: " + argv[i]); } } // In some configurations, we avoid preloading resources and classes eagerly. // In such cases, we will preload things prior to our first fork. if (!enableLazyPreload) { preload(bootTimingsTraceLog); } zygoteServer = new ZygoteServer(isPrimaryZygote); if (startSystemServer) { Runnable r = forkSystemServer(abiList, zygoteSocketName, zygoteServer); // {@code r == null} in the parent (zygote) process, and {@code r != null} in the // child (system_server) process. if (r != null) { r.run(); return; } } Log.i(TAG, "Accepting command socket connections"); // The select loop returns early in the child process after a fork and // loops forever in the zygote. caller = zygoteServer.runSelectLoop(abiList); } catch (Throwable ex) { Log.e(TAG, "System zygote died with fatal exception", ex); throw ex; } finally { if (zygoteServer != null) { zygoteServer.closeServerSocket(); } } // We're in the child process and have exited the select loop. Proceed to execute the // command. if (caller != null) { caller.run(); } } ZygoteInit#main 中大致做了以下几件事: preload:预加载系统基础类、资源文件、共享库等。 ZygoteServer:创建 Socket 服务端,用于和其他进程通信。 forkSystemServer:fork 出 system_server 子进程,下面会分析到。 ZygoteServer#runSelectLoop:socket 循环监听 AMS 用于创建应用进程的请求。 那么我简单总结一下,zygote 进程负责创建虚拟机,并注册 JNI 方法,成为 Java 进程的母体,用于持续孵化 Java 进程。在创建完system_server 进程后调用 runSelectLoop,随时等待接收创建新进程的请求并立即唤醒开始执行相应创建工作。 system_server 进程 Zygote 的初始化过程涉及到 system_server 进程的创建和启动,那我们就来看下 system_server 进程的启动过程: com.android.internal.os.ZygoteInit 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 /** * Prepare the arguments and forks for the system server process. * * @return A {@code Runnable} that provides an entrypoint into system_server code in the child * process; {@code null} in the parent. */ private static Runnable forkSystemServer(String abiList, String socketName, ZygoteServer zygoteServer) { String[] args = { "--setuid=1000", "--setgid=1000", "--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,1021,1023," + "1024,1032,1065,3001,3002,3003,3006,3007,3009,3010,3011", "--capabilities=" + capabilities + "," + capabilities, "--nice-name=system_server", "--runtime-args", "--target-sdk-version=" + VMRuntime.SDK_VERSION_CUR_DEVELOPMENT, // system_server 执行程序的类名 "com.android.server.SystemServer", }; ... ZygoteArguments parsedArgs; int pid; try { ... /* Request to fork the system server process */ pid = Zygote.forkSystemServer( parsedArgs.mUid, parsedArgs.mGid, parsedArgs.mGids, parsedArgs.mRuntimeFlags, null, parsedArgs.mPermittedCapabilities, parsedArgs.mEffectiveCapabilities); } catch (IllegalArgumentException ex) { throw new RuntimeException(ex); } /* For child process */ if (pid == 0) { if (hasSecondZygote(abiList)) { waitForSecondaryZygote(socketName); } zygoteServer.closeServerSocket(); return handleSystemServerProcess(parsedArgs); } return null; } /** * Finish remaining work for the newly forked system server process. */ private static Runnable handleSystemServerProcess(ZygoteArguments parsedArgs) { if (parsedArgs.mNiceName != null) { Process.setArgV0(parsedArgs.mNiceName); } ... if (parsedArgs.mInvokeWith != null) { ... throw new IllegalStateException("Unexpected return from WrapperInit.execApplication"); } else { ClassLoader cl = getOrCreateSystemServerClassLoader(); if (cl != null) { Thread.currentThread().setContextClassLoader(cl); } /* * Pass the remaining arguments to SystemServer. */ return ZygoteInit.zygoteInit(parsedArgs.mTargetSdkVersion, parsedArgs.mDisabledCompatChanges, parsedArgs.mRemainingArgs, cl); } /* should never reach here */ } public static Runnable zygoteInit(int targetSdkVersion, long[] disabledCompatChanges, String[] argv, ClassLoader classLoader) { if (RuntimeInit.DEBUG) { Slog.d(RuntimeInit.TAG, "RuntimeInit: Starting application from zygote"); } Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ZygoteInit"); RuntimeInit.redirectLogStreams(); // 设置异常处理机制 RuntimeInit.commonInit(); // 启动进程的Binder线程池 ZygoteInit.nativeZygoteInit(); return RuntimeInit.applicationInit(targetSdkVersion, disabledCompatChanges, argv, classLoader); } 看到上面的参数信息 args 包含了 com.android.server.SystemServer 就可以大致猜到最后应该是反射创建的实例。上面方法 ZygoteInit#forkSystemServer 继续追踪下去,system_server 进程的创建最终通过 Zygote.forkSystemServer 实现的: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 /** * Special method to start the system server process. In addition to the * common actions performed in forkAndSpecialize, the pid of the child * process is recorded such that the death of the child process will cause * zygote to exit. * * @param uid the UNIX uid that the new process should setuid() to after * fork()ing and and before spawning any threads. * @param gid the UNIX gid that the new process should setgid() to after * fork()ing and and before spawning any threads. * @param gids null-ok; a list of UNIX gids that the new process should * setgroups() to after fork and before spawning any threads. * @param runtimeFlags bit flags that enable ART features. * @param rlimits null-ok an array of rlimit tuples, with the second * dimension having a length of 3 and representing * (resource, rlim_cur, rlim_max). These are set via the posix * setrlimit(2) call. * @param permittedCapabilities argument for setcap() * @param effectiveCapabilities argument for setcap() * * @return 0 if this is the child, pid of the child * if this is the parent, or -1 on error. */ static int forkSystemServer(int uid, int gid, int[] gids, int runtimeFlags, int[][] rlimits, long permittedCapabilities, long effectiveCapabilities) { ZygoteHooks.preFork(); int pid = nativeForkSystemServer( uid, gid, gids, runtimeFlags, rlimits, permittedCapabilities, effectiveCapabilities); // Set the Java Language thread priority to the default value for new apps. Thread.currentThread().setPriority(Thread.NORM_PRIORITY); ZygoteHooks.postForkCommon(); return pid; } system_server 进程的最终实现是在 native 层,但是从注释来看,它的返回值如果是 0 则说明当前是子进程,否则可能依然是父进程或者 fork 失败。如果 system_server 进程创建成功,会依次执行到 handleSystemServerProcess 和 zygoteInit 方法,继续跟进到 RuntimeInit.applicationInit 一探究竟: frameworks/base/core/java/com/android/internal/os/RuntimeInit.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 protected static Runnable applicationInit(int targetSdkVersion, long[] disabledCompatChanges, String[] argv, ClassLoader classLoader) { ... // Remaining arguments are passed to the start class's static main return findStaticMain(args.startClass, args.startArgs, classLoader); } protected static Runnable findStaticMain(String className, String[] argv, ClassLoader classLoader) { Class<?> cl; try { cl = Class.forName(className, true, classLoader); } catch (ClassNotFoundException ex) { throw new RuntimeException( "Missing class when invoking static main " + className, ex); } Method m; try { m = cl.getMethod("main", new Class[] { String[].class }); } catch (NoSuchMethodException ex) { throw new RuntimeException( "Missing static main on " + className, ex); } catch (SecurityException ex) { throw new RuntimeException( "Problem getting static main on " + className, ex); } int modifiers = m.getModifiers(); if (! (Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers))) { throw new RuntimeException( "Main method is not public and static on " + className); } /* * This throw gets caught in ZygoteInit.main(), which responds * by invoking the exception's run() method. This arrangement * clears up all the stack frames that were required in setting * up the process. */ return new MethodAndArgsCaller(m, argv); } /** * Helper class which holds a method and arguments and can call them. This is used as part of * a trampoline to get rid of the initial process setup stack frames. */ static class MethodAndArgsCaller implements Runnable { /** method to call */ private final Method mMethod; /** argument array */ private final String[] mArgs; public MethodAndArgsCaller(Method method, String[] args) { mMethod = method; mArgs = args; } public void run() { try { mMethod.invoke(null, new Object[] { mArgs }); } catch (IllegalAccessException ex) { throw new RuntimeException(ex); } catch (InvocationTargetException ex) { Throwable cause = ex.getCause(); if (cause instanceof RuntimeException) { throw (RuntimeException) cause; } else if (cause instanceof Error) { throw (Error) cause; } throw new RuntimeException(ex); } } } 最终不出我们所料,findStaticMain 中传递的 className 对应的就是我们之前的 com.android.server.SystemServer 类,我们来看看它里面都做了些什么(SystemServer#main 会执行到 run 方法): frameworks/base/services/java/com/android/server/SystemServer.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 private void run() { try { ... // Prepare the main looper thread (this thread). android.os.Process.setThreadPriority( android.os.Process.THREAD_PRIORITY_FOREGROUND); android.os.Process.setCanSelfBackground(false); Looper.prepareMainLooper(); Looper.getMainLooper().setSlowLogThresholdMs( SLOW_DISPATCH_THRESHOLD_MS, SLOW_DELIVERY_THRESHOLD_MS); SystemServiceRegistry.sEnableServiceNotFoundWtf = true; ... // Initialize the system context. createSystemContext(); ... // Create the system service manager. mSystemServiceManager = new SystemServiceManager(mSystemContext); mSystemServiceManager.setStartInfo(mRuntimeRestart, mRuntimeStartElapsedTime, mRuntimeStartUptime); mDumper.addDumpable(mSystemServiceManager); LocalServices.addService(SystemServiceManager.class, mSystemServiceManager); // Prepare the thread pool for init tasks that can be parallelized SystemServerInitThreadPool tp = SystemServerInitThreadPool.start(); mDumper.addDumpable(tp); ... } finally { t.traceEnd(); // InitBeforeStartServices } // Setup the default WTF handler RuntimeInit.setDefaultApplicationWtfHandler(SystemServer::handleEarlySystemWtf); // Start services. try { t.traceBegin("StartServices"); startBootstrapServices(t); startCoreServices(t); startOtherServices(t); startApexServices(t); } catch (Throwable ex) { Slog.e("System", "******************************************"); Slog.e("System", "************ Failure starting system services", ex); throw ex; } finally { t.traceEnd(); // StartServices } ... // Loop forever. Looper.loop(); throw new RuntimeException("Main thread loop unexpectedly exited"); } 通过分析上面代码可以发现 system_server 为我们做了很多事情: 创建系统服务管理器 SystemServiceManager 启动引导服务(AMS、PMS等)、核心服务和其他各种服务 创建主线程 Looper 并一直循环,等待其他线程通过 Handler 发送消息再唤醒并处理。 nativeZygoteInit 上面的 com.android.internal.os.ZygoteInit#forkSystemServer 中 ZygoteInit.nativeZygoteInit() 方法主要从 native 层创建了进程的 Binder 线程池。值得注意的,这一切发生在 system_server 进程创建之后。最终可以追溯到: frameworks/native/libs/binder/ProcessState.cpp 1 2 3 4 5 6 7 8 9 10 11 12 void ProcessState::spawnPooledThread(bool isMain) { if (mThreadPoolStarted) { String8 name = makeBinderThreadName(); ALOGV("Spawning new pooled thread, name=%s\n", name.string()); sp<Thread> t = sp<PoolThread>::make(isMain); t->run(name.string()); pthread_mutex_lock(&mThreadCountLock); mKernelStartedThreads++; pthread_mutex_unlock(&mThreadCountLock); } } 系统启动日志分析 从 Android 开机开始到我们系统初始化完毕,这中间设有大量的关键日志,如果能够好好利用,对于我们排查一些系统性问题或者性能问题会有巨大帮助。以下列举部分关键节点的 Tag: 可查看启动耗时 boot_progress_start:表示 kernel 启动完成,第一个用户空间进程 init 进程启动 boot_progress_preload_start:zygote 进程开始预加载 class 资源(记录于 ZygoteInit#preload 调用时机) boot_progress_preload_end:zygote 进程 class 资源预加载结束 boot_progress_system_run:表示 system_server 进程启动(记录于 SystemServer#run 调用时机) boot_progress_pms_start:表示 PMS 服务启动并开始扫描应用的动作 boot_progress_pms_ready:表示 PMS 服务应用扫描完毕 boot_progress_ams_ready:表示 AMS 已经进入 systemReady 状态可以启动桌面应用了 boot_progress_enable_screen:标志着锁频界面已经点亮显示,开机完成并统计耗时 更多 native 层相关统计的 tag 参照:system/logging/logcat/event.logtags 我们可以额外留意一些系统进程和服务的相关日志,比如 zygote 运行相关日志: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 // Zygote64进程(Zygote): AndroidRuntime::start 11-23 14:36:51.260 557 557 D AndroidRuntime: >>>>>> START com.android.internal.os.ZygoteInit uid 0 <<<<<< // Zygote64进程: AndroidRuntime::startVm 11-23 14:36:51.304 557 557 D AndroidRuntime: CheckJNI is OFF // 执行ZygoteInit.preload() 11-23 14:36:52.134 557 557 D Zygote : begin preload // 执行ZygoteInit.preloadClasses(), 预加载3860个classes, 花费时长746ms 11-23 14:36:52.134 557 557 I Zygote : Preloading classes... 11-23 14:36:52.881 557 557 I Zygote : ...preloaded 3860 classes in 746ms. // 执行ZygoteInit.preloadClasses(), 预加载86组资源, 花费时长179ms 11-23 14:36:53.114 557 557 I Zygote : Preloading resources... 11-23 14:36:53.293 557 557 I Zygote : ...preloaded 86 resources in 179ms. // 执行ZygoteInit.preloadSharedLibraries() 11-23 14:36:53.494 557 557 I Zygote : Preloading shared libraries... 11-23 14:36:53.503 557 557 D Zygote : end preload // 执行com_android_internal_os_Zygote_nativeForkSystemServer(),成功fork出system_server进程 11-23 14:36:53.544 557 557 I Zygote : System server process 1274 has been created // Zygote开始进入runSelectLoop() 11-23 14:36:53.546 557 557 I Zygote : Accepting command socket connections system_server 进程运行相关日志: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 //进入system_server, 建立跟Zygote进程的socket通道 11-23 14:36:53.586 1274 1274 I Zygote : Process: zygote socket opened, supported ABIS: armeabi-v7a,armeabi // 执行SystemServer.run() 11-23 14:36:53.618 1274 1274 I SystemServer: Entered the Android system server! <===> boot_progress_system_run // 等待installd准备就绪 11-23 14:36:53.707 1274 1274 I Installer: Waiting for installd to be ready. //服务启动 11-23 14:36:53.732 1274 1274 I ActivityManager: Memory class: 192 //phase100 11-23 14:36:53.883 1274 1274 I SystemServiceManager: Starting phase 100 11-23 14:36:53.902 1274 1274 I SystemServer: Package Manager 11-23 14:37:03.816 1274 1274 I SystemServer: User Service ... 11-23 14:37:03.940 1274 1274 I SystemServer: Init Watchdog 11-23 14:37:03.941 1274 1274 I SystemServer: Input Manager 11-23 14:37:03.946 1274 1274 I SystemServer: Window Manager ... 11-23 14:37:04.081 1274 1274 I SystemServiceManager: Starting com.android.server.MountService$Lifecycle 11-23 14:37:04.088 1274 2717 D MountService: Thinking about reset, mSystemReady=false, mDaemonConnected=true 11-23 14:37:04.088 1274 1274 I SystemServiceManager: Starting com.android.server.UiModeManagerService 11-23 14:37:04.520 1274 1274 I SystemServer: NetworkTimeUpdateService //phase480 && 500 11-23 14:37:05.056 1274 1274 I SystemServiceManager: Starting phase 480 11-23 14:37:05.061 1274 1274 I SystemServiceManager: Starting phase 500 11-23 14:37:05.231 1274 1274 I ActivityManager: System now ready <==> boot_progress_ams_ready 11-23 14:37:05.234 1274 1274 I SystemServer: Making services ready 11-23 14:37:05.243 1274 1274 I SystemServer: WebViewFactory preparation //phase550 11-23 14:37:05.234 1274 1274 I SystemServiceManager: Starting phase 550 11-23 14:37:05.237 1274 1288 I ActivityManager: Force stopping com.android.providers.media appid=10010 user=-1: vold reset //Phase600 11-23 14:37:06.066 1274 1274 I SystemServiceManager: Starting phase 600 11-23 14:37:06.236 1274 1274 D MountService: onStartUser 0 基于记录以上关键日志的目的,我们可以通过以下 logcat 命令在手机开机后使用: adb logcat -b events |grep “boot_progress” adb logcat -s Zygote|SystemServer|SystemServiceManager|ActivityManager 总结 笔者认为,源码分析过程中如果能输出关键类调用的流程,能够显著加强后续理解。关于普通应用进程的创建和启动过程将在后续 Activity 启动分析中一并介绍。下图就是关于本文各进程启动过程的方法调用链路总结。 参考 AOSP 架构概览 Android 系统启动综述 https://juejin.cn/post/6999491633129455653

AI生成摘要

相关推荐 去reddit讨论

u3blog -

Android项目cicd流程总结(使用jenkins)

没有cicd之前我们都是怎么做的相信做安卓开发的都做过这些事 手动运行单元测试,根据报错改代码检查代码风格,根据报错改代码构建apk包,发给测试,有时候还得打很多个接收测试的反馈,改bug,重复之前的步骤把apk放到ftp或者其他地方去发布 是不是想到这一套流程,头都大了,虽然每一步都不难,但是连起来都手工操作就很繁琐 像这些手动流程固定的事,我们完全就可以交给机器来做,让我们有更多时间做点别的事,没错,这就是今天要说的cicd 什么是cicd,以及在cicd过程中包含了哪些步骤一般来说,安卓开发的CI/CD流程包括以下几个阶段:代码提交、代码检查、编译构建、单元测试集成测试、部署发布、用户反馈。 在代码提交阶段,开发者将自己的代码推送到远程仓库,例如Git或SVN,并触发CI/CD工具或平台例如Jenkins或Travis CI等在代码检查阶段,CI/CD工具或平台会对代码进行静态分析和风格检查,例如使用SonarQube或Checkstyle等。在编译构建阶段,CI/CD工具或平台会使用Gradle或Maven等工具对代码进行编译和打包,生成APK文件在单元测试阶段,CI/CD工具或平台会使用JUnit或Espresso等框架对代码进行单元测试,并生成测试报告。在集成测试阶段,CI/CD工具或平台会使用Appium或Selenium等框架对应用进行集成测试,并生成测试报告在部署发布阶段,CI/CD工具或平台会将APK文件上传到内部服务器或外部平台,例如蒲公英或Google Play等,并通知相关人员。在用户反馈阶段,开发者可以通过Bugly或Firebase等工具收集用户的反馈和错误信息,并根据需要进行修复和更新 通过以上几个步骤,我们可以把以前的app构建流程从手动变为自动,而且可以通过不断以非常低的成本的重复这个过程,提高我们的项目质量,这就是cicd带给我们的自信 今天我们来通过jenkins来实现上面的几个步骤 安装配置jenkins本文讨论的主要是在windows环境下安装jenkins 从jenkins官网下载对应的安装包即可安装过程很简单但是需要提供一个账号,就像下图显示的界面,这个账号需要有权限打开开始菜单,搜索本地安全策略,选择本地策略、用户权限分配,在右侧的策略中找到作为服务登录,双击打开。点击添加用户或组,在输入框中填入你的账户的名字,单击检查名称,如果加上了下划线,则说明没有问题,如果输入的用户不存在,则会跳出来一个找不到名称的对话框。 这里需要注意一点,windows家庭版默认是没有本地安全策略的,需要用一些技巧把它开启,如下: 1. 在桌面上单击右键,选择“新建”->“文本文档”。 2. 将文本文档重命名为“OpenLocalSecurityPolicy.bat”。 3. 右键单击“OpenLocalSecurityPolicy.bat”,选择“编辑”。 4. 将以下命令复制并粘贴到文本编辑器中: @echo off pushd "%SystemRoot%\system32" findstr /c:"[SR] Cannot repair member file" %windir%\logs\cbs\cbs.log >%userprofile%\Desktop\sfcdetails.txt start ms-settings:windowsdefender start ms-settings:windowsupdate start ms-settings:windowsupdate-history start ms-settings:windowsupdate-options start ms-settings:storagesense start ms-settings:storagesense-diagnostics start ms-settings:storagesense-configurecleanup start ms-settings:storagesense-changehowwesave start ms-settings:storagesense-runstoragecleanupnow start ms-settings:storagesense-storageusage start ms-settings:storagesense-changestoragesavelocations start ms-settings:backup start ms-settings:backup-advancedsettings start ms-settings:backup-addalocaldriveornetworklocation start ms-settings:backup-managebackups start ms-settings:backup-moreoptions start ms-settings:dateandtime start ms-settings:regionlanguage start ms-settings:regionlanguage-languagepacks start ms-settings:regionlanguage-speech start ms-settings:regionlanguage-keyboards start ms-settings:regionlanguage-morespeechservicesonline start ms-settings:speech start ms-settings:speech-microphoneprivacysettings 5. 保存并关闭文本编辑器。 6. 双击“OpenLocalSecurityPolicy.bat”文件,以打开本地安全策略。 修改默认根地址到其他盘符 默认情况下,jenkins的主目录都是在c盘,如果这样,我们使用中产生的数据都是在c盘,用过windows的都知道,数据放在c盘是很危险也是很让人不爽的一件事,我们可以通过修改jenkins的主目录方法来把数据放到其他盘 在成功安装了jenkins并解锁之后,我们可以配置环境变量JENKINS_HOME,地址就是我们想改的目录, 然后修改jenkins.xml <env name="JENKINS_HOME" value="%LocalAppData%\Jenkins.jenkins"/>改为 <env name="JENKINS_HOME" value="E:\jenkins"/> 配置常用的插件在第一次启动jenkins的时候,会让你选择安装哪些插件,这时候直接选择推荐的插件就好,包含了一些常用插件,比如git等等,如下图 配置针对于android的环境 android sdk 见下图 gradle – Dashboard -> Manage Jenkins -> Global Tool Configuration中的Gradle配置gradle路径即可 jdk – Dashboard -> Manage Jenkins -> Global Tool Configuration中的JDK配置jdk路径即可 git – Dashboard -> Manage Jenkins -> Global Tool Configuration中的Git installations配置git路径即可 配置Android的具体job信息新建一个freestyle的item,在里面做以下几步: 配置git仓库地址以及构建分支 设置构建触发器(定时构建) – 找到构建触发器,勾选build periodically,在编辑框里按照规则设置构建时间,在某天的某个时段自动构建,比如45 9-15/2 * * 1-5,虽然可以提交一次就构建一次,但是不建议这么做。构建表达式的规则见下图,可以根据自己的需要写表达式。 添加构建步骤,打包出apk,如下图在build step中触发 配置构建后步骤,自动把apk包上传到ftp或者其他地方 在Jenkins的项目中,选择“构建”->“增加构建步骤”->“执行shell”或“执行Windows批处理命令”一个上传ftp的例子 ftp -n <<EOF open ftp.example.com user username password cd /remote/directory put /local/file bye EOF 配置邮件通知在构建完成之后,特别是失败的时候,我们希望收到一封邮件告诉我们构建失败了,快去处理,我们可以通过以下步骤来实现 在Jenkins中安装Email Extension Plugin插件,可以在插件管理中搜索并安装。在Jenkins的系统管理中,配置邮件服务器的地址,用户名,密码和端口。如果使用的是QQ邮箱或者163邮箱,还需要获取邮箱授权码并使用授权码作为密码。在Jenkins的项目中,选择“构建后操作”->“增加构建后操作”->“Editable Email Notification”。在邮件通知的配置中,填写收件人,抄送人,邮件主题,邮件内容等信息。可以使用一些变量来自定义邮件内容,例如BUILDSTATUS表示构建状态,BUILDS​TATUS表示构建状态, {BUILD_URL}表示构建链接等。 这里特别要注意的是,上面的配置地址和授权码需要在job的设置里面进行,在全局配置有可能发不出邮件 配置单元测试和代码检查我们还需要在运行前执行代码lint检查和单元测试,也需要配插件,插件名字是JUnit和Warnings Next Generation 参考上面 配置Android的具体job信息 中的配置,添加lint和单元测试的任务配置单元测试插件和lint插件,主要指定报告文件的位置,见下图 把单元测试的结果放到邮件的附件中去,配置见下图,也可以放些别的东西 一劳永逸,使用docker把上面的配置做到随时随地使用上面的步骤完成之后,我们就能自动构建,上传apk什么的了,但是每次换台机器我们都得再配一次,想下就很累,这时候我们就可以用docker,创建一个容器,把上面这些操作放在容器里面,在新环境里面拉个镜像,创建容器跑起来,就ok啦,关于怎么用docker,就需要大家自己去搜索学习了 最后放张图吧,jenkins真好用啊

AI生成摘要

相关推荐 去reddit讨论

京东科技开发者 -

Android事件分发-基础原理和场景分析

和其他平台类似,Android 中 View 的布局是一个树形结构,各个 ViewGroup 和 View 是按树形结构嵌套布局的,从而会出现用户触摸的位置坐标可能会落在多个 View 的范围内,这样就不知道哪个 View 来响应这个事件,为了解决这一问题,就出现了事件分发机制。

AI生成摘要 本文介绍了Android中的事件分发机制,事件由上往下传递,如果事件没有被消费则继续传递到下一层,如果事件被消费则停止传递。事件分发是从Activity开始的,关键方法是ViewGroup和View中的相关方法。分发过程中有拦截且拦截后消费,DOWN事件会层层往下分发,直到有拦截后直接交给消费的View进行处理,MOVE和UP事件则会层层往下分发,最后直接交给消费事件的View进行处理,然后层层返回true。

相关推荐 去reddit讨论

千古八方的博客 -

Android 查看userdata分区开始位置及大小、导出userdata分区并修改用户数据分区里的文件

快速简洁答案: 1. 计算分区大小 rk3288:/ # cat /proc/partitions | grep mmcblk1p14 // 筛选自己的userdata分区 179 14 4534272 mmcblk1p14 分区大小 =  4534272 * 1024Byte = 4534272KB 换算为block  =  4534272KB / 512Byte  = 9068544 (0x8A6000)个block 2. 用AndroidTools导出分区,分区大小为第一步计算的0x8A6000个block  […] Android 查看userdata分区开始位置及大小、导出userdata分区并修改用户数据分区里的文件最先出现在千古八方的博客。

AI生成摘要

相关推荐 去reddit讨论

小众软件 -

PlainApp – 嫌弃广告、臃肿、不会用,程序员三怒之后,自己开发开源 Android 全家桶应用

PlainApp 是一款一句话说不清楚干什么的应用,但它是一位程序员经历了三次愤怒(有广告、太臃肿、不够简洁易用)之后,自己开发的一款开源 Android 全家桶应用,主要功能是:图片、视频、音乐播放

AI生成摘要 PlainApp是一款开源的Android应用,由一位程序员开发。它是一个多功能应用,包括图片、视频、音乐播放器,DLNA投屏,用电脑控制手机,文件传输,笔记本,RSS订阅和ChatGPT提问等功能。该应用没有广告,不臃肿,简洁易用。它还支持数据加密,无云服务和无追踪。网页版还有JSON查看器和二维码生成器等功能。用户可以通过对话式界面与应用进行交互,并在网页端为照片和视频添加标签。该应用可以实现电脑与手机之间的文字互传。

相关推荐 去reddit讨论

少数派 -

值得关注的操作系统动态:Ubuntu 23.04、Windows 11、Android 14 等

代号「上天」的 Ubuntu 23.04,口碑似乎在奔向相反方向;从资源管理器到设置,来看看 Windows 11 预览体验版各通道都更新了啥;Android 14 首个测试版本问世,开发者预览版中的对比度条件却被打回重造……欢迎查收本期系统与软件主题《奏折》。查看全文本文为会员文章,出自《单篇文章》,订阅后可阅读全文。

AI生成摘要 本文介绍了Ubuntu 23.04代号“上天”的口碑下降,Windows 11预览版各通道的更新以及Android 14首个测试版本的发布。开发者预览版中的对比度条件被打回重造。

相关推荐 去reddit讨论

InkData -

三星正考虑让 Bing 成为 Galaxy Android 设备的默认搜索引擎

根据纽约时报的报道,三星正在考虑让 Bing 成为 Galaxy Android 设备的默认搜索引擎。此前,三星一直使用 Google Search 作为 Galaxy Android 设备的默认搜索引擎。

AI生成摘要 三星正在考虑让Bing成为Galaxy Android设备的默认搜索引擎,这可能会导致谷歌每年损失30亿美元的搜索营收。苹果与Google的合作也在今年到期,目前尚不清楚苹果是否会考虑让Bing成为iOS设备的默认引擎。

相关推荐 去reddit讨论

InkData -

本周回顾:Hololens、Windows 11、Android 子系统、PowerToys、Edge浏览器等

本周的微软要闻涉及 Hololens、Windows 11、Android 子系统、官翻 Surface Pro 9、PowerToys、Edge 浏览器等。具体的总结如下:

AI生成摘要 本周微软新闻包括Hololens、Windows 11、Android子系统、Surface Pro 9、PowerToys和Edge浏览器等。Hololens 2将获得新功能,Windows 11将推出掌机版。Android子系统更新带来画中画支持和安全更新补丁。Surface Pro 9官翻售价优惠。PowerToys Run添加ChatGPT功能。Edge侧栏可固定到Windows侧边栏。

相关推荐 去reddit讨论

Rust.cc -

如何把android的rust代码导入到vscode 并且可以进行函数关联、编辑代码自动补全?

比如 把http://aospxref.com/android-13.0.0_r3/xref/system/security/keystore2/ 中的代码导入到vscode,但是提示错误,无法关联函数、变量等,不方便阅读、编辑。 [ERROR rust_analyzer::config] failed to find any projects in [AbsPathBuf("z:\code\gsi13aidl\system\security\keystore2")] [ERROR rust_analyzer::main_loop] FetchWorkspaceError: rust-analyzer failed to discover workspace 或者有没有其它工具可以把android的rust代码导入到vscode 并且可以进行函数关联、编辑代码自动补全?

AI生成摘要 文章讨论如何将Android的Rust代码导入到VSCode并进行函数关联和编辑自动补全。作者尝试将代码从http://aospxref.com/android-13.0.0_r3/xref/system/security/keystore2/导入到VSCode,但遇到了错误。作者询问是否有其他工具可以实现这一目标。

相关推荐 去reddit讨论