開機SystemServer到ActivityManagerService啟動過程分析

__Shadow發表於2016-08-14

開機SystemServer到ActivityManagerService啟動過程

一 從Systemserver到AMS

zygote-> systemserver:java入層口:

/**
 * The main entry point from zygote.
 */
public static void main(String[] args) {
    new SystemServer().run();
}

 

接下來繼續看SystemServer run函式執行過程:

private void run() {
    
    // 準備SystemServer執行環境:設定執行緒優先順序,建立主線層Looper,ActivityThread和SystemContext
    android.os.Process.setThreadPriority();
    Looper.prepareMainLooper();
    
    // 建立systemserver上程式的ActivityThread和SystemContext
    createSystemContext();
    
    // 增加SystemServiceManager:統一管理system services的建立,啟動和生命週期,多使用者切換
    mSystemServiceManager = new SystemServiceManager(mSystemContext);
    
    // Start services.
    
    // 1.建立AMS
    mActivityManagerService = mSystemServiceManager.startService(
            ActivityManagerService.Lifecycle.class).getService();
    
    
    // Start the Power Manager service
    mPowerManagerService = mSystemServiceManager.startService(PowerManagerService.class);

    
    // Start the package manager service
    mPackageManagerService = PackageManagerService.main();
            
    
    // 2.將SystemServer程式可加到AMS中排程管理
    mActivityManagerService.setSystemProcess();
    
    // 3.將相關provider執行在systemserver程式中:SettingsProvider
    mActivityManagerService.installSystemProviders();
    
    // 
    final Watchdog watchdog = Watchdog.getInstance();
    watchdog.init(context, mActivityManagerService);
    
    // Start Window Manager 
    wm = WindowManagerService.main();
    
    // 4.直接儲存wms物件,與WMS互動
    mActivityManagerService.setWindowManager(wm);
    
    // 5.通過WMS 彈出“正在啟動應用”框
    // R.string.android_upgrading_starting_apps
    ActivityManagerNative.getDefault().showBootMessage();
    
    // 6. AMS作為Framework核心,做好準備就緒後就開始啟動應用層,和對AMS有依賴的服務
    mActivityManagerService.systemReady(new Runnable(){
        //啟動SystemUI
        startSystemUi(context);
        
        //啟動WatchDog監控核心服務狀態
        Watchdog.getInstance().start();
        
        //
        mmsServiceF.systemRunning();
    });

    // Loop forever.
    Looper.loop();
}

 

以上6個步驟是SystemServer中關於AMS的呼叫,完成AMS的建立和系統的初始化,下面按照這步驟繼續升入分析。

  • 這裡有個疑問:

  AMS儲存物件,本身就在同一個程式,WMS與WMS之間的互動式直接呼叫速度會更快,其他服務為何不這樣,是因為耦合太強,還是實時性要求更高?
  彈出“正在啟動應用”框,這裡為何不直接呼叫AMS的showBootMessage而是通過binder方式呼叫,其他介面都是直接呼叫,為何?直接呼叫有何不可嗎?

 

ActivityManagerService 建立過程

接上面SystemServer.run中:
  mActivityManagerService = mSystemServiceManager.startService(
            ActivityManagerService.Lifecycle.class).getService();

  這是通過SystemServiceManager這樣一個模板類來建立執行在SystemServer中的Framework服務;
  並將建立的服務統一儲存在佇列管理,會涉及到多使用者切換。

  // Note: This method is invoked on the main thread but may need to attach various
    // handlers to other threads.  So take care to be explicit about the looper.
    public ActivityManagerService(Context systemContext) {
        
   // 1.系統Context 和 ActivityThread (將systemserver程式作為應用程式管理) mContext = systemContext; mFactoryTest = FactoryTest.getMode(); mSystemThread = ActivityThread.currentActivityThread(); // 2.AMS工作的執行緒和Handler,處理顯示相關的UiHandler ---》知識點HandlerThread和Handler mHandlerThread = new ServiceThread(TAG, android.os.Process.THREAD_PRIORITY_FOREGROUND, false /*allowIo*/); mHandlerThread.start(); mHandler = new MainHandler(mHandlerThread.getLooper()); mUiHandler = new UiHandler(); // 3. 廣播佇列BroadcastQueue初始化:前臺廣播佇列和後臺廣播佇列 mFgBroadcastQueue = new BroadcastQueue(this, mHandler,"foreground", BROADCAST_FG_TIMEOUT, false); mBgBroadcastQueue = new BroadcastQueue(this, mHandler,"background", BROADCAST_BG_TIMEOUT, true); mBroadcastQueues[0] = mFgBroadcastQueue; mBroadcastQueues[1] = mBgBroadcastQueue; // 4. Service 和 Provider 管理 mServices = new ActiveServices(this); mProviderMap = new ProviderMap(this); // 5.系統資料存放目錄:/data/system/ File dataDir = Environment.getDataDirectory(); File systemDir = new File(dataDir, "system"); systemDir.mkdirs(); // 電池狀態資訊,程式狀態 和 應用許可權管理 mBatteryStatsService = new BatteryStatsService(systemDir, mHandler); mProcessStats = new ProcessStatsService(this, new File(systemDir, "procstats")); mAppOpsService = new AppOpsService(new File(systemDir, "appops.xml"), mHandler); // 6.多使用者管理 mStartedUsers.put(UserHandle.USER_OWNER, new UserState(UserHandle.OWNER, true)); mUserLru.add(UserHandle.USER_OWNER); updateStartedUserArrayLocked(); // 7.最近任務,Activity,Task管理 mRecentTasks = new RecentTasks(this); mStackSupervisor = new ActivityStackSupervisor(this, mRecentTasks); mTaskPersister = new TaskPersister(systemDir, mStackSupervisor, mRecentTasks); // 建立一個新執行緒,用於監控和定時更新系統CPU資訊,30分鐘更新一次CPU和電池資訊 mProcessCpuTracker.init(); mProcessCpuThread = new Thread("CpuTracker") {} // 加入Watchdog監控起來 Watchdog.getInstance().addMonitor(this); Watchdog.getInstance().addThread(mHandler); }

 

  • 以上 AMS建立過程 涉及到Android 四大元件管理的初始化:

    Broadcast --》BroadcastQueue
    Provider --》ProviderMap
    Service --》ActiveServices
    Activity --》ActivityStackSupervisor

  備註1:Android6.0上加入多使用者功能,增加了一些涉及多使用者的管理。

      擴充知識點:HandlerThread,Handle,Looper

 

二 將SystemServer程式可加到AMS中排程管理

  接上面systemserver.run中:

    mActivityManagerService.setSystemProcess();

public void setSystemProcess() {
        // 將服務加入到ServiceManager中
        ServiceManager.addService(Context.ACTIVITY_SERVICE, this, true);
        ServiceManager.addService(ProcessStats.SERVICE_NAME, mProcessStats);
        ServiceManager.addService("meminfo", new MemBinder(this));
        ServiceManager.addService("gfxinfo", new GraphicsBinder(this));
        ServiceManager.addService("dbinfo", new DbBinder(this));
        
        // 設定application info LoadedApkinfo 有關 framework-res.apk
        ApplicationInfo info = mContext.getPackageManager().getApplicationInfo(
                    "android", STOCK_PM_FLAGS);
        mSystemThread.installSystemApplicationInfo(info, getClass().getClassLoader());
        
        //給SystemServer程式創建ProcessRecord,adj值,就是將SystemServer程式加入到AMS程式管理機制中,跟應用程式一致
        synchronized (this) {
            ProcessRecord app = newProcessRecordLocked(info, info.processName, false, 0);
            app.persistent = true;
            app.pid = MY_PID;
            app.maxAdj = ProcessList.SYSTEM_ADJ;
            app.makeActive(mSystemThread.getApplicationThread(), mProcessStats);
            synchronized (mPidsSelfLocked) {
                mPidsSelfLocked.put(app.pid, app);
            }
            updateLruProcessLocked(app, false, null);
            updateOomAdjLocked();
        }
    }

 

  •  setSystemProcess意義:

    這一步就是給SystemServer程式建立ProcessRecord,adj值,就是將SystemServer程式加入到AMS程式管理機制中,跟應用程式一致;
      程式排程更新優先順序oomadj值,個人感覺SystemServer程式跟應用程式就不一樣,卻加入AMS來排程管理,這樣做的意義何在?

四 建立執行在SystemServer程式中Provider

接上面SystemServer.run中:

  mActivityManagerService.installSystemProviders();

  備註2:  將相關provider執行在systemserver程式中:SettingsProvider
      具體安裝過程這裡暫不詳述,在應用啟動過程中具體分析。

五 AMS systemReady過程

接上面SystemServer.run中:

  mActivityManagerService.systemReady();

public void systemReady(final Runnable goingCallback) {
        synchronized(this) {
            if (mSystemReady) {
                goingCallback.run();
            }
            ……
            
            // 1.升級相關處理:傳送PRE_BOOT_COMPLETED廣播 等待升級處理完成才能繼續
            // Check to see if there are any update receivers to run.
            if (!mDidUpdate) {
                // 等待升級完成,否則直接返回
                if (mWaitingUpdate) {
                    return;
                }
                // 傳送PRE_BOOT_COMPLETED廣播
                final ArrayList<ComponentName> doneReceivers = new ArrayList<ComponentName>();
                mWaitingUpdate = deliverPreBootCompleted(new Runnable() {
                    // 等待所有接收PRE_BOOT_COMPLETED廣播者處理完畢
                    public void run() {
                        synchronized (ActivityManagerService.this) {
                            mDidUpdate = true;
                        }
                        showBootMessage(mContext.getText(
                                R.string.android_upgrading_complete),
                                false);
                                
                        // 將系統版本號和處理過的廣播寫入檔案:/data/system/called_pre_boots.dat檔案
                        writeLastDonePreBootReceivers(doneReceivers);
                        
                        // 繼續systemReady流程
                        systemReady(goingCallback);
                    }
                }, doneReceivers, UserHandle.USER_OWNER);

                if (mWaitingUpdate) {
                    return;
                }
                mDidUpdate = true;
            }

            mSystemReady = true;
        }
        
        // 2. 收集已經啟動的程式並殺死,除過persistent常駐程式
        ArrayList<ProcessRecord> procsToKill = null;
        synchronized(mPidsSelfLocked) {
            for (int i=mPidsSelfLocked.size()-1; i>=0; i--) {
                ProcessRecord proc = mPidsSelfLocked.valueAt(i);
                if (!isAllowedWhileBooting(proc.info)){
                    if (procsToKill == null) {
                        procsToKill = new ArrayList<ProcessRecord>();
                    }
                    procsToKill.add(proc);
                }
            }
        }

        synchronized(this) {
            if (procsToKill != null) {
                for (int i=procsToKill.size()-1; i>=0; i--) {
                    ProcessRecord proc = procsToKill.get(i);
                    Slog.i(TAG, "Removing system update proc: " + proc);
                    removeProcessLocked(proc, true, false, "system update done");
                }
            }

            // Now that we have cleaned up any update processes, we
            // are ready to start launching real processes and know that
            // we won't trample on them any more.
            mProcessesReady = true;
        }
        
        // 3.系統準備好後回撥傳入的Runnable:
        if (goingCallback != null) goingCallback.run();
        
    

      // 4. 傳送賬戶啟動的廣播,涉及多使用者
      long ident = Binder.clearCallingIdentity();


      Intent intent = new Intent(Intent.ACTION_USER_STARTED);
      broadcastIntentLocked(intent);
      intent = new Intent(Intent.ACTION_USER_STARTING);
      broadcastIntentLocked(intent);


      Binder.restoreCallingIdentity(ident);

        // 5. 啟動桌面Home Activity
        mBooting = true;
        startHomeActivityLocked(mCurrentUserId, "systemReady");
        mStackSupervisor.resumeTopActivitiesLocked();
  }

 

  • 下面看下AMS systemReady的過程:

1. 升級相關處理:傳送PRE_BOOT_COMPLETED廣播

  顧名思義:只有系統做OTA升級 和 手機初次開機的時候,應當才會走此廣播,下面看看這個函式具體的處理。

  接上面:

deliverPreBootCompleted(new Runnable() {
        // 向PMS查詢,所有接收ACTION_PRE_BOOT_COMPLETED廣播的Receiver
        Intent intent = new Intent(Intent.ACTION_PRE_BOOT_COMPLETED);
        List<ResolveInfo> ris = null;
        ris = AppGlobals.getPackageManager().queryIntentReceivers(
                    intent, null, 0, userId);
        
        // 只有系統廣播才能接收該廣播,去掉非系統應用
        for (int i=ris.size()-1; i>=0; i--) {
            if ((ris.get(i).activityInfo.applicationInfo.flags
                &ApplicationInfo.FLAG_SYSTEM) == 0) {
            ris.remove(i);
            }
        }
        
        // 給Intent設定flag:FLAG_RECEIVER_BOOT_UPGRADE,很關鍵這個看看flag的作用:
        // 只有設定這個標誌,才能讓應用在系統沒有ready的情況下啟動,見下文原始註釋
        intent.addFlags(Intent.FLAG_RECEIVER_BOOT_UPGRADE);
        
        // 將已經處理過ACTION_PRE_BOOT_COMPLETED廣播的Receiver去掉
        // 已經處理該廣播的Receiver記錄 和 對應的系統版本號 都記錄在:/data/system/called_pre_boots.dat檔案中,
        // 通過與系統當前版本號比對,確認是否已處理過。考慮處理過程異常中斷的情況:比如斷電
        ArrayList<ComponentName> lastDoneReceivers = readLastDonePreBootReceivers();
        
        // 將已經處理過的廣播去除,同時記錄已處理過儲存在 doneReceivers陣列中
        for (int i=0; i<ris.size(); i++) {
            ActivityInfo ai = ris.get(i).activityInfo;
            ComponentName comp = new ComponentName(ai.packageName, ai.name);
            if (lastDoneReceivers.contains(comp)) {
                // We already did the pre boot receiver for this app with the current
                // platform version, so don't do it again...
                ris.remove(i);
                i--;
                // ...however, do keep it as one that has been done, so we don't
                // forget about it when rewriting the file of last done receivers.
                doneReceivers.add(comp);
            }
        }
        
        // 內部類專門用來ACTION_PRE_BOOT_COMPLETED廣播的傳送,要看看這個PreBootContinuation類
        // 這塊邏輯一直在變,Android5.0, 6.0 , 以及看到在7.0上又變了,基本思路不變,本文程式碼基於Android6.0
        PreBootContinuation cont = new PreBootContinuation(intent, onFinishCallback, doneReceivers,
                ris, users);
        cont.go();
        return true;
    }

 

給intent設定的廣播意義:

    /**

    * Set when this broadcast is for a boot upgrade, a special mode that

    * allows the broadcast to be sent before the system is ready and launches

    * the app process with no providers running in it.

    * @hide

    */

    public static final int FLAG_RECEIVER_BOOT_UPGRADE = 0x02000000;

 

  • 繼續接著上面PreBootContinuation類:從繼承關係看到可以跨程式的

 

final class PreBootContinuation extends IIntentReceiver.Stub {
        void go() {
            //判斷是不是最後一個接收者
            if (lastRi != curRi) {
                
                // 疑問:如果不是最後一個接收者,則發給一個指定接收者ComponentName
                // 為什麼要在這裡指定接收者,一個個傳送,而不是交給廣播自己去處理?
                ActivityInfo ai = ris.get(curRi).activityInfo;
                ComponentName comp = new ComponentName(ai.packageName, ai.name);
                intent.setComponent(comp);
                doneReceivers.add(comp);
                lastRi = curRi;
                
                // 介面顯示正在處理的廣播,上面的指定接收者,就是為了這裡能顯示正在處理的廣播名稱?
                CharSequence label = ai.loadLabel(mContext.getPackageManager());
                showBootMessage(mContext.getString(R.string.android_preparing_apk, label), false);
            }
            
            // 傳送廣播,指定接收者處理完畢,會resultTo回來--》this
            Slog.i(TAG, "Pre-boot of " + intent.getComponent().toShortString()
                    + " for user " + users[curUser]);
            broadcastIntentLocked(null, null, intent, null, this,
                    0, null, null, null, AppOpsManager.OP_NONE,
                    null, true, false, MY_PID, Process.SYSTEM_UID, users[curUser]);
        }
        
        
        public void performReceive(Intent intent, int resultCode,
                String data, Bundle extras, boolean ordered,
                boolean sticky, int sendingUser) {
                
            // 指定接收者廣播處理完畢回撥resultTo回來,繼續處理下一個,如果所有處理完,則post訊息執行onFinishCallback
            curUser++;
            if (curUser >= users.length) {
                curUser = 0;
                curRi++;
                if (curRi >= ris.size()) {
                    // All done sending broadcasts!
                    if (onFinishCallback != null) {
                        // The raw IIntentReceiver interface is called
                        // with the AM lock held, so redispatch to
                        // execute our code without the lock.
                        mHandler.post(onFinishCallback);
                    }
                    return;
                }
            }
            go();
        }
    }

 

備註1:

  在Android L版本上:是直接傳送廣播,通過action:PRE_BOOT_COMPLETED,AMS會去發給各個接收者。處理完畢回撥resultTo回來;
  在Android M版本上:這裡就直接指定接收者,一個個傳送出去,處理完畢回撥resultTo回來,繼續下一個。介面上可以看到在變化:顯示正在處理的廣播;
  這樣做的好處介面體驗更好,木有看出有什麼其他特別的用意。

備註2:

  系統都有哪些地方接收PRE_BOOT_COMPLETED,以及什麼情況下應該接收該廣播?
  從目前看到的主要應用在資料庫應用升級方面,資料庫升級涉及到資料欄位變化,資料的增加等會比較耗時,
  為了加快應用啟動和提供資料,需要在開機過程中做升級操作,避免使用時耗時。

備註3:

  這裡其實存在一個隱患:從上面的流程看到,系統傳送廣播給接收者處理,只有等所有接收者處理完畢,才會繼續系統的啟動流程。
  試想:如果某個接收者的操作處理耗時較長,甚至被阻塞 或其他異常導致廣播處理無法完成,不能回撥回來怎麼辦?
  結果:開機時間需要的更長了,或無法開機,一直就卡在這裡無法開機。

  很不幸,這種情況被我遇到過,大概是這樣的:
    某次Hota升級某應用A註冊PRE_BOOT_COMPLETED廣播,處理該廣播時,由於某種情況需要訪問應用B的資料庫,等待應用B啟動,
    由於系統沒有ready和應用B非persisit程式,系統不讓啟動B,結果系統就被阻塞在這裡,始終無法開機。

  這其實是系統不合理的地方,沒有相應的超時控制的安全機制,所幸這裡只允許系統應用接收該廣播,如果允許第三方接收,後果可想而知。

 

2. 收集已經啟動的程式並殺死除過persistent程式

  比如接收PRE_BOOT_COMPLETED啟動的應用
  到此係統準備完畢,可以開始啟動應用程式,並置變數:mProcessesReady = true;

  疑問:系統還沒準備之前不允許啟動非persistent程式,這之前的接收PRE_BOOT_COMPLETED廣播的應用是如何啟動的?
  --》見應用啟動部分分析。

 

3. 系統準備好後回撥傳入的Runnable

  啟動應用和服務:{
    startSystemUi(context);
    connectivityF.systemReady();
    ……
    Watchdog.getInstance().start();
    mmsServiceF.systemRunning();
   }

4. 傳送賬戶啟動的廣播,涉及多使用者

  多使用者的問題這裡不討論。

  注意傳送該廣播前有如下操作:成對出現
  

  //操作前 clear
  long ident = Binder.clearCallingIdentity();

  //do相關操作
  ……

  //操作後restore
  Binder.restoreCallingIdentity(ident);

 通常這倆都是成對出現,具體的作用簡單說下:這涉及到許可權管理後面會討論。

        Binder.clearCallingIdentity():

      通過IPC binder呼叫來遠端程式,當前程式會記錄呼叫者的PID和UID,即通常使用的getCallingPid和getCallingUid,

        而會clearCallingIdentity把呼叫者PID和UID清除,將其設定為當前程式的PID和UID,並將原來的PID和UID作為返回值;

        PID和UID是儲存在一個long型數中,通過移位計算。

           Binder.restoreCallingIdentity(ident):恢復剛才清除的遠端呼叫者的PID和UID。

    這樣做有什麼作用?

    這涉及到許可權管理,clearCallingIdentity介面註釋,舉了incoming call例子,看下原註釋:

    

    大概的意思可以理解成這樣:

      

 

 

      在ProcessB中,InterfaceA呼叫InterfaceB時,InterfaceB中要做許可權檢查,通過getCallingPid,

      這時拿到的PID是ProcessA的,許可權不夠腫麼辦。ProcessB的許可權是夠可以的:就可以如下面

       

    程式碼裡面很多這樣的例子,具體原因請自行體會,貼一段原始碼看看

    

    

5. 啟動桌面Home Activity

  

接上面systemReady最後部分:
    // Start up initial activity.
    mBooting = true;
    startHomeActivityLocked(mCurrentUserId, "systemReady");
    mStackSupervisor.resumeTopActivitiesLocked();

 

一張圖說明AMS啟動如上整個過程:

  

 

  

 

 

AMS的systemready過程基本如上,整個系統準備OK,下面就將開始啟動桌面流程,進入到應用啟動過程分析,應用和元件的啟動將在下一篇分析。

 

相關文章