1. 程式人生 > >安卓彩38平臺出租開發學習之SystemServer啟動過程

安卓彩38平臺出租開發學習之SystemServer啟動過程

ble 持續時間 snap not lock power dex finger can

這兩天彩38平臺出租 haozbbs.com Q1446595067 有一個需求得聯調,在等待服務端同事完事,等待過程中,閱讀了一下Android8.0裏SystemServer的啟動過程,除了設置時區語言這些,其實主要就是初始化了系統上下文以及一些服務的啟動。

main()方法

SystemServer是一個進程,由zygote進程fork出來,所以它的入口方法就是main方法,代碼如下

    public static void main(String[] args) {
        new SystemServer().run();
    }

直接就是new了一個SystemServer,而後執行run()方法

run()方法

方法比較長,代碼如下

    private void run() {
        try {
            traceBeginAndSlog("InitBeforeStartServices");
            // 如果當前系統時間早於1970年元月1號,就更新之為1970年元月1號
            if (System.currentTimeMillis() < EARLIEST_SUPPORTED_TIME) {
                SystemClock.setCurrentTimeMillis(EARLIEST_SUPPORTED_TIME);
            }

            // 設置時區
            String timezoneProperty = SystemProperties.get("persist.sys.timezone");
            if (timezoneProperty == null || timezoneProperty.isEmpty()) {
                SystemProperties.set("persist.sys.timezone", "GMT"); // 默認時區是格林尼治時區
            }

            // 設置系統語言
            if (!SystemProperties.get("persist.sys.language").isEmpty()) {
                final String languageTag = Locale.getDefault().toLanguageTag();

                SystemProperties.set("persist.sys.locale", languageTag);
                SystemProperties.set("persist.sys.language", "");
                SystemProperties.set("persist.sys.country", "");
                SystemProperties.set("persist.sys.localevar", "");
            }

            // 系統Server不能進行非oneway通信,因為非oneway通信要等待對方的恢復,這個等待過程是阻塞的
            Binder.setWarnOnBlocking(true); // 所以設置阻塞時警告

            // 正式啟動SystemServer

            int uptimeMillis = (int) SystemClock.elapsedRealtime();
            EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_SYSTEM_RUN, uptimeMillis);
            if (!mRuntimeRestart) {
                MetricsLogger.histogram(null, "boot_system_server_init", uptimeMillis);
            }

            // 設置vmLibrary
            SystemProperties.set("persist.sys.dalvik.vm.lib.2", VMRuntime.getRuntime().vmLibrary());

            // Enable the sampling profiler.
            if (SamplingProfilerIntegration.isEnabled()) {
                SamplingProfilerIntegration.start();
                mProfilerSnapshotTimer = new Timer();
                mProfilerSnapshotTimer.schedule(new TimerTask() {
                    @Override
                    public void run() {
                        SamplingProfilerIntegration.writeSnapshot("system_server", null);
                    }
                }, SNAPSHOT_INTERVAL, SNAPSHOT_INTERVAL);
            }

            // 清除內存增長上限,因為加載SystemServer需要很多內存
            VMRuntime.getRuntime().clearGrowthLimit();

            // 設置內存利用率最大是0.8
            VMRuntime.getRuntime().setTargetHeapUtilization(0.8f);

            // 初始化指紋系統
            Build.ensureFingerprintProperty();

            // 設置訪問環境變量(例如sd卡路徑等)時,必須指定用戶
            Environment.setUserRequired(true);

            // 設置系統的Bundle是可defuse的,意為如果在解析bundle的時候,忽略發生的badParcelableException
            // 那個異常如果發送,就直接清空這個Bundle的內容。所以defuse使能最好在Bundle到了終點後再設置,因為這樣總不會導致下流再失去bundle的內容
            BaseBundle.setShouldDefuse(true);

            // 保證進入SystemServer的binder都運行在前臺
            BinderInternal.disableBackgroundScheduling(true);

            // 設置最大線程數為31
            BinderInternal.setMaxThreads(sMaxBinderThreads);

            // 設置當前線程(主線程)也是在前臺進行
            android.os.Process.setThreadPriority(
                    android.os.Process.THREAD_PRIORITY_FOREGROUND);
            android.os.Process.setCanSelfBackground(false); // 主線程不能自己切到後臺
            Looper.prepareMainLooper(); // 主線程的消息循環開始

            // 加載native服務
            System.loadLibrary("android_servers");

            // 檢測上次是否成功關機
            performPendingShutdown();

            // 初始化系統上下文
            createSystemContext();

            // 初始化SysytemServiceManager,並把它添加到LocalServices中
            mSystemServiceManager = new SystemServiceManager(mSystemContext);
            mSystemServiceManager.setRuntimeRestarted(mRuntimeRestart);
            LocalServices.addService(SystemServiceManager.class, mSystemServiceManager);
            // 創建SystemServerInit線程池的單例
            SystemServerInitThreadPool.get();
        } finally {
            traceEnd(); 
        }

        // 啟動一些服務
        // Start services.
        try {
            traceBeginAndSlog("StartServices");
            startBootstrapServices(); // 啟動引導服務
            startCoreServices(); // 啟動核心服務
            startOtherServices(); // 啟動其他服務
            SystemServerInitThreadPool.shutdown();
        } catch (Throwable ex) {
            ...
        } finally {
            traceEnd();
        }

        ... // 日誌

        // Loop forever.
        // 開始消息循環
        Looper.loop();
        throw new RuntimeException("Main thread loop unexpectedly exited"); // 消息循環是不能退出的
    }

代碼多但是邏輯並不復雜,值得註意的方法除了啟動引導服務、核心服務和其他服務外,再就是檢測上次關機是否成功的performPendingShutdown()方法,這個方法主要是針對recovery模式下系統更新引起的重啟,這種情況要多重啟一次。而這裏只是設置了一下sys.powerctl屬性,沒有執行重啟操作

performPendingShutdown()方法

代碼如下

    private void performPendingShutdown() {
        final String shutdownAction = SystemProperties.get(
                ShutdownThread.SHUTDOWN_ACTION_PROPERTY, ""); // 獲取上次的關機信息
        if (shutdownAction != null && shutdownAction.length() > 0) {
            boolean reboot = (shutdownAction.charAt(0) == ‘1‘); // 關機信息第一位表示關機是否是為了重啟

            final String reason;
            if (shutdownAction.length() > 1) {
                reason = shutdownAction.substring(1, shutdownAction.length()); // 第一位往後表示關機的原因
            } else {
                reason = null;
            }

            if (reason != null && reason.startsWith(PowerManager.REBOOT_RECOVERY_UPDATE)) {
                /*
                 * 關機原因是否是REBOOT_RECOVERY_UPDATE,也就是recovery模式下,為了執行系統更新而關的機
                 * 這種情況下,一定會多重啟一次,多的這一次重啟,原因就不是REBOOT_RECOVERY_UPDATE了
                 * @hide
                   public static final String REBOOT_RECOVERY_UPDATE = "recovery-update";
                 */
                File packageFile = new File(UNCRYPT_PACKAGE_FILE);
                if (packageFile.exists()) {
                    String filename = null;
                    try {
                        filename = FileUtils.readTextFile(packageFile, 0, null); // 讀取uncrypt_file的內容,獲取的是一個文件名
                    } catch (IOException e) {
                        Slog.e(TAG, "Error reading uncrypt package file", e);
                    }

                    if (filename != null && filename.startsWith("/data")) { // 如果讀出來的文件名以/data開頭,也就是在data目錄內
                        if (!new File(BLOCK_MAP_FILE).exists()) { // 如果block.map文件不存在,直接拋異常,重啟失敗
                            Slog.e(TAG, "Can‘t find block map file, uncrypt failed or " +
                                    "unexpected runtime restart?");
                            return;
                        }
                    }
                }
            }
            ShutdownThread.rebootOrShutdown(null, reboot, reason);
        }
    }

主要是調用了ShutdownThread.rebootOrShutdown()方法,這個方法只是保存了一下sys.powerctl屬性,代碼如下

    public static void rebootOrShutdown(final Context context, boolean reboot, String reason) {
        if (reboot) {
            .. // 日誌
            PowerManagerService.lowLevelReboot(reason);
            .. // 日誌
            reason = null;
        } else if (SHUTDOWN_VIBRATE_MS > 0 && context != null) {
            // 關機前要進行振動
            Vibrator vibrator = new SystemVibrator(context); // 振動器
            try {
                vibrator.vibrate(SHUTDOWN_VIBRATE_MS, VIBRATION_ATTRIBUTES); // 傳入振動持續時間和振動方式
            } catch (Exception e) {
                .. // 日誌
            }

            try {
                Thread.sleep(SHUTDOWN_VIBRATE_MS); // 振動是異步的,所以當前線程要阻塞一會兒,保證振動完了,再關機
            } catch (InterruptedException unused) {
            }
        }
        // Shutdown power
        PowerManagerService.lowLevelShutdown(reason);

如果是進來這個方法是要重啟,reboot就是真,先調用PowerManagerService.lowLevelReboot()方法,傳入重啟原因reason。此方法代碼如下

    public static void lowLevelReboot(String reason) {
        if (reason == null) {
            reason = "";
        }

        if (reason.equals(PowerManager.REBOOT_QUIESCENT)) {
            sQuiescent = true;
            reason = "";
        } else if (reason.endsWith("," + PowerManager.REBOOT_QUIESCENT)) {
            sQuiescent = true;
            reason = reason.substring(0,
                    reason.length() - PowerManager.REBOOT_QUIESCENT.length() - 1);
        }

        if (reason.equals(PowerManager.REBOOT_RECOVERY)
                || reason.equals(PowerManager.REBOOT_RECOVERY_UPDATE)) { // 如果reason是或REBOOT_RECOVERY或REBOOT_RECOVERY_UPDATE,就把reason換成recovery
            reason = "recovery";
        }

        if (sQuiescent) { 
            reason = reason + ",quiescent";
        }

        SystemProperties.set("sys.powerctl", "reboot," + reason); // 保存sys.powerctl屬性
        try {
            Thread.sleep(20 * 1000L); // 阻塞20秒
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        Slog.wtf(TAG, "Unexpected return from lowLevelReboot!");
    }

可以看到,主要是把重啟原因進行轉換和保存,然後把重啟線程阻塞20s。方法執行完後,就會執行lowLevelShutdown(),代碼如下

    public static void lowLevelShutdown(String reason) {
        if (reason == null) {
            reason = "";
        }
        SystemProperties.set("sys.powerctl", "shutdown," + reason); // 保存/更新sys.powerctl屬性
    }

回到SystemServer.run()方法,檢測完上次是否正常關機後,調用了createSystemContext()方法獲取系統上下文,代碼如下

    private void createSystemContext() {
        ActivityThread activityThread = ActivityThread.systemMain();
        mSystemContext = activityThread.getSystemContext();
        mSystemContext.setTheme(DEFAULT_SYSTEM_THEME);

        final Context systemUiContext = activityThread.getSystemUiContext();
        systemUiContext.setTheme(DEFAULT_SYSTEM_THEME);
    }

這個方法主要是調用了ActivityThread的一系列方法來實現,參見文章安卓開發學習之獲取系統上下文。然後就是調用startBootstrapServices()、startCoreServices()、startOtherServices()方法啟動一些服務,以前兩個方法為例看一下代碼
startBootstrapServices()方法

啟動一些引導服務,代碼如下

    private void startBootstrapServices() {

        // 創建SystemConfig,進行權限的獲取
        SystemServerInitThreadPool.get().submit(SystemConfig::getInstance, TAG_SYSTEM_CONFIG);

        // 開啟安裝器服務
        Installer installer = mSystemServiceManager.startService(Installer.class);

        // 設備識別器服務
        mSystemServiceManager.startService(DeviceIdentifiersPolicyService.class);

        // 啟動並初始化ActivityManagerService
        mActivityManagerService = mSystemServiceManager.startService(
                ActivityManagerService.Lifecycle.class).getService();
        mActivityManagerService.setSystemServiceManager(mSystemServiceManager);
        mActivityManagerService.setInstaller(installer);

        // 啟動電量管理服務
        mPowerManagerService = mSystemServiceManager.startService(PowerManagerService.class);

        // 初始化電量管理器
        mActivityManagerService.initPowerManagement();

        if (!SystemProperties.getBoolean("config.disable_noncore", false)) {
            traceBeginAndSlog("StartRecoverySystemService");
            // 啟動RecoverySystem服務
            mSystemServiceManager.startService(RecoverySystemService.class);
        }

        // 標記裸機已經啟動完了,如果我們困在了運行時的重啟循環中,這個標記可以跳出這個循環
        RescueParty.noteBoot(mSystemContext);

        // 啟動燈光服務,包括背景亮度、閃光燈等
        mSystemServiceManager.startService(LightsService.class);

        // 啟動顯示服務,用來顯示UI
        mDisplayManagerService = mSystemServiceManager.startService(DisplayManagerService.class);

        // 開始開機動畫
        mSystemServiceManager.startBootPhase(SystemService.PHASE_WAIT_FOR_DEFAULT_DISPLAY);

        // 加密模式下,只運行內核應用,此處先設置標誌位
        String cryptState = SystemProperties.get("vold.decrypt");
        if (ENCRYPTING_STATE.equals(cryptState)) {
            mOnlyCore = true;
        } else if (ENCRYPTED_STATE.equals(cryptState)) {
            mOnlyCore = true;
        }

        // 開啟包管理器服務
        mPackageManagerService = PackageManagerService.main(mSystemContext, installer,
                mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore);
        mFirstBoot = mPackageManagerService.isFirstBoot();
        // 獲取包管理器
        mPackageManager = mSystemContext.getPackageManager();

        if (!mOnlyCore) {
            boolean disableOtaDexopt = SystemProperties.getBoolean("config.disable_otadexopt",
                    false);
            if (!disableOtaDexopt) {
                try {
                    // OTADexOpt(空中下載dex並優化,用於OTA升級)使能時,啟動OTA升級服務
                    OtaDexoptService.main(mSystemContext, mPackageManagerService);
                } catch (Throwable e) {
                    ..
                } finally {
                   ..      
                }
            }
        }

        // 啟動用戶管理服務
        mSystemServiceManager.startService(UserManagerService.LifeCycle.class);

        traceBeginAndSlog("InitAttributerCache");
        // 從系統包中初始化屬性資源
        AttributeCache.init(mSystemContext);

        // 設置系統進程
        mActivityManagerService.setSystemProcess();

        // 設置ui的調度策略
        mDisplayManagerService.setupSchedulerPolicies();

        // 啟動OverlayManagerService,用於定制系統界面
        mSystemServiceManager.startService(new OverlayManagerService(mSystemContext, installer));

        // 開啟傳感器服務
        mSensorServiceStart = SystemServerInitThreadPool.get().submit(() -> {
            BootTimingsTraceLog traceLog = new BootTimingsTraceLog(
                    SYSTEM_SERVER_TIMING_ASYNC_TAG, Trace.TRACE_TAG_SYSTEM_SERVER);
            traceLog.traceBegin(START_SENSOR_SERVICE);
            startSensorService();
            traceLog.traceEnd();
        }, START_SENSOR_SERVICE);
    }

開啟了一些服務,無需多解釋

startCoreServices()方法

也是啟動一些服務,代碼如下

    private void startCoreServices() {

        // 啟動DropBox服務,用來記錄系統日誌
        mSystemServiceManager.startService(DropBoxManagerService.class);

        // 啟動電池服務
        mSystemServiceManager.startService(BatteryService.class);

        traceBeginAndSlog("StartUsageService");
        // 啟動使用狀態服務
        mSystemServiceManager.startService(UsageStatsService.class);
        mActivityManagerService.setUsageStatsManager(
                LocalServices.getService(UsageStatsManagerInternal.class));
        // 啟動WebView更新服務
        mWebViewUpdateService = mSystemServiceManager.startService(WebViewUpdateService.class);
    }

結語

SystemService的啟動過程就是如此,主要還是啟動一些服務

安卓彩38平臺出租開發學習之SystemServer啟動過程