APK安裝流程系列文章整體內(nèi)容如下:
- APK安裝流程詳解0——前言
- APK安裝流程詳解1——有關(guān)"安裝ing"的實(shí)體類概述
- APK安裝流程詳解2——PackageManager簡(jiǎn)介
- APK安裝流程詳解3——PackageManager與PackageManagerService
- APK安裝流程詳解4——安裝中關(guān)于so庫(kù)的哪些事
- APK安裝流程詳解5——PackageInstallerService和Installer
- APK安裝流程詳解6——PackageManagerService啟動(dòng)前奏
- APK安裝流程詳解7——PackageManagerService的啟動(dòng)流程(上)
- APK安裝流程詳解8——PackageManagerService的啟動(dòng)流程(下)
- APK安裝流程詳解9——PackageParser解析APK(上)
- APK安裝流程詳解10——PackageParser解析APK(下)
- APK安裝流程詳解11——普通應(yīng)用安裝簡(jiǎn)介
- APK安裝流程詳解12——PackageManagerService中的新安裝流程上(拷貝)
- APK安裝流程詳解13——PackageManagerService中的新安裝流程下(裝載)
- APK安裝流程詳解14——PMS中的新安裝流程上(拷貝)補(bǔ)充
- APK安裝流程詳解15——PMS中的新安裝流程下(裝載)補(bǔ)充
- APK安裝流程詳解16——Android包管理總結(jié)(尚未完結(jié)請(qǐng)期待)
本片文章的主要內(nèi)容如下:
- 1、ApplicationPackageManager中相關(guān)方法的跟蹤
- 1.1 、installPackageWithVerification(Uri,PackageInstallObserver, int, String,Uri, ManifestDigest,ContainerEncryptionParams)方法
- 1.2、installCommon(Uri,PackageInstallObserver, int, String,int)方法解析
- 2、PackageManagerService中相關(guān)方法的跟蹤
- 2.1 installPackage(String, IPackageInstallObserver2, int, String,VerificationParams,String)方法解析
- 2.2 installPackageAsUser(String, IPackageInstallObserver2, int, String, VerificationParams,String, int)方法解析
- 2.3 PackageHandler的HandlerMesaage方法中what值為INIT_COPY的情況:
- 2.4 PackageHandler的connectToService方法解析
- 2.5 PackageHandler的HandlerMesaage方法中what值為MCS_BOUND的情況:
- 2.6 HandlerParams的startCopy方法
- 2.7 InstallParams的handleStartCopy方法
從上面一片文章我們知道InstallAppProgress里面最后更新的代碼是調(diào)用到PackageManager#installPackageWithVerificationAndEncryption方法,那我們就從這個(gè)方法開(kāi)始進(jìn)行跟蹤分析
總體流程大致如下:
涉及到類的流程如下:
我將上面整個(gè)安裝流程分為兩大步驟
- 1、第一步:拷貝安裝包
- 2、第二步:裝載代碼
本片文章主要講解"拷貝",即將安裝包拷貝到/data目錄下,同時(shí)為了保證本篇文章的流程性, 本片文章只講主流程,在主流程涉及到的復(fù)雜問(wèn)題,或者小分支,我會(huì)提出問(wèn)題,但由于簡(jiǎn)書(shū)的篇幅問(wèn)題,我就不在本篇文章深入了,我會(huì)在這篇文章APK安裝流程詳解14——PMS中的新安裝流程上(拷貝)補(bǔ)充去深入講解,但我會(huì)在本篇文章中提出問(wèn)題,并給出答案的索引。
一、ApplicationPackageManager中相關(guān)方法的跟蹤
通過(guò)前面的研究我們知道PackageManager是個(gè)抽象類,具體的實(shí)現(xiàn)類是ApplicationPackageManager,那我們就來(lái)看下ApplicationPackageManager類的installExistingPackage(String)方法
(一)、installPackageWithVerification(Uri,PackageInstallObserver, int, String,Uri, ManifestDigest,ContainerEncryptionParams)方法解析
代碼在ApplicationPackageManager.java 1370行
@Override
public void installPackageWithVerification(Uri packageURI,
PackageInstallObserver observer, int flags, String installerPackageName,
Uri verificationURI, ManifestDigest manifestDigest,
ContainerEncryptionParams encryptionParams) {
final VerificationParams verificationParams = new VerificationParams(verificationURI, null,
null, VerificationParams.NO_UID, manifestDigest);
installCommon(packageURI, observer, flags, installerPackageName, verificationParams,
encryptionParams);
}
這個(gè)方法內(nèi)部很簡(jiǎn)單,就是調(diào)用了installCommon方法。那我們繼續(xù)跟蹤下
(二)、installCommon(Uri,PackageInstallObserver, int, String,int)方法解析
代碼在ApplicationPackageManager.java 1370行
private void installCommon(Uri packageURI,
PackageInstallObserver observer, int flags, String installerPackageName,
VerificationParams verificationParams, ContainerEncryptionParams encryptionParams) {
if (!"file".equals(packageURI.getScheme())) {
throw new UnsupportedOperationException("Only file:// URIs are supported");
}
if (encryptionParams != null) {
throw new UnsupportedOperationException("ContainerEncryptionParams not supported");
}
final String originPath = packageURI.getPath();
try {
mPM.installPackage(originPath, observer.getBinder(), flags, installerPackageName,
verificationParams, null);
} catch (RemoteException ignored) {
}
}
這個(gè)方法內(nèi)部做了兩次判斷,然后調(diào)用的mPM的installPackage方法。我們知道IPackageManager僅僅是AIDL的Binder通道,其實(shí)真正的調(diào)用方是PackageManagerService的installPackage(String, IPackageInstallObserver2, int, String, VerificationParams,String)方法
二、PackageManagerService中相關(guān)方法的跟蹤
(一) installPackage(String, IPackageInstallObserver2, int, String,VerificationParams,String)方法解析
代碼在PackageManagerService.java 9513行
@Override
public void installPackage(String originPath, IPackageInstallObserver2 observer,
int installFlags, String installerPackageName, VerificationParams verificationParams,
String packageAbiOverride) {
installPackageAsUser(originPath, observer, installFlags, installerPackageName,
verificationParams, packageAbiOverride, UserHandle.getCallingUserId());
}
看下這6個(gè)參數(shù):
- originPath:安裝包的位置,它必須是File類型在content的URI類型。這里傳遞過(guò)來(lái)的是toString的一個(gè)字符串。
- observer:是IPackageInstallObserver2類型的回調(diào)。通知調(diào)用者安裝完成
- installFlags:它的值可能是如下的值中的一個(gè)
- INSTALL_FORWARD_LOCK:表示安裝過(guò)程中是否鎖定
- INSTALL_REPLACE_EXISTING:表示是否替換包
- INSTALL_ALLOW_TEST:表示是否是測(cè)安裝包
比如在AndoridManifestL里面配置了"android:testOnly",則這里的標(biāo)志就是INSTALL_ALLOW_TEST- installerPackageName:安裝包的包名
- verificationParams:代表驗(yàn)證參數(shù)用于驗(yàn)證包安裝
- packageAbiOverride:一般傳null
我們看到PackageManagerService的installPackage方法什么都沒(méi)做,直接調(diào)用了PackageManagerService的installPackageAsUser方法
(二) installPackageAsUser(String, IPackageInstallObserver2, int, String, VerificationParams,String, int)方法解析
代碼在PackageManagerService.java 9521行
@Override
public void installPackageAsUser(String originPath, IPackageInstallObserver2 observer,
int installFlags, String installerPackageName, VerificationParams verificationParams,
String packageAbiOverride, int userId) {
// 第一步
// 檢查是否具有install權(quán)限
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES, null);
final int callingUid = Binder.getCallingUid();
enforceCrossUserPermission(callingUid, userId, true, true, "installPackageAsUser");
// 判斷當(dāng)前用戶是否被Restricted,回調(diào)onPackageInstalled方法,安裝失敗
if (isUserRestricted(userId, UserManager.DISALLOW_INSTALL_APPS)) {
try {
if (observer != null) {
observer.onPackageInstalled("", INSTALL_FAILED_USER_RESTRICTED, null, null);
}
} catch (RemoteException re) {
}
return;
}
// 第二步
// 判斷是安裝來(lái)源是 ADB、shell和all_user
if ((callingUid == Process.SHELL_UID) || (callingUid == Process.ROOT_UID)) {
// 如果發(fā)起端進(jìn)程是shell或者root,則添加flag:INSTALL_FROM_ADB
installFlags |= PackageManager.INSTALL_FROM_ADB;
} else {
// Caller holds INSTALL_PACKAGES permission, so we're less strict
// about installerPackageName.
// 如果不是則從flags中去掉INSTALL_FROM_ADB和INSTALL_ALL_USERS
installFlags &= ~PackageManager.INSTALL_FROM_ADB;
installFlags &= ~PackageManager.INSTALL_ALL_USERS;
}
UserHandle user;
if ((installFlags & PackageManager.INSTALL_ALL_USERS) != 0) {
user = UserHandle.ALL;
} else {
user = new UserHandle(userId);
}
// Only system components can circumvent runtime permissions when installing.
// Android 6.0 時(shí),當(dāng)權(quán)限屬于運(yùn)行時(shí)權(quán)限時(shí),需要彈出框,讓用戶授權(quán),對(duì)于system app,應(yīng)該取消彈框授權(quán),而是直接授權(quán)
if ((installFlags & PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS) != 0
&& mContext.checkCallingOrSelfPermission(Manifest.permission
.INSTALL_GRANT_RUNTIME_PERMISSIONS) == PackageManager.PERMISSION_DENIED) {
throw new SecurityException("You need the "
+ "android.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS permission "
+ "to use the PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS flag");
}
verificationParams.setInstallerUid(callingUid);
final File originFile = new File(originPath);
final OriginInfo origin = OriginInfo.fromUntrustedFile(originFile);
// 第三步
// 發(fā)送"INIT_COPY"消息,構(gòu)造InstallParams參數(shù)
final Message msg = mHandler.obtainMessage(INIT_COPY);
msg.obj = new InstallParams(origin, null, observer, installFlags, installerPackageName,
null, verificationParams, user, packageAbiOverride, null);
mHandler.sendMessage(msg);
}
這里面涉及到一個(gè)mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES, null);關(guān)于這個(gè)方法的詳解請(qǐng)參考APK安裝流程詳解14——PMS中的新安裝流程上(拷貝)補(bǔ)充中的 "一、在PackageManagerService的installPackageAsUser方法里面的代碼" 部分
就像這個(gè)方法的名字叫"installPackageAsUser"一樣,這段代碼主要是對(duì)用戶是否有權(quán)限安裝進(jìn)行檢查,以及安裝app是 僅僅給當(dāng)前用戶安裝,還是給所有用戶安裝。通過(guò)上面代碼可以看出,當(dāng)安裝進(jìn)程是shell或者root時(shí),否則大多數(shù)情況下,僅僅安裝給當(dāng)前用戶。
這里主要是對(duì)當(dāng)前用戶是否有權(quán)限安裝app進(jìn)行檢查,以及安裝的app是僅僅為當(dāng)前用戶安裝,還是給所有的用戶安裝。從以上代碼可以得出,當(dāng)安裝進(jìn)程是shell或者root時(shí),flags中又包含了INSTALL_ALL_USERS時(shí),才會(huì)給所有用戶安裝,否則大多數(shù)情況下,僅僅安裝給當(dāng)前的用戶。當(dāng)我們使用pm命令安裝的時(shí)候,可以選擇安裝給哪個(gè)用戶,也可以是全部用戶,就是這個(gè)原因。
該函數(shù)主要做了以下操作:
- 第一步:獲取權(quán)限,如果被拒絕,則退出執(zhí)行
- 第二步:設(shè)置installFlags參數(shù),即判斷安裝來(lái)源
- 第三步:發(fā)送了一個(gè)what值為INIT_COPY的message
PS:這里注意這個(gè)msg的obj變量對(duì)應(yīng)的InstallParams對(duì)象,這個(gè)InstallParams對(duì)象后面會(huì)多次用到;在構(gòu)造InstallParams的時(shí)候注意他的兩個(gè)參數(shù)為null
該方法的主要流程如下:
PackageHandler來(lái)執(zhí)行的,PackageHandler繼續(xù)字Handler,那我們來(lái)具體看看執(zhí)行代碼:
(三) PackageHandler的HandlerMesaage方法中what值為INIT_COPY的情況:
因?yàn)镻ackageHandler繼承Handler,所以我們來(lái)看下PackageHandler的HandlerMessage方法
代碼在PackageManagerService.java 1131行
public void handleMessage(Message msg) {
try {
doHandleMessage(msg);
} finally {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
}
}
前面的文章我們提及了PackageHandler是PackageServiceManager的內(nèi)部類,并且繼承Handler,所以我們直接看PackageHandler的HandlerMesaage方法,我們看到PackageHandler的HandlerMesaage方法其實(shí)是調(diào)用doHandleMessage(Message),然后在finally代碼塊里面設(shè)置了線程的優(yōu)先級(jí)為后臺(tái)線程。
那我們就來(lái)看下PackageHandler的doHandleMessage(Message)方法
1、PackageHandler的doHandleMessage方法
因?yàn)槲覀冎繫essage的what值是INIT_COPY,所以為了節(jié)省空間,我們就不粘貼其他的case了。
代碼在PackageManagerService.java 1139行
void doHandleMessage(Message msg) {
switch (msg.what) {
case INIT_COPY: {
// 第一步
// 取出InstallParams
HandlerParams params = (HandlerParams) msg.obj;
// idx為當(dāng)前需要安裝的APK個(gè)數(shù),mPendingInstalls里面保存所有需要安裝的APK解析出來(lái)的HandlerParams參數(shù)
int idx = mPendingInstalls.size();
if (DEBUG_INSTALL) Slog.i(TAG, "init_copy idx=" + idx + ": " + params);
// If a bind was already initiated we dont really
// need to do anything. The pending install
// will be processed later on.
// 第二步 分兩種情況
// 判斷綁定的連接是否已經(jīng)存在,如果已經(jīng)綁定了,則mBound為true。如果是第一次調(diào)用mBound為false。
if (!mBound) {
// If this is the only one pending we might
// have to bind to the service again.
// 第三步
// 連接安裝Service
if (!connectToService()) {
Slog.e(TAG, "Failed to bind to media container service");
params.serviceError();
return;
} else {
// Once we bind to the service, the first
// pending request will be processed.
// 如果成功綁定Service后,將新的安裝請(qǐng)求放入到mPendingIntalls中,等待處理
mPendingInstalls.add(idx, params);
}
} else {
// 如果之前已經(jīng)綁定過(guò)服務(wù),同樣將新的請(qǐng)求到mPendingIntalls中,等待處理
mPendingInstalls.add(idx, params);
// Already bound to the service. Just make
// sure we trigger off processing the first request.
if (idx == 0) {
// 如果是第一個(gè)安裝請(qǐng)求,則直接發(fā)送事件MCS_BOUND觸發(fā)處理流程
mHandler.sendEmptyMessage(MCS_BOUND);
}
}
break;
}
...
}
我將這個(gè)方法分為三個(gè)部分:
- 第一步,取出參數(shù)params,這個(gè)params就是之前傳入的InstallParams
- 第二步,獲取等待安裝隊(duì)列的個(gè)數(shù),并根據(jù)mBound的值進(jìn)行不同的處理。mBound為true,表示已經(jīng)綁定,mBound為false表示未綁定。第一次調(diào)用則mBound為默認(rèn)值為false。
- 第三步,如果是第一次調(diào)用,則調(diào)用connectToService()方法,如果不是第一次調(diào)用,且已經(jīng)綁定則將params添加到mPendingInstalls(等待安裝隊(duì)列)的最后一個(gè)位置。如果是第一個(gè)安裝請(qǐng)求,則發(fā)送MCS_BOUND事件觸發(fā)接下來(lái)的流程
這個(gè)方法整體流程如下圖:
如果是第一次走這個(gè)方法,則方法里面主要有兩個(gè)流程
- 調(diào)用connectToService()方法
- 然后調(diào)用mPendingInstalls.add(idx, params);
如果不是第一次走這個(gè)方法,則方法里面的主要流程
- 先調(diào)用mPendingInstalls.add(idx, params);
- 發(fā)送一個(gè)what值是MCS_BOUND 的Message
我們假設(shè)第一次,則先調(diào)用connectToService()方法
(四) PackageHandler的connectToService方法解析
代碼在PackageManagerService.java 1104行
private boolean connectToService() {
if (DEBUG_SD_INSTALL) Log.i(TAG, "Trying to bind to" +
" DefaultContainerService");
Intent service = new Intent().setComponent(DEFAULT_CONTAINER_COMPONENT);
Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
if (mContext.bindServiceAsUser(service, mDefContainerConn,
Context.BIND_AUTO_CREATE, UserHandle.OWNER)) {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
mBound = true;
return true;
}
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
return false;
}
這里可以看到bind到了一個(gè)service,這個(gè)service的ComponentName是"DEFAULT_CONTAINER_COMPONENT"這個(gè)常量,那我們就來(lái)看下這個(gè)ComponentName
代碼在PackageManagerService.java 377行
static final String DEFAULT_CONTAINER_PACKAGE = "com.android.defcontainer";
static final ComponentName DEFAULT_CONTAINER_COMPONENT = new ComponentName(
DEFAULT_CONTAINER_PACKAGE,
"com.android.defcontainer.DefaultContainerService");
所以我們知道bind的service是DefaultContainerService。綁定DefaultContainerService之后,設(shè)定進(jìn)程的優(yōu)先級(jí)為T(mén)HREAD_PRIORITY_DEFAULT。然后等bindServiceAsUser這個(gè)方法執(zhí)行完則又把線程的優(yōu)先級(jí)設(shè)為T(mén)HREAD_PRIORITY_BACKGROUND。
這里面涉及到一個(gè)變量mDefContainerConn
1、mDefContainerConn變量
要想研究mDefContainerConn,我們先來(lái)看下它的類型
代碼在PackageManagerService.java 921行
final private DefaultContainerConnection mDefContainerConn =
new DefaultContainerConnection();
我們知道了mDefContainerConn的類型是DefaultContainerConnection,那我們來(lái)看下DefaultContainerConnection這個(gè)類。
代碼在PackageManagerService.java 923行
class DefaultContainerConnection implements ServiceConnection {
public void onServiceConnected(ComponentName name, IBinder service) {
if (DEBUG_SD_INSTALL) Log.i(TAG, "onServiceConnected");
IMediaContainerService imcs =
IMediaContainerService.Stub.asInterface(service);
mHandler.sendMessage(mHandler.obtainMessage(MCS_BOUND, imcs));
}
public void onServiceDisconnected(ComponentName name) {
if (DEBUG_SD_INSTALL) Log.i(TAG, "onServiceDisconnected");
}
}
通過(guò)上面代碼我們知道DefaultContainerConnection實(shí)現(xiàn)了ServiceConnection。所以在連接成功的時(shí)候會(huì)調(diào)用onServiceConnected方法,我們看到當(dāng)連接成功的時(shí)候會(huì)向mHandler發(fā)送一個(gè)Message去,這個(gè)Message的what值為MCS_BOUND。
2、mDefContainerConn變量與DefaultContainerService
上文我們提及到mContext.bindServiceAsUser(service, mDefContainerConn,Context.BIND_AUTO_CREATE, UserHandle.OWNER)方法,其實(shí)就是"綁定"DefaultContainerService。我們知道bind一個(gè)Service,其中負(fù)責(zé)通信的ServiceConnection,而本方法中負(fù)責(zé)通信的就是mDefContainerConn。所以一旦綁定成功會(huì)執(zhí)行mDefContainerConn的onServiceConnected方法。而現(xiàn)實(shí)是當(dāng)綁定成功后在onServiceConnected中將一個(gè)IBinder轉(zhuǎn)換成了一個(gè)IMediaContainerService。這個(gè)就是onServiceConnected回調(diào)函數(shù)中根據(jù)參數(shù)傳進(jìn)來(lái)的IMediaContainerService.Stub的對(duì)象引用創(chuàng)建的一個(gè)遠(yuǎn)程代理對(duì)象,后面PacakgeManagerServic通過(guò)該代理對(duì)象訪問(wèn)DefaultContainerService服務(wù)
關(guān)于DefaultContainerService這里我就不詳細(xì)講解了,我會(huì)在下一篇文章APK安裝流程詳解14——PMS中的新安裝流程上(拷貝)補(bǔ)充中的二、DefaultContainerService詳解,進(jìn)行講解
關(guān)于mContext.bindServiceAsUser(service, mDefContainerConn,
Context.BIND_AUTO_CREATE, UserHandle.OWNER)為什么等于bindService這里也不詳細(xì)講解了,我們會(huì)在后面的文章APK安裝流程詳解14——PMS中的新安裝流程上(拷貝)補(bǔ)充中的 四、為什么說(shuō)mContext.bindServiceAsUser等于mContext.bindService 中詳細(xì)講解
(五) PackageHandler的HandlerMesaage方法中what值為MCS_BOUND的情況:
void doHandleMessage(Message msg) {
switch (msg.what) {
...
case MCS_BOUND: {
if (DEBUG_INSTALL) Slog.i(TAG, "mcs_bound");
// 第一步
if (msg.obj != null) {
mContainerService = (IMediaContainerService) msg.obj;
}
// 第二步
// 如果綁定DefaultContainerService服務(wù)失敗,則不能安裝程序
if (mContainerService == null) {
// 第二步的情況A
if (!mBound) {
// Something seriously wrong since we are not bound and we are not
// waiting for connection. Bail out.
Slog.e(TAG, "Cannot bind to media container service");
for (HandlerParams params : mPendingInstalls) {
// Indicate service bind error
params.serviceError();
}
mPendingInstalls.clear();
} else {
Slog.w(TAG, "Waiting to connect to media container service");
}
// 如果安裝請(qǐng)求隊(duì)列不為空
} else if (mPendingInstalls.size() > 0) {
// 第二步的情況B
HandlerParams params = mPendingInstalls.get(0);
if (params != null) {
// 調(diào)動(dòng)startCopy函數(shù)處理安裝請(qǐng)求
if (params.startCopy()) {
// We are done... look for more work or to
// go idle.
if (DEBUG_SD_INSTALL) Log.i(TAG,
"Checking for more work or unbind...");
// Delete pending install
if (mPendingInstalls.size() > 0) {
// 刪除請(qǐng)求安裝隊(duì)列的頭元素,即下標(biāo)為0的元素
mPendingInstalls.remove(0);
}
// 第二步的情況B下的分支甲
if (mPendingInstalls.size() == 0) {
// 如果安裝請(qǐng)求都處理完了
if (mBound) {
if (DEBUG_SD_INSTALL) Log.i(TAG,
"Posting delayed MCS_UNBIND");
// 通過(guò)發(fā)送MCS_UNBIND消息處理斷開(kāi)綁定請(qǐng)求
removeMessages(MCS_UNBIND);
Message ubmsg = obtainMessage(MCS_UNBIND);
// Unbind after a little delay, to avoid
// continual thrashing.
sendMessageDelayed(ubmsg, 10000);
}
} else {
// 第二步的情況B下的分支乙
// There are more pending requests in queue.
// Just post MCS_BOUND message to trigger processing
// of next pending install.
// 如果還有未處理的請(qǐng)求,則繼續(xù)發(fā)送MCS_BOUND消息
if (DEBUG_SD_INSTALL) Log.i(TAG,
"Posting MCS_BOUND for next work");
mHandler.sendEmptyMessage(MCS_BOUND);
}
}
}
} else {
// Should never happen ideally.
Slog.w(TAG, "Empty queue");
}
break;
}
}
我將上面整體流程分為兩個(gè)步驟
- 第一步,獲取mContainerService對(duì)象
- 第二步,根據(jù)上面的mContainerService是否為空,進(jìn)入下面兩個(gè)分支
- 情況A:如果mContainerService為null,則綁定DefaultContainerService失敗,不能安裝,則調(diào)用HandlerParams的serviceError方法,并清空mPendingInstalls列表
- 情況B:如果mContainerService不為null。則獲取安裝等待隊(duì)列mPendingInstalls的第一個(gè)元素,其實(shí)也就是我們最開(kāi)始添加進(jìn)去的InstallParams對(duì)象。并執(zhí)行startCopy()方法,執(zhí)行startCopy()方法后,刪除我們我們獲取的mPendingInstalls第一個(gè)元素。這時(shí)候又分甲乙兩種情況,甲情況下是沒(méi)有元素了,即mPendingInstalls.size=0;乙情況下還有元素,那我們就依次來(lái)看下
- 分支甲:mPendingInstalls為空,且當(dāng)前的綁定狀態(tài)還是"綁定"(mBound為true),則發(fā)送一個(gè)what為MCS_UNBIND的Message消息解除綁定
- 分支乙:mPendingInstalls不為空,繼續(xù)發(fā)送MCS_BOUND消息,繼續(xù)處理下一個(gè),只到隊(duì)列為空
上面這個(gè)方法涉及到一個(gè)核心方法即startCopy()來(lái)處理安裝請(qǐng)求,通過(guò)方法名叫"startCopy"我們猜測(cè)是"開(kāi)始拷貝"的意思,那我們就一起來(lái)看下
(六) HandlerParams的startCopy方法
代碼在PackageManagerService.java 10253行
final boolean startCopy() {
boolean res;
// 第一步
try {
if (DEBUG_INSTALL) Slog.i(TAG, "startCopy " + mUser + ": " + this);
//MAX_RETIRES目前為4,表示嘗試4次安裝,如果還不成功,則認(rèn)為安裝失敗
if (++mRetries > MAX_RETRIES) {
Slog.w(TAG, "Failed to invoke remote methods on default container service. Giving up");
mHandler.sendEmptyMessage(MCS_GIVE_UP);
handleServiceError();
return false;
} else {
// 調(diào)用handleStartCopy抽象方法
handleStartCopy();
res = true;
}
} catch (RemoteException e) {
if (DEBUG_INSTALL) Slog.i(TAG, "Posting install MCS_RECONNECT");
mHandler.sendEmptyMessage(MCS_RECONNECT);
res = false;
}
// 第二步
// 調(diào)用handleReturnCode抽象方法,這個(gè)方法會(huì)在handleStartCopy執(zhí)行完拷貝相關(guān)行為之后,根據(jù)handleStartCopy做進(jìn)一步的處理,主要返回狀態(tài)碼
handleReturnCode();
return res;
}
這里會(huì)涉及到一個(gè)HandlerParams和InstallParams的關(guān)系請(qǐng)參考APK安裝流程詳解14——PMS中的新安裝流程上(拷貝)補(bǔ)充中的五、HandlerParams與InstallParams簡(jiǎn)介
我將上面方法內(nèi)部劃分為兩個(gè)部分
- 第一部分,即try-catch塊內(nèi)部,判斷是錯(cuò)次數(shù),是否大于四次,這里有兩種情況:
- 如果超過(guò)4次,即mRetries>=4則,表示已經(jīng)嘗試了4次(4次是極限),則發(fā)送一個(gè)what值為MCS_GIVE_UP的Message放棄本次安裝。
- 如果沒(méi)超過(guò)4次, 則調(diào)用handleStartCopy()方法,如果在這個(gè)方法中出現(xiàn)異常。其中handleStartCopy()為真正的核心方法。如果在handleStartCopy()方法調(diào)用的時(shí)候產(chǎn)生了異常則發(fā)送一個(gè)what為MCS_RECONNECT的Message
- 上面完成之后,調(diào)用handleReturnCode()
這個(gè)方法涉及了到兩個(gè)Message的what值的處理邏輯,這兩個(gè)what值分別是MCS_GIVE_UP和MCS_RECONNECT,按我們就來(lái)一起來(lái)看下:
MCS_RECONNECT代碼在PackageManagerService.java 1226行
MCS_GIVE_UP代碼在PackageManagerService.java 1262行
為了方便我直接把他們合在一起了。
void doHandleMessage(Message msg) {
switch (msg.what) {
...
case MCS_RECONNECT: {
if (DEBUG_INSTALL) Slog.i(TAG, "mcs_reconnect");
if (mPendingInstalls.size() > 0) {
if (mBound) {
disconnectService();
}
if (!connectToService()) {
Slog.e(TAG, "Failed to bind to media container service");
for (HandlerParams params : mPendingInstalls) {
// Indicate service bind error
params.serviceError();
Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "queueInstall",
System.identityHashCode(params));
}
mPendingInstalls.clear();
}
}
break;
}
case MCS_GIVE_UP: {
if (DEBUG_INSTALL) Slog.i(TAG, "mcs_giveup too many retries");
HandlerParams params = mPendingInstalls.remove(0);
Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "queueInstall",
System.identityHashCode(params));
break;
}
...
}
}
- MCS_RECONNECT:判斷安裝請(qǐng)求隊(duì)列mPendingInstalls是否還有元素,如果有元素先斷開(kāi)綁定,則再次重新調(diào)用connectToService方法,我們知道connectToService()內(nèi)部會(huì)再次執(zhí)行綁定DefaultContainerService,而在綁定成功后會(huì)再次發(fā)送一個(gè)what值為MCS_BOUND的Message,從而又回到了startCopy里面。
- MCS_GIVE_UP:直接刪除了安裝請(qǐng)求隊(duì)列mPendingInstalls里面下標(biāo)為0的元素。
通過(guò)上文我們知道這個(gè)的HandlerParams的具體實(shí)現(xiàn)類是InstallParams,即handleStartCopy方法的具體實(shí)現(xiàn)是在InstallParams的handleStartCopy里面。
綜上所述:
startCopy()方法調(diào)用其子類的InstallParams的handleStartCopy()來(lái)完成拷貝工作的,考慮到安裝過(guò)程的不去誒的那個(gè)性,startCopy主要工作是進(jìn)行錯(cuò)誤處理,當(dāng)捕獲到handleStartCopy跑出的異常時(shí),startCopy將發(fā)送MCS_RECONNECT,在MCS_RECONNECT消息處理中,將會(huì)重新綁定
DefaultContainerService,如果綁定成功,那么安裝過(guò)程將會(huì)重新開(kāi)始。startCopy也就將會(huì)再次調(diào)用,重試的次數(shù)記錄在mRetries中,當(dāng)累計(jì)重試超過(guò)4次時(shí),安裝將失敗,如果安裝失敗,startCopy將會(huì)調(diào)用handleReturnCode()方法來(lái)處理。
下面我們就來(lái)看下handleStartCopy的具體實(shí)現(xiàn)
(七) InstallParams的handleStartCopy方法
代碼在PackageManagerService.java 10307行
/*
* Invoke remote method to get package information and install
* location values. Override install location based on default
* policy if needed and then create install arguments based
* on the install location.
*/
public void handleStartCopy() throws RemoteException {
int ret = PackageManager.INSTALL_SUCCEEDED;
// 第一步
// If we're already staged, we've firmly committed to an install location
// 是安裝在手機(jī)內(nèi)部存儲(chǔ)空間還是sdcard中,設(shè)置對(duì)應(yīng)標(biāo)志位
// 新安裝的情況下stage為false
if (origin.staged) {
if (origin.file != null) {
installFlags |= PackageManager.INSTALL_INTERNAL;
installFlags &= ~PackageManager.INSTALL_EXTERNAL;
} else if (origin.cid != null) {
installFlags |= PackageManager.INSTALL_EXTERNAL;
installFlags &= ~PackageManager.INSTALL_INTERNAL;
} else {
throw new IllegalStateException("Invalid stage location");
}
}
// 是否安裝在SD卡上
final boolean onSd = (installFlags & PackageManager.INSTALL_EXTERNAL) != 0;
// 是否安裝在內(nèi)部空間上
final boolean onInt = (installFlags & PackageManager.INSTALL_INTERNAL) != 0;
PackageInfoLite pkgLite = null;
// 檢查APK的安裝位置是否正確
if (onInt && onSd) {
// 如果既要安裝到SD卡上也要安裝上內(nèi)部空間,則由沖突安裝失敗
// Check if both bits are set.
Slog.w(TAG, "Conflicting flags specified for installing on both internal and external");
ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
} else {
pkgLite = mContainerService.getMinimalPackageInfo(origin.resolvedPath, installFlags,
packageAbiOverride);
/*
* If we have too little free space, try to free cache
* before giving up.
*/
// 釋放存儲(chǔ)空間
if (!origin.staged && pkgLite.recommendedInstallLocation
== PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE) {
// TODO: focus freeing disk space on the target device
final StorageManager storage = StorageManager.from(mContext);
final long lowThreshold = storage.getStorageLowBytes(
Environment.getDataDirectory());
final long sizeBytes = mContainerService.calculateInstalledSize(
origin.resolvedPath, isForwardLocked(), packageAbiOverride);
if (mInstaller.freeCache(null, sizeBytes + lowThreshold) >= 0) {
pkgLite = mContainerService.getMinimalPackageInfo(origin.resolvedPath,
installFlags, packageAbiOverride);
}
/*
* The cache free must have deleted the file we
* downloaded to install.
*
* TODO: fix the "freeCache" call to not delete
* the file we care about.
*/
if (pkgLite.recommendedInstallLocation
== PackageHelper.RECOMMEND_FAILED_INVALID_URI) {
pkgLite.recommendedInstallLocation
= PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE;
}
}
}
// 第二步
if (ret == PackageManager.INSTALL_SUCCEEDED) {
int loc = pkgLite.recommendedInstallLocation;
if (loc == PackageHelper.RECOMMEND_FAILED_INVALID_LOCATION) {
ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
} else if (loc == PackageHelper.RECOMMEND_FAILED_ALREADY_EXISTS) {
ret = PackageManager.INSTALL_FAILED_ALREADY_EXISTS;
} else if (loc == PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE) {
ret = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
} else if (loc == PackageHelper.RECOMMEND_FAILED_INVALID_APK) {
ret = PackageManager.INSTALL_FAILED_INVALID_APK;
} else if (loc == PackageHelper.RECOMMEND_FAILED_INVALID_URI) {
ret = PackageManager.INSTALL_FAILED_INVALID_URI;
} else if (loc == PackageHelper.RECOMMEND_MEDIA_UNAVAILABLE) {
ret = PackageManager.INSTALL_FAILED_MEDIA_UNAVAILABLE;
} else {
// Override with defaults if needed.
loc = installLocationPolicy(pkgLite);
if (loc == PackageHelper.RECOMMEND_FAILED_VERSION_DOWNGRADE) {
ret = PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE;
} else if (!onSd && !onInt) {
// Override install location with flags
if (loc == PackageHelper.RECOMMEND_INSTALL_EXTERNAL) {
// Set the flag to install on external media.
installFlags |= PackageManager.INSTALL_EXTERNAL;
installFlags &= ~PackageManager.INSTALL_INTERNAL;
} else {
// Make sure the flag for installing on external
// media is unset
installFlags |= PackageManager.INSTALL_INTERNAL;
installFlags &= ~PackageManager.INSTALL_EXTERNAL;
}
}
}
}
// 第三步
//createInstallArgs 用于創(chuàng)建一個(gè)安裝參數(shù)對(duì)象
final InstallArgs args = createInstallArgs(this);
mArgs = args;
// 第四步
if (ret == PackageManager.INSTALL_SUCCEEDED) {
/*
* ADB installs appear as UserHandle.USER_ALL, and can only be performed by
* UserHandle.USER_OWNER, so use the package verifier for UserHandle.USER_OWNER.
*/
int userIdentifier = getUser().getIdentifier();
if (userIdentifier == UserHandle.USER_ALL
&& ((installFlags & PackageManager.INSTALL_FROM_ADB) != 0)) {
userIdentifier = UserHandle.USER_OWNER;
}
/*
* Determine if we have any installed package verifiers. If we
* do, then we'll defer to them to verify the packages.
*/
final int requiredUid = mRequiredVerifierPackage == null ? -1
: getPackageUid(mRequiredVerifierPackage, userIdentifier);
if (!origin.existing && requiredUid != -1
&& isVerificationEnabled(userIdentifier, installFlags)) {
// 對(duì)當(dāng)前包做驗(yàn)證操作
final Intent verification = new Intent(
Intent.ACTION_PACKAGE_NEEDS_VERIFICATION);
verification.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
verification.setDataAndType(Uri.fromFile(new File(origin.resolvedPath)),
PACKAGE_MIME_TYPE);
verification.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
final List<ResolveInfo> receivers = queryIntentReceivers(verification,
PACKAGE_MIME_TYPE, PackageManager.GET_DISABLED_COMPONENTS,
0 /* TODO: Which userId? */);
if (DEBUG_VERIFY) {
Slog.d(TAG, "Found " + receivers.size() + " verifiers for intent "
+ verification.toString() + " with " + pkgLite.verifiers.length
+ " optional verifiers");
}
final int verificationId = mPendingVerificationToken++;
verification.putExtra(PackageManager.EXTRA_VERIFICATION_ID, verificationId);
verification.putExtra(PackageManager.EXTRA_VERIFICATION_INSTALLER_PACKAGE,
installerPackageName);
verification.putExtra(PackageManager.EXTRA_VERIFICATION_INSTALL_FLAGS,
installFlags);
verification.putExtra(PackageManager.EXTRA_VERIFICATION_PACKAGE_NAME,
pkgLite.packageName);
verification.putExtra(PackageManager.EXTRA_VERIFICATION_VERSION_CODE,
pkgLite.versionCode);
if (verificationParams != null) {
if (verificationParams.getVerificationURI() != null) {
verification.putExtra(PackageManager.EXTRA_VERIFICATION_URI,
verificationParams.getVerificationURI());
}
if (verificationParams.getOriginatingURI() != null) {
verification.putExtra(Intent.EXTRA_ORIGINATING_URI,
verificationParams.getOriginatingURI());
}
if (verificationParams.getReferrer() != null) {
verification.putExtra(Intent.EXTRA_REFERRER,
verificationParams.getReferrer());
}
if (verificationParams.getOriginatingUid() >= 0) {
verification.putExtra(Intent.EXTRA_ORIGINATING_UID,
verificationParams.getOriginatingUid());
}
if (verificationParams.getInstallerUid() >= 0) {
verification.putExtra(PackageManager.EXTRA_VERIFICATION_INSTALLER_UID,
verificationParams.getInstallerUid());
}
}
final PackageVerificationState verificationState = new PackageVerificationState(
requiredUid, args);
mPendingVerification.append(verificationId, verificationState);
final List<ComponentName> sufficientVerifiers = matchVerifiers(pkgLite,
receivers, verificationState);
// Apps installed for "all" users use the device owner to verify the app
UserHandle verifierUser = getUser();
if (verifierUser == UserHandle.ALL) {
verifierUser = UserHandle.OWNER;
}
/*
* If any sufficient verifiers were listed in the package
* manifest, attempt to ask them.
*/
if (sufficientVerifiers != null) {
final int N = sufficientVerifiers.size();
if (N == 0) {
Slog.i(TAG, "Additional verifiers required, but none installed.");
ret = PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE;
} else {
for (int i = 0; i < N; i++) {
final ComponentName verifierComponent = sufficientVerifiers.get(i);
final Intent sufficientIntent = new Intent(verification);
sufficientIntent.setComponent(verifierComponent);
mContext.sendBroadcastAsUser(sufficientIntent, verifierUser);
}
}
}
final ComponentName requiredVerifierComponent = matchComponentForVerifier(
mRequiredVerifierPackage, receivers);
if (ret == PackageManager.INSTALL_SUCCEEDED
&& mRequiredVerifierPackage != null) {
/*
* Send the intent to the required verification agent,
* but only start the verification timeout after the
* target BroadcastReceivers have run.
*/
verification.setComponent(requiredVerifierComponent);
mContext.sendOrderedBroadcastAsUser(verification, verifierUser,
android.Manifest.permission.PACKAGE_VERIFICATION_AGENT,
new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
final Message msg = mHandler
.obtainMessage(CHECK_PENDING_VERIFICATION);
msg.arg1 = verificationId;
mHandler.sendMessageDelayed(msg, getVerificationTimeout());
}
}, null, 0, null, null);
/*
* We don't want the copy to proceed until verification
* succeeds, so null out this field.
*/
mArgs = null;
}
} else {
// 不需要做驗(yàn)證
/*
* No package verification is enabled, so immediately start
* the remote call to initiate copy using temporary file.
*/
// 調(diào)用InstallArgs的copyApk函數(shù)
ret = args.copyApk(mContainerService, true);
}
}
mRet = ret;
}
- 第一步:判斷origin.staged的值,要判斷origin.staged的值,這里origin.staged為false,關(guān)于為什么origin.staged為false,請(qǐng)查看APK安裝流程詳解14——PMS中的新安裝流程上(拷貝)補(bǔ)充中的七、為什么新安裝的情況下 origin.staged等于false
- 第二步:設(shè)置ret參數(shù)
- 第三步:創(chuàng)建安裝參數(shù)對(duì)象args
- 第四步:這里根絕是否需要驗(yàn)證分為兩種情況
- 情況1——需要驗(yàn)證:需要構(gòu)建一個(gè)Intent對(duì)象verifaction,并設(shè)置相應(yīng)的參數(shù),然后發(fā)送一個(gè)廣播進(jìn)行包驗(yàn)證。在廣播的onReceive中,則發(fā)送了一個(gè)what值為CHECK_PENDING_VERIFICATION的message,這個(gè)message的arg1的值為verificationId,
- 情況2——不需要驗(yàn)證:直接調(diào)用InstallArgs的copyApk方法進(jìn)行包拷貝
整體流程圖下圖:
這方面里面涉及到的幾個(gè)比較難理解的地方,我就不一一解答了,我會(huì)下一篇依次解答,如下:
- 1、mContainerService.getMinimalPackageInfo(origin.resolvedPath, installFlags, packageAbiOverride)方法:
- 2、mContainerService.calculateInstalledSize(origin.resolvedPath, isForwardLocked(), packageAbiOverride)方法:
請(qǐng)參考APK安裝流程詳解14——PMS中的新安裝流程上(拷貝)補(bǔ)充中的 三、mContainerService.getMinimalPackageInfo(String.int,String)方法與calculateInstalledSize(String,boolean,String)方法的講解 里面會(huì)想詳細(xì)講解- 3、createInstallArgs(InstallParams)方法:
請(qǐng)參考APK安裝流程詳解14——PMS中的新安裝流程上(拷貝)補(bǔ)充中的九、createInstallArgs(InstallParams)方法解答- 4、isVerificationEnabled(int userId, int installFlags) 的理解:
請(qǐng)參考APK安裝流程詳解14——PMS中的新安裝流程上(拷貝)補(bǔ)充中的十、sVerificationEnabled(int userId, int installFlags) 的理解方法解答- 6、Context.sendBroadcast(Intent intent)的功能是否和Context.sendBroadcastAsUser一樣:
請(qǐng)參考APK安裝流程詳解14——PMS中的新安裝流程上(拷貝)補(bǔ)充中的十一、Context.sendBroadcast(Intent intent)的功能是和Context.sendBroadcastAsUser(Intent,UserHandle)一樣的解答
這里面會(huì)涉及兩個(gè)問(wèn)題如下:
- 1、what值為CHECK_PENDING_VERIFICATION的Message的處理內(nèi)容
- 2、InstallArgs的copyApk(IMediaContainerService, boolean)方法的具體實(shí)現(xiàn)
我在這里說(shuō)下
1、what值為CHECK_PENDING_VERIFICATION的Message的處理內(nèi)容
代碼在PackageManagerService.java 1506行
case CHECK_PENDING_VERIFICATION: {
// 獲取verificationId
final int verificationId = msg.arg1;
// mPendingVerification 是一個(gè)SparseArray,鍵值對(duì)是verificationId和其對(duì)應(yīng)的PackageVerificationState
// PackageVerificationState 可以理解為安裝包驗(yàn)證的狀態(tài)
final PackageVerificationState state = mPendingVerification.get(verificationId);
// 如果包的驗(yàn)證狀態(tài)不為空,并且驗(yàn)證沒(méi)有超時(shí)
if ((state != null) && !state.timeoutExtended()) {
final InstallArgs args = state.getInstallArgs();
final Uri originUri = Uri.fromFile(args.origin.resolvedFile);
Slog.i(TAG, "Verification timed out for " + originUri);
// 移除verificationId對(duì)應(yīng)的PackageVerificationState對(duì)象
mPendingVerification.remove(verificationId);
// 初始化ret值為 驗(yàn)證失敗導(dǎo)致安裝失敗
int ret = PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE;
// 如果默認(rèn)的包驗(yàn)證器允許繼續(xù)安裝
if (getDefaultVerificationResponse() == PackageManager.VERIFICATION_ALLOW) {
Slog.i(TAG, "Continuing with installation of " + originUri);
// 設(shè)置驗(yàn)證跟蹤類別,PackageManager.VERIFICATION_ALLOW_WITHOUT_SUFFICIENT表示:允許在沒(méi)有驗(yàn)證者的情況下進(jìn)行安裝
state.setVerifierResponse(Binder.getCallingUid(),
PackageManager.VERIFICATION_ALLOW_WITHOUT_SUFFICIENT);
//發(fā)送廣播,對(duì)應(yīng)的ACTION為ACTION_PACKAGE_VERIFIED
broadcastPackageVerified(verificationId, originUri,
PackageManager.VERIFICATION_ALLOW,
state.getInstallArgs().getUser());
try {
// 調(diào)用args的copyApk(IMediaContainerService,boolean)方法
ret = args.copyApk(mContainerService, true);
} catch (RemoteException e) {
Slog.e(TAG, "Could not contact the ContainerService");
}
} else {
// 如果默認(rèn)的包驗(yàn)證器,拒絕驗(yàn)證,則發(fā)送廣播告訴之
broadcastPackageVerified(verificationId, originUri,
PackageManager.VERIFICATION_REJECT,
state.getInstallArgs().getUser());
}
// 調(diào)用processPendingInstall方法
processPendingInstall(args, ret);
// 發(fā)送一個(gè)what值為MCS_UNBIND的Message
mHandler.sendEmptyMessage(MCS_UNBIND);
}
break;
}
我們發(fā)現(xiàn)無(wú)論是否需要驗(yàn)證,最后都是調(diào)用InstallArgs的copyApk(IMediaContainerService,boolean)方法,而且這兩個(gè)入?yún)⒍际枪潭ǖ模谝粋€(gè)參數(shù)為mContainerService,第二個(gè)參數(shù)為true。所以最后的拷貝肯定是通過(guò)copyApk來(lái)執(zhí)行的,通過(guò)上面的代碼我們知道這個(gè)InstallArgs其實(shí)是FileInstallArgs。所以我們只需要關(guān)注FileInstallArgs的copyApk(IMediaContainerService,boolean)方法即可
2、FileInstallArgs的copyApk(IMediaContainerService,boolean)方法
代碼在PackageManagerService.java 11050行
int copyApk(IMediaContainerService imcs, boolean temp) throws RemoteException {
// 上面我們已經(jīng)說(shuō)了,在新安裝的情況下origin.staged=false
if (origin.staged) {
if (DEBUG_INSTALL) Slog.d(TAG, origin.file + " already staged; skipping copy");
codeFile = origin.file;
resourceFile = origin.file;
return PackageManager.INSTALL_SUCCEEDED;
}
// 第一步
// 獲取目錄
try {
// 創(chuàng)建目錄
final File tempDir = mInstallerService.allocateStageDirLegacy(volumeUuid);
codeFile = tempDir;
resourceFile = tempDir;
} catch (IOException e) {
Slog.w(TAG, "Failed to create copy file: " + e);
return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
}
final IParcelFileDescriptorFactory target = new IParcelFileDescriptorFactory.Stub() {
@Override
public ParcelFileDescriptor open(String name, int mode) throws RemoteException {
if (!FileUtils.isValidExtFilename(name)) {
throw new IllegalArgumentException("Invalid filename: " + name);
}
try {
final File file = new File(codeFile, name);
final FileDescriptor fd = Os.open(file.getAbsolutePath(),
O_RDWR | O_CREAT, 0644);
Os.chmod(file.getAbsolutePath(), 0644);
return new ParcelFileDescriptor(fd);
} catch (ErrnoException e) {
throw new RemoteException("Failed to open: " + e.getMessage());
}
}
};
int ret = PackageManager.INSTALL_SUCCEEDED;
// 第二步
// 真正的文件拷貝
ret = imcs.copyPackage(origin.file.getAbsolutePath(), target);
if (ret != PackageManager.INSTALL_SUCCEEDED) {
Slog.e(TAG, "Failed to copy package");
return ret;
}
// 第三步
// 獲取庫(kù)的跟目錄
final File libraryRoot = new File(codeFile, LIB_DIR_NAME);
NativeLibraryHelper.Handle handle = null;
// 拷貝 Native代碼 即so文件
try {
handle = NativeLibraryHelper.Handle.create(codeFile);
ret = NativeLibraryHelper.copyNativeBinariesWithOverride(handle, libraryRoot,
abiOverride);
} catch (IOException e) {
Slog.e(TAG, "Copying native libraries failed", e);
ret = PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
} finally {
IoUtils.closeQuietly(handle);
}
return ret;
}
我將上面的內(nèi)容大體上分為三個(gè)步驟
- 第一步:創(chuàng)建目錄文件
- 第二步:進(jìn)行代碼拷貝
- 第三步:進(jìn)行Native代碼拷貝
這里重點(diǎn)說(shuō)下這行代碼ret = imcs.copyPackage(origin.file.getAbsolutePath(), target)。我們知道imc其實(shí)是一個(gè)遠(yuǎn)程的代理,實(shí)際的調(diào)用方式DefaultContainerService的成員變量mBinder。那我們就一起來(lái)看下DefaultContainerService的成員變量mBinder的copyPackage方法的執(zhí)行
3、DefaultContainerService#mBinder#copyPackage方法
代碼在DefaultContainerService.java 128行
/**
* Copy package to the target location.
*
* @param packagePath absolute path to the package to be copied. Can be
* a single monolithic APK file or a cluster directory
* containing one or more APKs.
* @return returns status code according to those in
* {@link PackageManager}
*/
@Override
public int copyPackage(String packagePath, IParcelFileDescriptorFactory target) {
// 第一步:
if (packagePath == null || target == null) {
return PackageManager.INSTALL_FAILED_INVALID_URI;
}
// 第二步:
PackageLite pkg = null;
try {
final File packageFile = new File(packagePath);
pkg = PackageParser.parsePackageLite(packageFile, 0);
// 第三步
return copyPackageInner(pkg, target);
} catch (PackageParserException | IOException | RemoteException e) {
Slog.w(TAG, "Failed to copy package at " + packagePath + ": " + e);
return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
}
}
太好了,居然有注釋,那我們就來(lái)看下注釋
將包復(fù)制到目標(biāo)位置
- 入?yún)ⅲ簆ackagePath:要復(fù)制包的絕對(duì)路徑,可以是一個(gè)APK文件,也可以是多個(gè)APK文件
- 出參:安裝的狀態(tài)碼
我將這方法內(nèi)部整體流程分為三步
- 第一步,判斷入?yún)ackagePath和target是否為空,如果為空則直接返回安裝失敗,失敗原因不正確的地址路徑
- 第二步,通過(guò)PackageParser的PackageParser方法解析出"輕量級(jí)"的安裝包內(nèi)容。
- 第三步,調(diào)用copyPackageInner(PackageLite, IParcelFileDescriptorFactory)方法
PS:在第二步和第三步中有個(gè)try-catch塊,如果其中出現(xiàn)異常,則直接返回安裝失敗,失敗原因是空間不足。
所以我們看出這個(gè)copyPackage方法內(nèi)部,其實(shí)沒(méi)有真正的實(shí)現(xiàn)包拷貝,而是類似于"預(yù)加載"的作用,先獲取一個(gè)"輕量級(jí)"的安裝包。
那我們來(lái)看下copyPackageInner方法
4、DefaultContainerService#copyPackageInner方法
代碼在DefaultContainerService.java 367行
private int copyPackageInner(PackageLite pkg, IParcelFileDescriptorFactory target)
throws IOException, RemoteException {
// 第一部分
copyFile(pkg.baseCodePath, target, "base.apk");
// 第二部分
if (!ArrayUtils.isEmpty(pkg.splitNames)) {
for (int i = 0; i < pkg.splitNames.length; i++) {
copyFile(pkg.splitCodePaths[i], target, "split_" + pkg.splitNames[i] + ".apk");
}
}
return PackageManager.INSTALL_SUCCEEDED;
}
我將這個(gè)方法的內(nèi)部執(zhí)行分為兩個(gè)部分
- 第一部分:調(diào)用copyFile,注意這里傳入兩個(gè)的參數(shù)是baseCodePath和"base.apk"
- 第二部分:如果PackageLite有APK拆分,則遍歷所有的分APK,再依次調(diào)用copyFile
我們看到這里調(diào)用了copyFile(String, IParcelFileDescriptorFactory, String)方法來(lái)進(jìn)行的代碼拷貝,那我們就來(lái)看下copyFile里面是怎么執(zhí)行的
5、DefaultContainerService#copyFile(String, IParcelFileDescriptorFactory, String)方法
代碼在DefaultContainerService.java 379行
private void copyFile(String sourcePath, IParcelFileDescriptorFactory target, String targetName)
throws IOException, RemoteException {
Slog.d(TAG, "Copying " + sourcePath + " to " + targetName);
InputStream in = null;
OutputStream out = null;
try {
// 輸入流
in = new FileInputStream(sourcePath);
// 輸出流
out = new ParcelFileDescriptor.AutoCloseOutputStream(
target.open(targetName, ParcelFileDescriptor.MODE_READ_WRITE));
// 進(jìn)行拷貝
Streams.copy(in, out);
} finally {
IoUtils.closeQuietly(out);
IoUtils.closeQuietly(in);
}
}
我們看到這個(gè)方法很簡(jiǎn)單,就是構(gòu)建輸入流,構(gòu)建輸出流,然后調(diào)用Streams的方法進(jìn)行拷貝。
這里順帶說(shuō)下Streams.java這個(gè)類,這個(gè)類是libcore/io/Streams.java。copy方法如下:
/**
* Copies all of the bytes from {@code in} to {@code out}. Neither stream is closed.
* Returns the total number of bytes transferred.
*/
public static int copy(InputStream in, OutputStream out) throws IOException {
int total = 0;
byte[] buffer = new byte[8192];
int c;
while ((c = in.read(buffer)) != -1) {
total += c;
out.write(buffer, 0, c);
}
return total;
}
通過(guò)這個(gè)方法完成了安裝包的拷貝功能
至此 PMS中的新安裝流程上(拷貝)已經(jīng)全部講解完成
上一篇文章 APK安裝流程詳解11——普通應(yīng)用安裝簡(jiǎn)介
下一篇文章 APK安裝流程詳解13——PackageManagerService中的新安裝流程下(裝載)