鍍金池/ 教程/ Android/ 第3章? 深入理解 SystemServer
第2章? 深入理解 Java Binder 和 MessageQueue
第7章? 深入理解 ContentProvider
第5章? 深入理解 PowerManagerService
第3章? 深入理解 SystemServer
第8章? 深入理解ContentService 和 AccountManagerService
第1章?開發(fā)環(huán)境部署
第4章? 深入理解 PackageManagerService
第6章?深入理解ActivityManagerService

第3章? 深入理解 SystemServer

本章主要內(nèi)容:

·??分析SystemServer

·??分析EntropyService、DropBoxManagerService、DiskStatsService

·??分析DeviceStorageMonitorService、SamplingProfilerService以及ClipboardService

本章所涉及的源代碼文件名及位置:

·??SystemServer.java

frameworks/base/services/java/com/android/server/SystemServer.java

·??com_android_server_SystemServer.cpp

frameworks/base/services/jni/com_android_server_SystemServer.cpp

·??System_init.cpp

frameworks/base/cmds/system_server/library/System_init.cpp

·??EntropyService.java

frameworks/base/services/java/com/android/server/EntropyService.java

·??DropBoxManagerService.java

frameworks/base/services/java/com/android/server/DropBoxManagerService.java

·??ActivityManagerService.java

frameworks/base/services/java/com/android/server/am/ActivityManagerService.java

·??DiskStatsService.java

frameworks/base/services/java/com/android/server/DiskStatsService.java

·??dumpsys.cpp

frameworks/base/cmds/dumpsys/dumpsys.cpp

·??DeviceStorageMonitorService.java

frameworks/base/services/java/com/android/server/DeviceStorageMonitorService.java

·??SamplingProfilerService.java

frameworks/base/services/java/com/android/server/SamplingProfilerService.java

·??SamplingProfilerIntegration.java

frameworks/base/core/java/com/android/internal/os/SamplingProfilerIntegration.java

·??SamplingProfiler.java

libcore/dalvik/src/main/java/dalvik/system/profiler/SamplingProfiler.java

·??ClipboardService.java

frameworks/base/services/java/com/android/server/ClipboardService.java

·??ClipboardManager.java(android.content)

frameworks/base/core/java/android/content/ClipboardManager.java

·??ClipboardManager.java(android.text)

frameworks/base/core/java/android/text/ClipboardManager.java

·??ClipData.java

frameworks/base/core/java/android/content/ClipData.java

3.1 ?概述

SystemServer是什么?它可是Android Java世界的兩大支柱之一。另外一個(gè)支柱是專門負(fù)責(zé)孵化Java進(jìn)程的Zygote。這兩大支柱倒了任何一根,都會(huì)導(dǎo)致Android Java世界的崩潰(所有由Zygote孵化的Java進(jìn)程都會(huì)被銷毀。SystemServer就是由Zygote孵化而來)。崩潰之后,幸好Linux系統(tǒng)中的天字號(hào)進(jìn)程init會(huì)重新啟動(dòng)它們以重建Java世界。[①]

SystemServer正如其名,和系統(tǒng)服務(wù)有著重要關(guān)系。Android系統(tǒng)中幾乎所有的核心Service都在這個(gè)進(jìn)程中,如ActivityManagerService、PowerManagerService和WindowManagerService等。那么,作為這些服務(wù)的大本營,SystemServer會(huì)是什么樣的呢?

3.2? SystemServer分析

SystemServer是由Zygote孵化而來的一個(gè)進(jìn)程,通過ps命令,可知其進(jìn)程名為system_server。

3.2.1? main函數(shù)分析

SystemServer核心邏輯的入口是main函數(shù),其代碼如下:

[-->SystemServer.java]

public static void main(String[] args) {

? ???if(System.currentTimeMillis() < EARLIEST_SUPPORTED_TIME) {

??????????? //如果系統(tǒng)時(shí)鐘早于1970,則設(shè)置系統(tǒng)時(shí)鐘從1970開始

???????????Slog.w(TAG, "System clock is before 1970; setting to 1970.");

???????????SystemClock.setCurrentTimeMillis(EARLIEST_SUPPORTED_TIME);

??????? }

??????? //判斷性能統(tǒng)計(jì)功能是否開啟

??????? if(SamplingProfilerIntegration.isEnabled()) {

???????????SamplingProfilerIntegration.start();

???????????timer = new Timer();

???????????timer.schedule(new TimerTask() {

???????????????@Override

???????????????public void run() {

???????????????????//SystemServer性能統(tǒng)計(jì),每小時(shí)統(tǒng)計(jì)一次,統(tǒng)計(jì)結(jié)果輸出為文件

???????????????????SamplingProfilerIntegration.writeSnapshot("system_server",

??????????????????????????????????????????????????????null);

???????????????}// SNAPSHOT_INTERVAL定義為1小時(shí)

???????????}, SNAPSHOT_INTERVAL, SNAPSHOT_INTERVAL);

??????? }

??????? //和Dalvik虛擬機(jī)相關(guān)的設(shè)置,主要是內(nèi)存使用方面的控制

???????dalvik.system.VMRuntime.getRuntime().clearGrowthLimit();

???????VMRuntime.getRuntime().setTargetHeapUtilization(0.8f);

??????? //加載動(dòng)態(tài)庫libandroid_servers.so

???????System.loadLibrary("android_servers");

???????init1(args);//調(diào)用native的init1函數(shù)

? }

main函數(shù)首先做一些初始化工作,然后加載動(dòng)態(tài)庫libandroid_servers.so,最后再調(diào)用native的init1函數(shù)。該函數(shù)在libandroid_servers.so庫中實(shí)現(xiàn),其代碼如下:

[-->com_android_server_SystemServer.cpp]

extern "C" int system_init();

static voidandroid_server_SystemServer_init1(JNIEnv* env, jobject clazz)

{

???system_init(); //調(diào)用上面那個(gè)用extern 聲明的system_init函數(shù)

}

?而system_init函數(shù)又在另外一個(gè)庫libsystem_server.so中實(shí)現(xiàn),代碼如下:

[-->System_init.cpp]

extern "C" status_t system_init()

{

???? LOGI("Enteredsystem_init()");

???? //初始化Binder系統(tǒng)

???sp<ProcessState> proc(ProcessState::self());

???? //獲取ServiceManager的客戶端對(duì)象BpServiceManager

???sp<IServiceManager> sm = defaultServiceManager();

???

???//GrimReaper是一個(gè)很“血腥“的名字,俗稱死神

??? sp<GrimReaper>grim = new GrimReaper();

??? /*

??? 下面這行代碼的作用就是注冊(cè)grim對(duì)象為ServiceManager死亡信息的接收者。一旦SM死亡,

??? Binder系統(tǒng)就會(huì)發(fā)送訃告信息,這樣grim對(duì)象的binderDied函數(shù)就會(huì)被調(diào)用。該函數(shù)內(nèi)部

??? 將kill自己(即SystemServer)。

??? 筆者覺得,對(duì)于這種因摯愛離世而自殺的物體,叫死神好像不太合適

??? */

???sm->asBinder()->linkToDeath(grim, grim.get(), 0);

?

??? charpropBuf[PROPERTY_VALUE_MAX];

??? //判斷SystemServer是否啟動(dòng)SurfaceFlinger服務(wù),該值由init.rc

??? //腳本設(shè)置,默認(rèn)為零,即不啟動(dòng)SF服務(wù)

??? property_get("system_init.startsurfaceflinger",propBuf, "1");

??? /*

??? 從4.0開始,和顯示相關(guān)的核心服務(wù)surfaceflinger可獨(dú)立到另外一個(gè)進(jìn)程中。

??? 筆者認(rèn)為,這可能和目前SystemServer的負(fù)擔(dān)過重有關(guān)。另外,隨著智能終端上HDMI的普及,

??? 未來和顯示相關(guān)的工作將會(huì)越來越繁重。將SF放在單獨(dú)進(jìn)程中,不僅可加強(qiáng)集中管理,也可充分

??? 利用未來智能終端上多核CPU的資源

??? */

??? if(strcmp(propBuf, "1") == 0) {

???????SurfaceFlinger::instantiate();

??? }

??? //判斷SystemServer是否啟動(dòng)傳感器服務(wù),默認(rèn)將啟動(dòng)傳感器服務(wù)

???property_get("system_init.startsensorservice", propBuf,"1");

??? if(strcmp(propBuf, "1") == 0) {

??????? //和SF相同,傳感器服務(wù)也支持在獨(dú)立進(jìn)程中實(shí)現(xiàn)

???????SensorService::instantiate();

??? }

??? //獲得AndroidRuntime對(duì)象

???AndroidRuntime* runtime = AndroidRuntime::getRuntime();

??? JNIEnv*env = runtime->getJNIEnv();

??? ......//查找Java層的SystemServer類,獲取init2函數(shù)的methodID

??? jclassclazz = env->FindClass("com/android/server/SystemServer");

??? ......

???jmethodID methodId = env->GetStaticMethodID(clazz, "init2","()V");

??? ......//通過JNI調(diào)用Java層的init2函數(shù)

??? env->CallStaticVoidMethod(clazz,methodId);

??? //主線程加入Binder線程池

???ProcessState::self()->startThreadPool();

???IPCThreadState::self()->joinThreadPool();

??? returnNO_ERROR;

}

那么,SystemServer的main函數(shù)究竟做了什么呢?

通過init1函數(shù),辛辛苦苦從Java層穿越到Native層,做了一些初始化工作后,又通過JNI從Native層穿越到Java層去調(diào)用init2函數(shù)。

init2函數(shù)返回后,最終又回歸到Native層。

是不是感覺init1和init2這兩個(gè)函數(shù)的命名似曾相識(shí),和我們初學(xué)編程時(shí)自定義的函數(shù)名非常像呢?其實(shí)代碼中有一段“扭捏”的注釋,解釋了編寫這種“初級(jí)”代碼的原因。很簡(jiǎn)單,就是在對(duì)AndroidRuntime初始化前必須對(duì)一些核心服務(wù)初始化。

通過注釋可看出,這段代碼的作者也擔(dān)心被人指責(zé),但至少可以把函數(shù)名取得更形象一點(diǎn)吧?

3.2.2 ?Services群英會(huì)

init1函數(shù)看起來一點(diǎn)也不復(fù)雜,其實(shí)好戲都在init2中,其代碼如下:

[-->SystemServer.java]

public static final void init2() {

?????? ?Thread thr = new ServerThread();

???????thr.setName("android.server.ServerThread");

???????thr.start();//啟動(dòng)一個(gè)線程,這個(gè)線程就像英雄大會(huì)一樣,聚集了各路英雄

}

上面的代碼將創(chuàng)建一個(gè)新的線程ServerThread,該線程的run函數(shù)有600多行。如此之長(zhǎng)的原因是,Android平臺(tái)中眾多Service都匯集于此。先看Services的集體亮相,如圖3-1所示。

http://wiki.jikexueyuan.com/project/deep-android-v2/images/chapter3/image001.png" alt="image" />

圖3-1? Services群英會(huì)

圖3-1中有7大類共43個(gè)Service(包括Watchdog)。實(shí)際上,還有一些Service并沒有在ServerThread的run函數(shù)中露面,后面遇到時(shí)再做介紹。圖3-1中的7大類服務(wù)主要包括:

·??位于第一大類的是Android的核心服務(wù),如ActivityManagerService、WindowManagerService等。

·??位于第二大類的是和通信相關(guān)的服務(wù),如Wifi相關(guān)服務(wù)、Telephone相關(guān)服務(wù)。

·??位于第三大類的是和系統(tǒng)功能相關(guān)的服務(wù),如AudioService、MountService、UsbService等。

·??位于第四大類的是BatteryService、VibratorService等服務(wù)。

·??位于第五大類的是EntropyService,DiskStatsService、Watchdog等相對(duì)獨(dú)立的服務(wù)。

·??位于第六大類的是藍(lán)牙服務(wù)

·??位于第七大類的是UI方面的服務(wù),如狀態(tài)欄服務(wù),通知管理服務(wù)等。

以上服務(wù)的分類并非官方標(biāo)準(zhǔn),僅是筆者個(gè)人之見。

本章將分析其中的第五類服務(wù)。該類中的Service之間關(guān)系簡(jiǎn)單,而且功能相對(duì)獨(dú)立。第五大類服務(wù)包括:

·??EntropyService,熵服務(wù),它和隨機(jī)數(shù)的生成有關(guān)。

·??ClipboardService,剪貼板服務(wù)。

·??DropBoxManagerService,該服務(wù)和系統(tǒng)運(yùn)行時(shí)日志的存儲(chǔ)與管理有關(guān)。

·??DiskStatsService以及DeviceStorageMonitorService,這兩個(gè)服務(wù)用于查看和監(jiān)測(cè)系統(tǒng)存儲(chǔ)空間。

·??SamplingProfilerService,這個(gè)服務(wù)是4.0新增的,功能非常簡(jiǎn)單。

·??Watchdog,即看門狗,是Android的“老員工”了。我們?cè)诰鞩第4章“深入理解Zygote”中曾分析過它。Android2.3以后其內(nèi)存檢測(cè)功能被去掉,所以與Android 2.2相比,更顯簡(jiǎn)單了。這只小狗很可愛,就留給讀者自己分析了。后面,將逐次分析這第五類服務(wù)的其他幾項(xiàng)服務(wù)。

3.3? EntropyService分析

根據(jù)物理學(xué)基本原理,一個(gè)系統(tǒng)的熵越大,該系統(tǒng)就越不穩(wěn)定。在Android中,目前也只有隨機(jī)數(shù)喜歡處于這種不穩(wěn)定的系統(tǒng)中了。

SystemServer中添加該服務(wù)的代碼如下:

ServiceManager.addService("entropy", newEntropyService());

上邊代碼非常簡(jiǎn)單,從中可直接分析EntropyService的構(gòu)造函數(shù):

[-->EntropyService.java]

public EntropyService() {

??????? //調(diào)用另外一個(gè)構(gòu)造函數(shù),getSystemDir函數(shù)返回的是/data/system目錄

???????this(getSystemDir() + "/entropy.dat","/dev/urandom");

}

public EntropyService(String entropyFile, StringrandomDevice) {

?? this.randomDevice= randomDevice;//urandom是Linux系統(tǒng)中產(chǎn)生隨機(jī)數(shù)的設(shè)備

? ?// /data/system/entropy.dat文件保存了系統(tǒng)此前的熵信息

??this.entropyFile = entropyFile;

? //下面有4個(gè)關(guān)鍵函數(shù)

??loadInitialEntropy();//①

??addDeviceSpecificEntropy();//②

??writeEntropy();//③

??scheduleEntropyWriter();//④

}

從以上代碼中可以看出,EntropyService構(gòu)造函數(shù)中依次調(diào)用了4個(gè)關(guān)鍵函數(shù),這4個(gè)函數(shù)比較簡(jiǎn)單,這里只介紹它們的作用。感興趣的讀者可自行分析其代碼。

·??loadInitialEntropy函數(shù):將entropy.dat文件的中內(nèi)容寫到urandom設(shè)備,這樣可增加系統(tǒng)的隨機(jī)性。根據(jù)代碼中的注釋,系統(tǒng)中有一個(gè)entropypool。在系統(tǒng)剛啟動(dòng)時(shí),該pool中的內(nèi)容為空,導(dǎo)致早期生成的隨機(jī)數(shù)變得可預(yù)測(cè)。通過將entropy.dat數(shù)據(jù)寫到該entropy pool(這樣該pool中的內(nèi)容就不為空)中,隨機(jī)數(shù)的生成就無規(guī)律可言了。

·??addDeviceSpecificEntropy函數(shù):將一些和設(shè)備相關(guān)的信息寫入urandom設(shè)備。這些信息如下:

out.println("Copyright (C) 2009 The AndroidOpen Source Project");

out.println("All Your Randomness Are BelongTo Us");

out.println(START_TIME);

out.println(START_NANOTIME);

out.println(SystemProperties.get("ro.serialno"));

out.println(SystemProperties.get("ro.bootmode"));

out.println(SystemProperties.get("ro.baseband"));

out.println(SystemProperties.get("ro.carrier"));

out.println(SystemProperties.get("ro.bootloader"));

out.println(SystemProperties.get("ro.hardware"));

out.println(SystemProperties.get("ro.revision"));

out.println(new Object().hashCode());

out.println(System.currentTimeMillis());

out.println(System.nanoTime());

該函數(shù)的注釋表明,即使向urandom的entropy pool中寫入固定信息,也能增加隨機(jī)數(shù)生成的隨機(jī)性。從熵的角度考慮,系統(tǒng)的質(zhì)量越大(即pool中的內(nèi)容越多),該系統(tǒng)越不穩(wěn)定。

·??writeEntropy函數(shù):讀取urandom設(shè)備的內(nèi)容到entropy.dat文件。

·??scheduleEntropyWriter函數(shù):向EntropyService內(nèi)部的Handler發(fā)送一個(gè)ENTROPY_WHAT消息。該消息每3小時(shí)發(fā)送一次。收到該消息后,EntropyService會(huì)再次調(diào)用writeEntropy函數(shù),將urandom設(shè)備的內(nèi)容寫到entropy.dat中。

通過上面的分析可知,entropy.dat文件保存了urandom設(shè)備內(nèi)容的快照(每三小時(shí)更新一次)。當(dāng)系統(tǒng)重新啟動(dòng)時(shí),EntropyService又利用這個(gè)文件來增加系統(tǒng)的熵,通過這種方式使隨機(jī)數(shù)的生成更加不可預(yù)測(cè)。

EntropyService本身的代碼很簡(jiǎn)單,但是為了盡量保證隨機(jī)數(shù)的隨機(jī)性,Android還是下了一番苦功的。

?

3.4?DropBoxManagerService分析

DropBoxManagerService(簡(jiǎn)稱DBMS,下同)用于生成和管理系統(tǒng)運(yùn)行時(shí)的一些日志文件。這些日志文件大多記錄的是系統(tǒng)或某個(gè)應(yīng)用程序出錯(cuò)時(shí)的信息。

下面來分析這項(xiàng)服務(wù)。其中向SystemServer添加DBMS的代碼:

ServiceManager.addService(Context.DROPBOX_SERVICE,//服務(wù)名為”dropbox”

???? ??????????????????????new DropBoxManagerService(context,

?????????????????????????? newFile("/data/system/dropbox")));

?

3.4.1? DBMS構(gòu)造函數(shù)分析

DBMS構(gòu)造函數(shù)如下:

[-->DropBoxManagerService.java]

public DropBoxManagerService(final Contextcontext, File path) {

???????mDropBoxDir = path;//path指定dropbox目錄為/data/system/dropbox

???????mContext = context;

???????mContentResolver = context.getContentResolver();

?

???????IntentFilter filter = new IntentFilter();

???????filter.addAction(Intent.ACTION_DEVICE_STORAGE_LOW);

???????filter.addAction(Intent.ACTION_BOOT_COMPLETED);

??????? //注冊(cè)一個(gè)Broadcast監(jiān)聽對(duì)象,當(dāng)系統(tǒng)啟動(dòng)完畢或者設(shè)備存儲(chǔ)空間不足時(shí),會(huì)收到廣播

???????context.registerReceiver(mReceiver, filter);

?????? //當(dāng)Settings數(shù)據(jù)庫相應(yīng)項(xiàng)發(fā)生變化時(shí)候,也需要告知DBMS進(jìn)行相應(yīng)處理

???????mContentResolver.registerContentObserver(

???????????Settings.Secure.CONTENT_URI, true,

???????????new ContentObserver(new Handler()) {

???????????????public void onChange(boolean selfChange) {

?????????????//當(dāng)Settings數(shù)據(jù)庫發(fā)生變化時(shí)候, BroadcastReceiver的onReceive函數(shù)

?????????????//將被調(diào)用。注意第二個(gè)參數(shù)為null

???????????????????mReceiver.onReceive(context,(Intent) null);

???????????????}

??????? });

}

根據(jù)上面代碼可知:DBMS注冊(cè)一個(gè)BroadcastReceiver對(duì)象,同時(shí)會(huì)監(jiān)聽Settings數(shù)據(jù)庫的變動(dòng)。其核心邏輯都在此BroadcastReceiver的onReceive函數(shù)中。該函數(shù)在以下三種情況發(fā)生時(shí)被調(diào)用:

·??當(dāng)系統(tǒng)啟動(dòng)完畢時(shí),由BOOT_COMPLETED廣播觸發(fā)。

·??當(dāng)設(shè)備存儲(chǔ)空間不足時(shí),由DEVICE_STORAGE_LOW廣播觸發(fā)。

·??當(dāng)Settings數(shù)據(jù)庫相應(yīng)項(xiàng)發(fā)生變化時(shí)候,該函數(shù)也會(huì)被觸發(fā)。

這個(gè)函數(shù)內(nèi)容較簡(jiǎn)單,主要功能是存儲(chǔ)空間不足時(shí)需要?jiǎng)h除一些老舊的日志文件以節(jié)省存儲(chǔ)空間。讀者可自行分析這個(gè)函數(shù)。

?

3.4.2? dropbox日志文件的添加

要想理清一個(gè)Service,最面好從它提供的服務(wù)開始進(jìn)行分析。根據(jù)前面對(duì)DBMS的介紹可知,它提供了記錄系統(tǒng)運(yùn)行時(shí)日志信息的功能,所以這里先從dropbox日志文件的生成時(shí)說起。

當(dāng)某個(gè)應(yīng)用程序因?yàn)榘l(fā)生異常而崩潰(crash)時(shí),ActivityManagerService(簡(jiǎn)稱AMS,下同)的handleApplicationCrash函數(shù)被調(diào)用,其代碼如下:

[-->ActivityManagerService.java]

public void handleApplicationCrash(IBinder app,

???? ????????????????ApplicationErrorReport.CrashInfocrashInfo) {

?? ProcessRecordr = findAppProcess(app, "Crash");

?? ......

? ?//調(diào)用addErrorToDropBox函數(shù),第一個(gè)參數(shù)是一個(gè)字符串,為“crash”

?? addErrorToDropBox("crash",r, null, null, null, null, null, crashInfo);

?? ......

}

下面來看addErrorToDropBox函數(shù):

[-->ActivityManagerService.java]

public void addErrorToDropBox(String eventType,

???????????ProcessRecord process, ActivityRecord activity,

??????????? ActivityRecordparent, String subject,

???????????final String report, final File logFile,

???????????final ApplicationErrorReport.CrashInfo crashInfo) {

??????

?? ?/*

??? dropbox日志文件的命名有一定的規(guī)則,其前綴都是一個(gè)特定的tag(標(biāo)簽),

??? tag由兩部分組成,合起來是”進(jìn)程類型”_”事件類型”。

??? 下邊代碼中的processClass函數(shù)返回該進(jìn)程的類型,包括“system_server”、“system_app”

??? 和“data_app”三種。eventType用于指定事件類型,目前也有三種類型:“crash“、”wtf“

??? (what aterrible failure)和“anr”

??? */

??? finalString dropboxTag = processClass(process) + "_" + eventType;

??? //獲取DBMS Bn端的對(duì)象DropBoxManager

???????final DropBoxManager dbox = (DropBoxManager)

???????????????mContext.getSystemService(Context.DROPBOX_SERVICE);

??? ?/*

??? ??對(duì)于DBMS,不僅通過tag于標(biāo)示文件名,還可以根據(jù)配置的情況,允許或禁止特定tag日志

????? 文件的記錄。isTagEnable將判斷DBMS是否禁止該標(biāo)簽,如果該tag已被禁止,則不允許記

????? 錄日志文件

????? */

??????? if(dbox == null || !dbox.isTagEnabled(dropboxTag)) return;

??????? //創(chuàng)建一個(gè)StringBuilder,用于保存日志信息

???????final StringBuilder sb = new StringBuilder(1024);

???????appendDropBoxProcessHeaders(process, sb);

??????? ......//將信息保存到字符串sb中

???????? //單獨(dú)啟動(dòng)一個(gè)線程用于向DBMS添加信息

???????Thread worker = new Thread("Error dump: " + dropboxTag) {

???????????@Override

???????????public void run() {

???????????????if (report != null) {

???????????????????sb.append(report);

???????????????}

???????????????if (logFile != null) {

???????????????????try {//如果有l(wèi)og文件,那么就把log文件內(nèi)容讀到sb中

???????????????????????sb.append(FileUtils.readTextFile(logFile,

??????????????????????????????? 128 * 1024,"\n\n[[TRUNCATED]]"));

???????????????????} ......

???????????????}

???????????????//讀取crashInfo信息,一般記錄的是調(diào)用堆棧信息

???????????????if (crashInfo != null && crashInfo.stackTrace != null) {

???????????????????sb.append(crashInfo.stackTrace);

???????????????}

???????????????String setting = Settings.Secure.ERROR_LOGCAT_PREFIX + dropboxTag;

??????????????//查詢Settings數(shù)據(jù)庫,判斷該tag類型的日志是否對(duì)所記錄的信息有行數(shù)限制,

?????????????//例如某些tag的日志文件只準(zhǔn)記錄1000行的信息

??? ??????????int lines =Settings.Secure.getInt(mContext.getContentResolver(),

???????????????????????????????????????????????????????setting, 0);

???????????????if (lines > 0) {

???????????????????sb.append("\n");

???????????????????? InputStreamReader input =null;

???????????????????try {

??????????????????????? //創(chuàng)建一個(gè)新進(jìn)程以運(yùn)行l(wèi)ogcat,后面的參數(shù)都是logcat常用的參數(shù)

??????????????????????? java.lang.Processlogcat = new

???????????????????????? ?ProcessBuilder("/system/bin/logcat",

??????????????????????? "-v","time", "-b", "events", "-b","system", "-b",

??????????????????????????? ?"main", "-t", String.valueOf(lines))

??????????????????????????????? .redirectErrorStream(true).start();

???????????????????? //由于新進(jìn)程的輸出已經(jīng)重定向,因此這里可以獲取最后lines行的信息,

???????????????????//不熟悉ProcessBuidler的讀者可以查看SDK中關(guān)于它的用法說明

???????????????????? ......

??????????????????}

???????????????}

??????????????//調(diào)用DBMS的addText

???????????????dbox.addText(dropboxTag, sb.toString());

???????????}

??????? };

??????? if(process == null || process.pid == MY_PID) {

???????????worker.run(); //如果是SystemServer進(jìn)程crash了,則不能在別的線程執(zhí)行

??????? }else {

???????????worker.start();

???? }

}

由上面代碼可知,addErrorToDropBox在生成日志的內(nèi)容上花了不少工夫,因?yàn)檫@些是最重要的,最后僅調(diào)用addText函數(shù)便將內(nèi)容傳給DBMS的功能。

addText函數(shù)定義在DropBoxManager類中,代碼如下:

[-->DropBoxManager.java]

public void addText(String tag, String data) {

? /*

?? mService和DBMS交互。DBMS對(duì)外只提供一個(gè)add函數(shù)用于日志添加,而DBM提供了3個(gè)函數(shù),

?? 分別是addText、addData、addFile,以方便我們的使用

? */

?? try ......

}

DBM向DBMS傳遞的數(shù)據(jù)被封裝在一個(gè)Entry中。下面來看DBMS的add函數(shù),其代碼如下:

[-->DropBoxManagerService.java]

public void add(DropBoxManager.Entry entry) {

??????? Filetemp = null;

???????OutputStream output = null;

??????? finalString tag = entry.getTag();//先取出這個(gè)Entry的tag

??????? try{

???????????int flags = entry.getFlags();

??????????? ......

??????? ????//做一些初始化工作,包括生成dropbox目錄、統(tǒng)計(jì)當(dāng)前已有的dropbox文件信息等

???????????init();

???????????if (!isTagEnabled(tag)) return;//如果該tag被禁止,則不能生成日志文件

???????????long max = trimToFit();

???????????long lastTrim = System.currentTimeMillis();

???????????//BlockSize一般是4KB

???????????byte[] buffer = new byte[mBlockSize];

???????????//從Entry中得到一個(gè)輸入流。與Java I/O相關(guān)的類比較多,且用法非常靈活

??? ???????//建議讀者閱讀《Java編程思想》中“Java I/O系統(tǒng)”一章

??????????? InputStreaminput = entry.getInputStream();

??????????? ......

???????????int read = 0;

???????????while (read < buffer.length) {

???????????????int n = input.read(buffer, read, buffer.length - read);

?????????? ?????if (n <= 0) break;

???????????????read += n;

???????????}

???????????//先生成一個(gè)臨時(shí)文件,命名方式為”drop線程id.tmp”

???????????temp = new File(mDropBoxDir, "drop" +

???????????????????????? Thread.currentThread().getId()+ ".tmp");

???????????int bufferSize = mBlockSize;

???????????if (bufferSize > 4096) bufferSize = 4096;

???????????if (bufferSize < 512) bufferSize = 512;

???????????FileOutputStream foutput = new FileOutputStream(temp);

???????????output = new BufferedOutputStream(foutput, bufferSize);

????????????//生成GZIP壓縮文件

???????????if (read == buffer.length &&

???? ????????????((flags &DropBoxManager.IS_GZIPPED) == 0)) {

???????????????output = new GZIPOutputStream(output);

???????????????flags = flags | DropBoxManager.IS_GZIPPED;

?????????? ?}

????????????/*

????????????????DBMS很珍惜/data分區(qū),若所生成文件的size大于一個(gè)BlockSize,

????????????????則一定要先壓縮。

??????????? */

??????????? ......//寫文件,這段代碼非常繁瑣,其主要目的是盡量節(jié)省存儲(chǔ)空間

??????????? /*

??????????? 生成一個(gè)EntryFile對(duì)象,并保存到DBMS內(nèi)部的一個(gè)數(shù)據(jù)容器中。

????????????DBMS除了負(fù)責(zé)生成文件外,還提供查詢的功能,這個(gè)功能由getNextEntry完成。

????????? ???另外,剛才生成的臨時(shí)文件在createEntry函數(shù)中會(huì)重命為帶標(biāo)簽的名字,

????????????讀者可自行分析createEntry函數(shù)

????????????*/

???????????long time = createEntry(temp, tag, flags);

???????????temp = null;

???????????Intent dropboxIntent = new

??????????????????????? ?Intent(DropBoxManager.ACTION_DROPBOX_ENTRY_ADDED);

???????????dropboxIntent.putExtra(DropBoxManager.EXTRA_TAG, tag);

???????????dropboxIntent.putExtra(DropBoxManager.EXTRA_TIME, time);

???????????if (!mBooted) {

???????????????dropboxIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);

???????????}

???????????//發(fā)送DROPBOX_ENTRY_ADDED廣播。系統(tǒng)中目前還沒有程序接收該廣播

???????????mContext.sendBroadcast(dropboxIntent,

??????????????????????????? android.Manifest.permission.READ_LOGS);

??????? }......

}

上面代碼中略去了DBMS寫文件的部分,我們從代碼注釋中可獲悉,DBMS非常珍惜/data分區(qū)的空間,每一個(gè)日志文件都需要考慮壓縮以節(jié)省存儲(chǔ)空間。如果說細(xì)節(jié)體現(xiàn)功力,那么這正是一個(gè)極好的例證。

一個(gè)真實(shí)設(shè)備上/data/system/dropbox目錄的內(nèi)容如圖3-2所示。

http://wiki.jikexueyuan.com/project/deep-android-v2/images/chapter3/image002.png" alt="image" />

圖3-2? 真實(shí)設(shè)備中dropbox目錄的內(nèi)容

圖3-2中最后一項(xiàng)data_app_anr@1324836096560.txt.gz的大小是6.1KB,該文件解壓后得到的文件大小是42kB??磥?,壓縮確實(shí)節(jié)省了不少存儲(chǔ)空間。

另外,我們從圖3-2中還發(fā)現(xiàn)了其他不同的tag,如SYSTEM_BOOT、SYSTEM_TOMBSTONE等,這些都是由BootReceiver在收到BOOT_COMPLETE廣播后收集相關(guān)信息并傳遞給DBMS而生成的日志文件。

3.4.3? DBMS和settings數(shù)據(jù)庫

DBMS的運(yùn)行依賴一些配置項(xiàng)。其實(shí)除了DBMS,SystemServer中很多服務(wù)都依賴相關(guān)的配置選項(xiàng)。這些配置項(xiàng)都是通過SettingsProvider操作Settings數(shù)據(jù)庫來設(shè)置和查詢的。SettingsProvider是系統(tǒng)中很重要的一個(gè)APK,如果將其刪除,系統(tǒng)就不能正常啟動(dòng)。

這里總結(jié)一下和DBMS相關(guān)的設(shè)置項(xiàng),具體情況如下(注意,右邊雙引號(hào)的內(nèi)容是該配置項(xiàng)在數(shù)據(jù)庫中的名字。這些和系統(tǒng)相關(guān)的項(xiàng)都在Settings數(shù)據(jù)庫中的Secure表內(nèi)):

//用來判斷是否允許記錄該tag類型的日志文件。默認(rèn)是允許生成任何tag類型的文件

Secure.DROPBOX_TAG_PREFIX+tag: “dropbox:”+tag

//用于控制每個(gè)日志文件的存活時(shí)間,默認(rèn)是三天。大于三天的日志文件就會(huì)被刪除以節(jié)省空間

Secure.DROPBOX_AGE_SECONDS: ”dropbox_age_seconds”

//用于控制系統(tǒng)保存的日志文件個(gè)數(shù),默認(rèn)是1000個(gè)文件

Secure.DROPBOX_MAX_FILES:”dropbox_max_files”

//用于控制dropbox目錄最多占存儲(chǔ)空間容量的比例,默認(rèn)是10%

Secure.DROPBOX_QUOTA_PERCENT:”dropbox_quota_percent”

//不允許dropbox使用的存儲(chǔ)空間的比例,默認(rèn)是10%,也就是dropbox最多只能使用90%的空間

Secure.DROPBOX_RESERVE_PERCENT:”dropbox_reserve_percent”

//dropbox最大能使用的空間大小,默認(rèn)是5MB

Secure.DROPBOX_QUOTA_KB:”dropbox_quota_kb”

感興趣的讀者可以通過adb shell進(jìn)入/data/data/com.android.providers.settings/databases/目錄,然后利用sqlite3命令操作settings.db,其中有一個(gè)Secure表。不過系統(tǒng)中的很多選項(xiàng)在該表中都沒有相關(guān)設(shè)置,因此實(shí)際運(yùn)行時(shí)都會(huì)使用代碼中設(shè)置的默認(rèn)值。

3.5?DiskStatsService和DeviceStorageMonitorService分析

DiskStatsService和DeviceStroageMonitorService與系統(tǒng)內(nèi)部存儲(chǔ)管理和監(jiān)控有關(guān)。

3.5.1?DiskStatsService分析

DiskStatsService代碼非常簡(jiǎn)單,不過也有一個(gè)很有意思的地方,例如:

[-->DiskStatsService.java]

public class DiskStatsService extends Binder

DiskStatsService從Binder派生,卻沒有實(shí)現(xiàn)任何接口,也就是說,DiskStatsService沒有任何業(yè)務(wù)函數(shù)可供調(diào)用。為什么系統(tǒng)會(huì)存在這樣的服務(wù)呢?

為了解釋這個(gè)問題,有必要先把系統(tǒng)中一個(gè)很重要的命令dumpsys請(qǐng)出來。正如其名,這個(gè)命令用于打印系統(tǒng)中指定服務(wù)的信息,代碼如下:

[-->dumpsys.cpp]

int main(int argc, char* const argv[])

{

??? //先獲取與ServiceManager進(jìn)程通信的BpServiceManager對(duì)象

???sp<IServiceManager> sm = defaultServiceManager();

???fflush(stdout);

???

?? ?Vector<String16> services;

???Vector<String16> args;

??? if (argc== 1) {//如果輸入?yún)?shù)個(gè)數(shù)為1,則先查詢?cè)赟M中注冊(cè)的所有Service

???????services = sm->listServices();

??????? //將service排序

???????services.sort(sort_func);

??????? args.add(String16("-a"));

??? } else {

??? ????//指定查詢某個(gè)service

???????services.add(String16(argv[1]));

??????? //保存剩余參數(shù),以后可以傳給service的dump函數(shù)

??????? for(int i=2; i<argc; i++) {

???????????args.add(String16(argv[i]));

??????? }

??? }

?

??? constsize_t N = services.size();

??? ......

??? for(size_t i=0; i<N; i++) {

???????sp<IBinder> service = sm->checkService(services[i]);

????????......

???????? //通過Binder調(diào)用該service的dump函數(shù),將args也傳給dump函數(shù)

??????? ?int err = service->dump(STDOUT_FILENO,args);

??????? ?......

??? }

??? return0;

}

從上面代碼可知,dumpsys通過Binder調(diào)用某個(gè)Service的dump函數(shù)。那么

“dumpsys diskstats”的輸出會(huì)是什么呢?馬上來試試,結(jié)果如圖3-3所示。

http://wiki.jikexueyuan.com/project/deep-android-v2/images/chapter3/image003.png" alt="image" />

圖3-3? dumpsys diskstats的結(jié)果圖示

圖3-3說明了執(zhí)行“dumpsysdiskstats”打印了系統(tǒng)中內(nèi)部存儲(chǔ)設(shè)備的使用情況。dumpsys是工作中常用的命令,建議讀者掌握它的用法。

再來看DiskStatsService的dump函數(shù),代碼如下:

[-->DiskStatsService.java]

protected void dump(FileDescriptor fd, PrintWriterpw, String[] args) {

???????byte[] junk = new byte[512];

??????? for(int i = 0; i < junk.length; i++) junk[i] = (byte) i;?

??????? //輸出/data/system/perftest.tmp文件信息,輸出后即刪除該文件

??????? //目前還不清楚這個(gè)文件由誰生成。從名字上看應(yīng)該和性能測(cè)試有關(guān)

??????? Filetmp = new File(Environment.getDataDirectory(),

??????????????????????????????? "system/perftest.tmp");

???????FileOutputStream fos = null;

???????IOException error = null;

??????? longbefore = SystemClock.uptimeMillis();

??????? try{

???????????fos = new FileOutputStream(tmp);

???????????fos.write(junk);

??????? } ......

??????? longafter = SystemClock.uptimeMillis();

??????? if(tmp.exists()) tmp.delete();

?

??????? if(error != null) {

??????????? ......

??????? }else {

???????????pw.print("Latency: ");

???????????pw.print(after - before);

???????????pw.println("ms [512B Data Write]");

??????? }

??????? //打印內(nèi)部存儲(chǔ)設(shè)備各個(gè)分區(qū)的信息

???????reportFreeSpace(Environment.getDataDirectory(), "Data", pw);

???????reportFreeSpace(Environment.getDownloadCacheDirectory(),"Cache", pw);

???????reportFreeSpace(new File("/system"), "System", pw);

??????? //有些廠商還會(huì)將/proc/yaffs信息打印出來

}

從前述代碼中可發(fā)現(xiàn),DiskStatsService沒有實(shí)現(xiàn)任何業(yè)務(wù)接口,似乎只是為了調(diào)試而存在。所以筆者認(rèn)為,DiskStatsService的功能完全可以被整合到后面即將介紹的DeviceStorageManagerService類中??傊?,本節(jié)最重要的就是dumpsys這個(gè)命令了,建議讀者一定要掌握它的用法。

3.5.2?DeviceStorageManagerService分析

DeviceStorageManagerService(簡(jiǎn)稱DSMS,下同)是用來監(jiān)測(cè)系統(tǒng)內(nèi)部存儲(chǔ)空間的狀態(tài)的,添加該服務(wù)的代碼如下:

//DSMS的服務(wù)名為“devicestoragemonitor “

ServiceManager.addService(DeviceStorageMonitorService.SERVICE,

??????????????????????? newDeviceStorageMonitorService(context));

DSMS的構(gòu)造函數(shù)的代碼如下:

[-->DeviceStorageManagerService.java]

public DeviceStorageMonitorService(Contextcontext) {

???????mLastReportedFreeMemTime = 0;

???????mContext = context;

???????mContentResolver = mContext.getContentResolver();

???????mDataFileStats = new StatFs(DATA_PATH);//獲取data分區(qū)的信息

???????mSystemFileStats = new StatFs(SYSTEM_PATH);// 獲取system分區(qū)的信息

???????mCacheFileStats = new StatFs(CACHE_PATH);// 獲取cache分區(qū)的信息

??????? //獲得data分區(qū)的總大小

???????mTotalMemory = ((long)mDataFileStats.getBlockCount() *

???????????????????????mDataFileStats.getBlockSize())/100L;

??????? /*

??? ????創(chuàng)建三個(gè)Intent,分別用于通知存儲(chǔ)空間不足、存儲(chǔ)空間恢復(fù)正常和存儲(chǔ)空間滿。

??????? 由于設(shè)置了REGISTERED_ONLY_BEFORE_BOOT標(biāo)志,這3個(gè)Intent廣播只能由

??? ????系統(tǒng)服務(wù)接收

??? ????*/

??? ???mStorageLowIntent = newIntent(Intent.ACTION_DEVICE_STORAGE_LOW);

????? ?mStorageLowIntent.addFlags(

??????????????????????? Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);

???????mStorageOkIntent = new Intent(Intent.ACTION_DEVICE_STORAGE_OK);

???????mStorageOkIntent.addFlags(

???????????????????Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);

???????mStorageFullIntent = new Intent(Intent.ACTION_DEVICE_STORAGE_FULL);

???????mStorageFullIntent.addFlags(

???????????????????Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);

???????mStorageNotFullIntent = new

?? ?????????????????Intent(Intent.ACTION_DEVICE_STORAGE_NOT_FULL);

???????mStorageNotFullIntent.addFlags(

???????????????????Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);

???????

??????? //查詢Settings數(shù)據(jù)庫中sys_storage_threshold_percentage的值,默認(rèn)是10,

??????? //即當(dāng)/data空間只剩下10%的時(shí)候,,認(rèn)為空間不足

???????mMemLowThreshold = getMemThreshold();

?????? //查詢Settings數(shù)據(jù)庫中的sys_storage_full_threshold_bytes的值,默認(rèn)是1MB,

?????? //即當(dāng)data分區(qū)只剩1MB時(shí),就認(rèn)為空間已滿,剩下的這1MB空間保留給系統(tǒng)自用

???????mMemFullThreshold = getMemFullThreshold();

????? ??//檢查內(nèi)存

???????checkMemory(true);

}

再來看checkMemory函數(shù),代碼如下:

?

private final void checkMemory(boolean checkCache){

??if(mClearingCache) {

??????......//如果正在清理空間,則不作處理

? ???} else{

???????????restatDataDir();//重新計(jì)算三個(gè)分區(qū)的剩余空間大小

??????????? //如果剩余空間低于mMemLowThreshold,那么先清理一次空間

????????????clearCache();

????????? ??//如果空間仍不足,則發(fā)送廣播,并在狀態(tài)欄上設(shè)置一個(gè)警告通知

?????????????sendNotification();

?????????????......

????????? ??//如果空間已滿,則調(diào)用下面這個(gè)函數(shù),以發(fā)送一次存儲(chǔ)已滿的廣播

???????? ????sendFullNotification();

??????? } ......

??????//DEFAULT_CHECK_INTERVAL為1分鐘,即每一分鐘會(huì)觸發(fā)一次檢查,似乎有點(diǎn)短

???????postCheckMemoryMsg(true, DEFAULT_CHECK_INTERVAL);

}

當(dāng)空間不足時(shí),DSMS會(huì)先嘗試clearCache函數(shù),該函數(shù)內(nèi)部會(huì)與PackageManagerService交互,其代碼如下:

[-->DeviceStorageManagerService.java]

private final void clearCache() {

?if(mClearCacheObserver == null) {

????? //創(chuàng)建一個(gè)CachePackageDataObserver對(duì)象,當(dāng)PKM清理完空間時(shí)會(huì)回調(diào)該對(duì)象的

????? //onRemoveCompleted函數(shù)

??????mClearCacheObserver = new CachePackageDataObserver();

?? }

?mClearingCache= true;//設(shè)置mClearingCache的值為true,表示我們正在清理空間

?try {

???? //調(diào)用PKM的freeStorageAndNotify函數(shù)以清理空間,這個(gè)函數(shù)在分析PKM時(shí)再介紹

??? IPackageManager.Stub.asInterface(

??????????? ServiceManager.getService("package")).

??????????? freeStorageAndNotify(mMemLowThreshold,mClearCacheObserver);

?? } ......

}

CachePackageDataObserver是DSMS定義的內(nèi)部類,其onRemoveCompleted函數(shù)很簡(jiǎn)單,就是重新發(fā)送消息,讓DSMS再檢測(cè)一次內(nèi)存空間。

DeviceStorageManagerService的功能單一,沒有重載dump函數(shù)。而DiskStatsService唯一有用的就是dump功能了。不知Google的工程師為什么沒有把DeviceStorageManagerService和DiskStatsService的功能整合到一起。

3.6? SamplingProfilerService分析

添加SamplingProfilerService服務(wù)的代碼如下:

ServiceManager.addService("samplingprofiler",//服務(wù)名

??????????????????????????? newSamplingProfilerService(context));

3.6.1?SamplingProfilerService構(gòu)造函數(shù)分析

下面給來分析SamplingProfilerService的構(gòu)造函數(shù),其代碼如下:

[-->SamplingProfilerService.java]

public SamplingProfilerService(Context context) {

?????? //注冊(cè)一個(gè)CotentObserver,用于監(jiān)測(cè)Settings數(shù)據(jù)庫的變化

???????registerSett