文章目錄
1.Package Manager簡介
包指的是Apk、jar和so文件等等,它們被加載到Android內存中,由一個包轉變成可執行的代碼,這就需要一個機制來進行包的加載、解析、管理等操作,這就是包管理機制。包管理機制由許多類一起組成,其中核心爲PackageManagerService
(PMS
),它負責對包進行管理。
PackageManager
是一個抽象類,具體實現類爲ApplicationPackageManager
,ApplicationPackageManager
中的方法會通過IPackageManager
與PMS
進行進程間通信,因此PackageManager
所提供的功能最終是由PMS
來實現的。
Package Manager
的功能主要包含以下部分:
- 獲取一個應用程序的所有信息(
ApplicationInfo
) - 獲取
Activity
、Provider
、Receiver
、Service
四大組件的信息 - 權限處理,包括對系統和應用定義的
Permission
和Permission Group
信息的增加、刪除、查詢和檢查 - 獲取包的信息,查詢包的
UID
、GID
、包名、系統默認程序等信息 - 安裝、卸載APK
APK的安裝場景主要有以下幾種:
- 通過adb命令安裝:adb 命令包括
adb push/install
- 用戶下載的Apk,通過系統安裝器
packageinstaller
安裝該Apk。
最終都交由PMS
來進行處理。
2.PackageInstaller簡介
2.1 PackageInstaller初始化
代碼參考AOSP 中 API 23
(android 6.0.1)
packageinstaller
: http://androidxref.com/6.0.1_r10/xref/packages/apps/PackageInstaller/
它的AndroidManifest.xml
顯示入口爲PackageInstallerActivity
,
<activity android:name=".PackageInstallerActivity"
android:configChanges="orientation|keyboardHidden|screenSize"
android:excludeFromRecents="true">
當我們調用PackageInstaller
來安裝應用時會跳轉到PackageInstallerActivity
,並調用PackageInstallerActivity
的onCreate
方法, 這個方法部分功能爲:
- 從
Intent
獲取相關數據 - 分別對
package協議
和file協議
的Uri進行處理,得到包信息PackageInfo
- 對不同來源Apk進行處理(已知來源直接調用
initiateInstall
安裝,未知來源彈框提示)
代碼如下:
protected void onCreate(Bundle icicle) {
...
// 1.從Intent獲取相關數據
final Intent intent = getIntent();
if (PackageInstaller.ACTION_CONFIRM_PERMISSIONS.equals(intent.getAction())) {
final int sessionId = intent.getIntExtra(PackageInstaller.EXTRA_SESSION_ID, -1);
final PackageInstaller.SessionInfo info = mInstaller.getSessionInfo(sessionId);
....
mSessionId = sessionId;
mPackageURI = Uri.fromFile(new File(info.resolvedBaseCodePath));
mOriginatingURI = null;
mReferrerURI = null;
} else {
mSessionId = -1;
mPackageURI = intent.getData();
mOriginatingURI = intent.getParcelableExtra(Intent.EXTRA_ORIGINATING_URI);
mReferrerURI = intent.getParcelableExtra(Intent.EXTRA_REFERRER);
}
...
final boolean unknownSourcesAllowedByAdmin = isUnknownSourcesAllowedByAdmin();
final boolean unknownSourcesAllowedByUser = isUnknownSourcesEnabled();
// 是否爲未知來源的APK
boolean requestFromUnknownSource = isInstallRequestFromUnknownSource(intent);
...
// 2.分別對package協議和file協議的Uri進行處理,得到包信息PackageInfo
final PackageUtil.AppSnippet as;
if ("package".equals(mPackageURI.getScheme())) { // package協議
mInstallFlowAnalytics.setFileUri(false);
mPkgInfo = mPm.getPackageInfo(mPackageURI.getSchemeSpecificPart(),
PackageManager.GET_PERMISSIONS | PackageManager.GET_UNINSTALLED_PACKAGES);
....
} else { // file協議
mInstallFlowAnalytics.setFileUri(true);
final File sourceFile = new File(mPackageURI.getPath());
PackageParser.Package parsed = PackageUtil.getPackageInfo(sourceFile);
....
mPkgInfo = PackageParser.generatePackageInfo(parsed, null,
PackageManager.GET_PERMISSIONS, 0, 0, null,
new PackageUserState());
...
}
mInstallFlowAnalytics.setPackageInfoObtained();
...
// 3.對不同來源Apk進行處理
if (!requestFromUnknownSource) { // 已知來源的Apk直接安裝
initiateInstall();
return;
}
....
final boolean isManagedProfile = mUserManager.isManagedProfile();
if (!unknownSourcesAllowedByAdmin
|| (!unknownSourcesAllowedByUser && isManagedProfile)) { // 禁止安裝,就彈出提示Dialog
showDialogInner(DLG_ADMIN_RESTRICTS_UNKNOWN_SOURCES);
mInstallFlowAnalytics.setFlowFinished(
InstallFlowAnalytics.RESULT_BLOCKED_BY_UNKNOWN_SOURCES_SETTING);
} else if (!unknownSourcesAllowedByUser) { // 彈出詢問框
// Ask user to enable setting first
showDialogInner(DLG_UNKNOWN_SOURCES);
mInstallFlowAnalytics.setFlowFinished(
InstallFlowAnalytics.RESULT_BLOCKED_BY_UNKNOWN_SOURCES_SETTING);
} else { // 允許安裝未知來源的APK
initiateInstall();
}
===>可以看到接着是調用了initiateInstall
方法, 這個方法部分功能爲:
- 得到包名
- 根據包名獲取應用程序信息
- 調用
startInstallConfirm
初始化安裝確認界面
代碼如下:
private void initiateInstall() {
String pkgName = mPkgInfo.packageName; //1.得到包名
// 檢查設備上是否已有包含此名稱的包,但它已被重命名爲其他內容
String[] oldName = mPm.canonicalToCurrentPackageNames(new String[] { pkgName });
if (oldName != null && oldName.length > 0 && oldName[0] != null) {
pkgName = oldName[0];
mPkgInfo.packageName = pkgName;
mPkgInfo.applicationInfo.packageName = pkgName;
}
// Check if package is already installed. display confirmation dialog if replacing pkg
try {
// 2.根據包名獲取應用程序信息
mAppInfo = mPm.getApplicationInfo(pkgName,
PackageManager.GET_UNINSTALLED_PACKAGES);
if ((mAppInfo.flags&ApplicationInfo.FLAG_INSTALLED) == 0) {
mAppInfo = null;
}
} catch (NameNotFoundException e) {
mAppInfo = null;
}
mInstallFlowAnalytics.setReplace(mAppInfo != null);
mInstallFlowAnalytics.setSystemApp(
(mAppInfo != null) && ((mAppInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0));
// 3. 初始化安裝確認界面
startInstallConfirm();
}
===>startInstallConfirm
初始化並顯示安裝確認界面,就是我們平常安裝APK時出現的界面,界面上有確認和取消按鈕,並列出了安裝該APK需要訪問的系統權限,這個方法部分功能爲:
- 初始化界面相關代碼
- 界面列出安裝該APK需要訪問的系統權限
- 綁定監聽安裝按鈕的事件
代碼如下:
private void startInstallConfirm() {
// 1.初始化界面相關代碼
...
// 2.列出安裝該APK需要訪問的系統權限,並顯示
AppSecurityPermissions perms = new AppSecurityPermissions(this, mPkgInfo);
final int N = perms.getPermissionCount(AppSecurityPermissions.WHICH_ALL);
if (mAppInfo != null) {
msg = (mAppInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0
? R.string.install_confirm_question_update_system
: R.string.install_confirm_question_update;
mScrollView = new CaffeinatedScrollView(this);
mScrollView.setFillViewport(true);
boolean newPermissionsFound = false;
if (!supportsRuntimePermissions) {
newPermissionsFound =
(perms.getPermissionCount(AppSecurityPermissions.WHICH_NEW) > 0);
mInstallFlowAnalytics.setNewPermissionsFound(newPermissionsFound);
if (newPermissionsFound) {
permVisible = true;
mScrollView.addView(perms.getPermissionsView(
AppSecurityPermissions.WHICH_NEW));
}
}
....
} else {
findViewById(R.id.tabscontainer).setVisibility(View.GONE);
findViewById(R.id.divider).setVisibility(View.VISIBLE);
}
....
mInstallFlowAnalytics.setPermissionsDisplayed(permVisible);
....
mInstallConfirm.setVisibility(View.VISIBLE);
mOk = (Button)findViewById(R.id.ok_button);
mCancel = (Button)findViewById(R.id.cancel_button);
mOk.setOnClickListener(this); // 3.綁定安裝按鈕事件
mCancel.setOnClickListener(this);
if (mScrollView == null) {
// There is nothing to scroll view, so the ok button is immediately
// set to install.
mOk.setText(R.string.install);
mOkCanInstall = true;
} else {
mScrollView.setFullScrollAction(new Runnable() {
@Override
public void run() {
mOk.setText(R.string.install); // 安裝按鈕
mOkCanInstall = true;
}
});
}
}
簡單總結下PackageInstaller
初始化:
- 分別對
package協議
和file協議
的Uri進行處理,得到mPkgInfo
- 對不同來源Apk進行處理(已知來源直接調用
initiateInstall
安裝,未知來源彈框提示) - 如果允許安裝未知來源或者根據
Intent
判斷得出該APK不是未知來源,就會初始化安裝確認界面
2.2 PackageInstaller安裝APK
用戶點擊安裝確認界面的mOk
按鈕,就會觸發PackageInstallerActivity
的onClick
方法,內部調用了startInstall
方法,如下所示:
public void onClick(View v) {
if (v == mOk) {
if (mOkCanInstall || mScrollView == null) {
mInstallFlowAnalytics.setInstallButtonClicked();
if (mSessionId != -1) {
mInstaller.setPermissionsResult(mSessionId, true);
mInstallFlowAnalytics.setFlowFinishedWithPackageManagerResult(
PackageManager.INSTALL_SUCCEEDED);
finish();
} else {
startInstall(); // 安裝函數
}
} else {
mScrollView.pageScroll(View.FOCUS_DOWN);
}
} else if(v == mCancel) {
...
}
}
===>startInstall
啓動了一個新的InstallAppProgress
activity, 並把一些參數傳了過去,代碼如下:
private void startInstall() {
// Start subactivity to actually install the application
Intent newIntent = new Intent();
newIntent.putExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO,
mPkgInfo.applicationInfo);
newIntent.setData(mPackageURI);
newIntent.setClass(this, InstallAppProgress.class);
// 省略傳入部分參數代碼
....
if(localLOGV) Log.i(TAG, "downloaded app uri="+mPackageURI);
startActivity(newIntent);
finish();
}
===>InstallAppProgress
類的onCreate
函數先取得傳入參數,再調用initView
方法,代碼如下:
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
// 省略傳入部分參數代碼
...
initView();
}
===>InstallAppProgress
類的initView
方法,代碼如下:
public void initView() {
PackageManager pm = getPackageManager();
try {
PackageInfo pi = pm.getPackageInfo(mAppInfo.packageName,
PackageManager.GET_UNINSTALLED_PACKAGES);
if(pi != null) {
installFlags |= PackageManager.INSTALL_REPLACE_EXISTING;
}
} catch (NameNotFoundException e) {
}
if((installFlags & PackageManager.INSTALL_REPLACE_EXISTING )!= 0) {
Log.w(TAG, "Replacing package:" + mAppInfo.packageName);
}
....
if ("package".equals(mPackageURI.getScheme())) { // package協議
try {
pm.installExistingPackage(mAppInfo.packageName);
observer.packageInstalled(mAppInfo.packageName,
PackageManager.INSTALL_SUCCEEDED);
} catch (PackageManager.NameNotFoundException e) {
observer.packageInstalled(mAppInfo.packageName,
PackageManager.INSTALL_FAILED_INVALID_APK);
}
} else {
pm.installPackageWithVerificationAndEncryption(mPackageURI, observer, installFlags,
installerPackageName, verificationParams, null);
}
}
===>精簡了一下,主要是這兩個方法:
pm.installExistingPackage
,實際上調用的就是ApplicationPackageManager
的installExistingPackage
,它是爲其他用戶安裝已安裝過的apkpm.installPackageWithVerificationAndEncryption
,實際上調用的就是ApplicationPackageManager
的installPackageWithVerificationAndEncryption
,它是安裝一個apk
這裏我們只跟蹤ApplicationPackageManager
的installPackageWithVerificationAndEncryption
, 最終內部是調用了 mPM.installPackage
, 代碼如下:
public void installPackageWithVerificationAndEncryption(Uri packageURI,
IPackageInstallObserver observer, int flags, String installerPackageName,
VerificationParams verificationParams, ContainerEncryptionParams encryptionParams) {
installCommon(packageURI, new LegacyPackageInstallObserver(observer), flags,
installerPackageName, verificationParams, encryptionParams);
}
--->installCommon方法的實現如下:
private void installCommon(Uri packageURI,
PackageInstallObserver observer, int flags, String installerPackageName,
VerificationParams verificationParams, ContainerEncryptionParams encryptionParams) {
...
final String originPath = packageURI.getPath();
try {
mPM.installPackage(originPath, observer.getBinder(), flags, installerPackageName,
verificationParams, null);
} catch (RemoteException ignored) {
}
}
===> mPM
的定義爲IPackageManager
,也就是IPackageManager.aidl
,很明顯,它最終是調到了PMS
的installPackage
方法。
IPackageManager mPM
總結下PackageInstaller
安裝APK,其實就是調到了PMS
的installPackage
方法進行安裝。
3. PMS簡介
3.1 交互簡介
從源碼來分析:
ApplicationPackageManager
是PackageManager
的實現體,內部有成員變量IPackageManager mPM
, ApplicationPackageManager
是由ContextImpl.java
初始化的,同時初始化的還有mPM
變量
public PackageManager getPackageManager() {
if (mPackageManager != null) {
return mPackageManager;
}
IPackageManager pm = ActivityThread.getPackageManager(); // ApplicationPackageManager中的mPM
if (pm != null) {
// ApplicationPackageManager初始化
return (mPackageManager = new ApplicationPackageManager(this, pm));
}
return null;
}
--->ApplicationPackageManager構造函數如下:
ApplicationPackageManager(ContextImpl context,
IPackageManager pm) {
mContext = context;
mPM = pm;
}
---> ActivityThread.getPackageManager如下:
public static IPackageManager getPackageManager() {
if (sPackageManager != null) {
//Slog.v("PackageManager", "returning cur default = " + sPackageManager);
return sPackageManager;
}
IBinder b = ServiceManager.getService("package");
//Slog.v("PackageManager", "default service binder = " + b);
sPackageManager = IPackageManager.Stub.asInterface(b);
//Slog.v("PackageManager", "default service = " + sPackageManager);
return sPackageManager;
}
可以看出 mPM
變量就是IPackageManager.Stub
轉換的代理IPackageManagerProxy
。參考 aidl自動生成類分析
public class PackageManagerService extends IPackageManager.Stub {
}
1.PackageManagerService
是 IPackageManager
在服務端的接口
2.ApplicationPackageManager
的mPM
成員或者ActivityThread.getPackageManager
,是IPackageManager
在客戶端的代理接口,
3.2 PMS拷貝APK流程
PMS
的installPackage
方法繼續查看源碼, 內部調用了installPackageAsUser
方法
public void installPackage(String originPath, IPackageInstallObserver2 observer,
int installFlags, String installerPackageName, VerificationParams verificationParams,
String packageAbiOverride) {
installPackageAsUser(originPath, observer, installFlags, installerPackageName,
verificationParams, packageAbiOverride, UserHandle.getCallingUserId());
}
===>installPackageAsUser
方法,這個方法部分功能爲:
- 檢查調用者是否有安裝apk的權限
- 由於安裝程序是一個耗時的過程,所以使用了Handler消息傳遞機制,發送
INIT_COPY
消息
代碼如下:
public void installPackageAsUser(String originPath, IPackageInstallObserver2 observer,
int installFlags, String installerPackageName, VerificationParams verificationParams,
String packageAbiOverride, int userId) {
// 1.檢查調用者是否有安裝apk的權限
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES, null);
...
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.
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);
final Message msg = mHandler.obtainMessage(INIT_COPY);
msg.obj = new InstallParams(origin, null, observer, installFlags, installerPackageName,
null, verificationParams, user, packageAbiOverride, null);
// 2.由於安裝程序是一個耗時的過程,所以使用了Handler消息傳遞機制,發送INIT_COPY消息
mHandler.sendMessage(msg);
}
---->mHandler的定義:
final PackageHandler mHandler;
處理INIT_COPY
類型的消息,這個方法部分功能爲:
connectToService
用於標識是否綁定了DefaultContainerService
,DefaultContainerService
是用於檢查和複製可移動文件的服務,這是一個比較耗時的操作,因此DefaultContainerService
沒有和PMS
運行在同一進程中,它運行在com.android.defcontainer
進程,通過IMediaContainerService
和PMS
進行IPC
通信- 發送
MCS_BOUND
類型的消息,觸發處理第一個安裝請求
代碼如下所示:
void doHandleMessage(Message msg) {
switch (msg.what) {
case INIT_COPY: {
HandlerParams params = (HandlerParams) msg.obj;
int idx = mPendingInstalls.size();
if (DEBUG_INSTALL) Slog.i(TAG, "init_copy idx=" + idx + ": " + params);
// mBound用於標識是否綁定了服務,默認值爲false
if (!mBound) {
//如果沒有綁定服務,重新綁定,connectToService方法內部如果綁定成功會將mBound置爲true
if (!connectToService()) {
Slog.e(TAG, "Failed to bind to media container service");
params.serviceError();
//綁定服務失敗則return
return;
} else {
//綁定服務成功,將請求添加到ArrayList類型的mPendingInstalls中,等待處理
mPendingInstalls.add(idx, params);
}
} else {
//已經綁定服務
mPendingInstalls.add(idx, params); // 加入待安裝List
if (idx == 0) {
mHandler.sendEmptyMessage(MCS_BOUND);// 發送MCS_BOUND
}
}
break;
處理MCS_BOUND
類型的消息, 如果待安裝mPendingInstalls
不爲空,這個方法部分功能爲:
startCopy
開始複製APK- 如果APK安裝成功,刪除本次安裝請求,
mPendingInstalls.remove(0);
- 如果沒有安裝請求了,發送
MCS_UNBIND
解綁服務的請求 - 如果還有其他的安裝請求,接着發送
MCS_BOUND
消息繼續處理剩餘的安裝請求
代碼如下所示:
case MCS_BOUND: {
if (DEBUG_INSTALL) Slog.i(TAG, "mcs_bound");
if (msg.obj != null) {
mContainerService = (IMediaContainerService) msg.obj;
}
if (mContainerService == null) {
if (!mBound) {
...
// 綁定失敗,清空安裝請求隊列
mPendingInstalls.clear();
} else {
Slog.w(TAG, "Waiting to connect to media container service");
}
} else if (mPendingInstalls.size() > 0) {// 待安裝List不爲空
HandlerParams params = mPendingInstalls.get(0);
if (params != null) {
if (params.startCopy()) { // 1.開始複製APK
// We are done... look for more work or to
// Delete pending install
//2.如果APK安裝成功,刪除本次安裝請求
if (mPendingInstalls.size() > 0) {
mPendingInstalls.remove(0);
}
if (mPendingInstalls.size() == 0) {
if (mBound) {
// 3.如果沒有安裝請求了,發送MCS_UNBIND解綁服務的請求
removeMessages(MCS_UNBIND);
Message ubmsg = obtainMessage(MCS_UNBIND);
// Unbind after a little delay, to avoid
// continual thrashing.
sendMessageDelayed(ubmsg, 10000);
}
} else {
// 4.如果還有其他的安裝請求,接着發送MCS_BOUND消息繼續處理剩餘的安裝請求
mHandler.sendEmptyMessage(MCS_BOUND);
}
}
}
} else {
// Should never happen ideally.
Slog.w(TAG, "Empty queue");
}
break;
}
由INIT_COPY
的發送消息,我們可以知道InstallParams
是HandlerParams
真正實現體,代碼如下:
final Message msg = mHandler.obtainMessage(INIT_COPY);
msg.obj = new InstallParams(origin, null, observer, installFlags, installerPackageName,
null, verificationParams, user, packageAbiOverride, null);
--->InstallParams
前面關鍵函數是HandlerParams
的startCopy
,也就是InstallParams
的startCopy
,調用startCopy
方法時會先自動加1,如果次數大於4次就放棄這個安裝請求,代碼如下:
final boolean startCopy() {
boolean res;
try {
// 1.startCopy方法嘗試的次數,超過了4次,就放棄這個安裝請求
if (++mRetries > MAX_RETRIES) {
...
return false;
} else {
handleStartCopy(); // 2.調用handleStartCopy
res = true;
}
} catch (RemoteException e) {
...
}
handleReturnCode();
return res;
}
===>handleStartCopy
方法在InstallParams
中實現,這個方法部分功能爲:
- 確定APK的安裝位置。
onSd
:安裝到SD卡,onInt
:內部存儲即Data分區 - 判斷安裝的位置
- 根據
InstallParams
創建InstallArgs
對象,InstallArgs
是一個抽象類,定義了APK的安裝邏輯,比如複製和重命名APK等,它有3個子類,都被定義在PMS
中,如下圖所示。
private InstallArgs createInstallArgs(InstallParams params) {
if (params.move != null) {
return new MoveInstallArgs(params);
} else if (installOnExternalAsec(params.installFlags) || params.isForwardLocked()) {
return new AsecInstallArgs(params);
} else {
return new FileInstallArgs(params);
}
}
FileInstallArgs
用於處理安裝到非ASEC的存儲空間的APK,也就是內部存儲空間(Data分區)
AsecInstallArgs
用於處理安裝到ASEC中(mnt/asec)即SD卡中的APK
MoveInstallArgs
用於處理已安裝APK的移動的邏輯。
對APK進行檢查後就會調用InstallArgs
的copyApk
方法進行安裝。
代碼如下:
public void handleStartCopy() throws RemoteException {
...
// 1.確定APK的安裝位置。onSd:安裝到SD卡, onInt:內部存儲即Data分區
final boolean onSd = (installFlags & PackageManager.INSTALL_EXTERNAL) != 0;
final boolean onInt = (installFlags & PackageManager.INSTALL_INTERNAL) != 0;
PackageInfoLite pkgLite = null;
if (onInt && onSd) {
// APK不能同時安裝在SD卡和Data分區
ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
} else {
// 獲取APK的少量的信息
pkgLite = mContainerService.getMinimalPackageInfo(origin.resolvedPath, installFlags,
packageAbiOverride);
...
if (ret == PackageManager.INSTALL_SUCCEEDED) {
// 2.判斷安裝的位置
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) {
...
} else {
// Override with defaults if needed.
loc = installLocationPolicy(pkgLite);
...
}
}
//3.根據InstallParams創建InstallArgs對象
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)) {
final ComponentName requiredVerifierComponent = matchComponentForVerifier(
mRequiredVerifierPackage, receivers);
if (ret == PackageManager.INSTALL_SUCCEEDED
&& mRequiredVerifierPackage != null) {
...
// 一堆檢查...
}
} else {
/*
* No package verification is enabled, so immediately start
* the remote call to initiate copy using temporary file.
*/
ret = args.copyApk(mContainerService, true);
}
}
mRet = ret;
}
===>InstallArgs
的copyApk
方法, 不同的InstallArgs
子類會有着不同的處理,這裏以FileInstallArgs
爲例,這個方法部分功能爲:
- 創建臨時文件存儲目錄,如
/data/app/vmdl123456.tmp
,其中123456
是安裝的sessionId
- 通過
IMediaContainerService
跨進程調用DefaultContainerService
的copyPackage
方法,這個方法會在DefaultContainerService
所在的進程中將APK複製到臨時存儲目錄,比如/data/app/vmdl 123456.tmp/base.apk
。
代碼如下:
int copyApk(IMediaContainerService imcs, boolean temp) throws RemoteException {
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 {
// 1.創建臨時文件存儲目錄
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;
}
...
int ret = PackageManager.INSTALL_SUCCEEDED;
// 2.通過IMediaContainerService跨進程調用DefaultContainerService的copyPackage方法
ret = imcs.copyPackage(origin.file.getAbsolutePath(), target);
if (ret != PackageManager.INSTALL_SUCCEEDED) {
Slog.e(TAG, "Failed to copy package");
return ret;
}
final File libraryRoot = new File(codeFile, LIB_DIR_NAME);
NativeLibraryHelper.Handle handle = null;
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;
}
===>DefaultContainerService
的copyPackage
,最終調用FileInputStream
寫入,代碼流程如下:
public int copyPackage(String packagePath, IParcelFileDescriptorFactory target) {
...
final File packageFile = new File(packagePath);
pkg = PackageParser.parsePackageLite(packageFile, 0);
return copyPackageInner(pkg, target);
...
}
---->
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;
}
---->
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));
Streams.copy(in, out);
} finally {
IoUtils.closeQuietly(out);
IoUtils.closeQuietly(in);
}
}
3.3 PMS安裝APK流程
我們回到APK的複製調用鏈的頭部方法:HandlerParams
的startCopy
方法,在最後會調用InstallParams
的handleReturnCode
,代碼如下所示:
void handleReturnCode() {
// If mArgs is null, then MCS couldn't be reached. When it
// reconnects, it will try again to install. At that point, this
// will succeed.
if (mArgs != null) {
processPendingInstall(mArgs, mRet);
}
}
===>processPendingInstall
,這個方法部分功能爲:
args.doPreInstall
安裝前處理,檢查APK的狀態,在安裝前確保安裝環境的可靠,如果不可靠會清除複製的APK文件installPackageLI
安裝流程args.doPostInstall
處理安裝後的收尾操作,如果安裝不成功,刪除掉安裝相關的目錄與文件
代碼如下:
private void processPendingInstall(final InstallArgs args, final int currentStatus) {
// Queue up an async operation since the package installation may take a little while.
mHandler.post(new Runnable() {
public void run() {
mHandler.removeCallbacks(this);
// Result object to be returned
PackageInstalledInfo res = new PackageInstalledInfo();
res.returnCode = currentStatus;
res.uid = -1;
res.pkg = null;
res.removedInfo = new PackageRemovedInfo();
if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
// 安裝前處理
args.doPreInstall(res.returnCode);
synchronized (mInstallLock) {
installPackageLI(args, res);// 安裝
}
// 安裝後收尾
args.doPostInstall(res.returnCode, res.uid);
}
....
});
}
===>installPackageLI
安裝流程,這個方法部分功能爲:
- 創建PackageParser解析APK
- 檢查APK是否存在,標誌位
replace
置爲true表示是替換安裝 - 如果Settings中保存有要安裝的APK的信息,說明此前安裝過該APK,則需要校驗APK的簽名信息,確保安全的進行替換
- 遍歷每個權限,對權限進行處理
- 將臨時文件重新命名,比如前面提到的
/data/app/vmdl123456.tmp/base.apk
,重命名爲/data/app/包名-1/base.apk
。這個新命名的包名會帶上一個數字後綴1,每次升級一個已有的App,這個數字會不斷的累加,參考FileInstallArgs
的getNextCodePath
- 替換安裝或安裝新的APK
- 更新應用程序所屬的用戶
代碼如下:
private void installPackageLI(InstallArgs args, PackageInstalledInfo res) {
...
PackageParser pp = new PackageParser();//1
final PackageParser.Package pkg;
try {
// 1.創建PackageParser解析APK
pkg = pp.parsePackage(tmpPackageFile, parseFlags);
} catch (PackageParserException e) {
res.setError("Failed parse during installPackageLI", e);
return;
}
...
// Get rid of all references to package scan path via parser.
pp = null;
String oldCodePath = null;
boolean systemApp = false;
synchronized (mPackages) {
// 2. 檢查APK的package是已存在,替換已存在的,也就是更新模式
if ((installFlags & PackageManager.INSTALL_REPLACE_EXISTING) != 0) {
String oldName = mSettings.mRenamedPackages.get(pkgName);
if (pkg.mOriginalPackages != null
&& pkg.mOriginalPackages.contains(oldName)
&& mPackages.containsKey(oldName)) {
// This package is derived from an original package,
// and this device has been updating from that original
// name. We must continue using the original name, so
// rename the new package here.
pkg.setPackageName(oldName);
pkgName = pkg.packageName;
replace = true;//設置標誌位表示是替換安裝
}
...
}
PackageSetting ps = mSettings.mPackages.get(pkgName);
//3.查看Settings中是否存有要安裝的APK的信息,如果有就獲取簽名信息
if (ps != null) {
if (DEBUG_INSTALL) Slog.d(TAG, "Existing package: " + ps);
//檢查簽名的正確性
if (shouldCheckUpgradeKeySetLP(ps, scanFlags)) {
if (!checkUpgradeKeySetLP(ps, pkg)) {
res.setError(INSTALL_FAILED_UPDATE_INCOMPATIBLE, "Package "
+ pkg.packageName + " upgrade keys do not match the "
+ "previously installed version");
return;
}
}
...
}
// Check whether the newly-scanned package wants to define an already-defined perm
// 4.遍歷每個權限,對權限進行處理
int N = pkg.permissions.size();
for (int i = N-1; i >= 0; i--) {
PackageParser.Permission perm = pkg.permissions.get(i);
BasePermission bp = mSettings.mPermissions.get(perm.info.name);
...
}
}
if (systemApp && onExternal) {
// 系統APP不能在SD卡上替換安裝
res.setError(INSTALL_FAILED_INVALID_INSTALL_LOCATION,
"Cannot install updates to system apps on sdcard");
return;
}
...
// 5.重命名臨時文件
if (!args.doRename(res.returnCode, pkg, oldCodePath)) {
res.setError(INSTALL_FAILED_INSUFFICIENT_STORAGE, "Failed rename");
return;
}
startIntentFilterVerifications(args.user.getIdentifier(), replace, pkg);
if (replace) {
//6.1 替換安裝
replacePackageLI(pkg, parseFlags, scanFlags | SCAN_REPLACING, args.user,
installerPackageName, volumeUuid, res);
} else {
// 6.2 安裝新的APK
installNewPackageLI(pkg, parseFlags, scanFlags | SCAN_DELETE_DATA_ON_FAILURES,
args.user, installerPackageName, volumeUuid, res);
}
synchronized (mPackages) {
final PackageSetting ps = mSettings.mPackages.get(pkgName);
if (ps != null) {
// 7.更新應用程序所屬的用戶
res.newUsers = ps.queryInstalledUsers(sUserManager.getUserIds(), true);
}
}
}
===>這裏我們以新安裝APK爲例,會調用PMS
的installNewPackageLIF
方法,這個方法部分功能爲:
- 掃描APK,將APK的信息存儲在
PackageParser.Package
類型的newPackage
中,一個Package
的信息包含了1個base APK以及0個或者多個split APK - 更新
Settings
信息,Settings
用於保存所有包的動態設置。 - 安裝失敗則刪除APK
代碼如下:
private void installNewPackageLI(PackageParser.Package pkg, int parseFlags, int scanFlags,
UserHandle user, String installerPackageName, String volumeUuid,
PackageInstalledInfo res) {
...
try {
//1. 掃描APK
PackageParser.Package newPackage = scanPackageLI(pkg, parseFlags, scanFlags,
System.currentTimeMillis(), user);
// 2.更新Settings信息
updateSettingsLI(newPackage, installerPackageName, volumeUuid, null, null, res, user);
// delete the partially installed application. the data directory will have to be
// restored if it was already existing
if (res.returnCode != PackageManager.INSTALL_SUCCEEDED) {
//3.安裝失敗則刪除APK
deletePackageLI(pkgName, UserHandle.ALL, false, null, null,
dataDirExists ? PackageManager.DELETE_KEEP_DATA : 0,
res.removedInfo, true);
}
} catch (PackageManagerException e) {
res.setError("Package couldn't be installed in " + pkg.codePath, e);
}
}
===>updateSettingsLI
中設置APK安裝成功,代碼如下:
private void updateSettingsLI(PackageParser.Package newPackage, String installerPackageName,
String volumeUuid, int[] allUsers, boolean[] perUserInstalled, PackageInstalledInfo res,
UserHandle user) {
String pkgName = newPackage.packageName;
synchronized (mPackages) {
mSettings.setInstallStatus(pkgName, PackageSettingBase.PKG_INSTALL_INCOMPLETE);
mSettings.writeLPr();
}
if (DEBUG_INSTALL) Slog.d(TAG, "New package installed in " + newPackage.codePath);
synchronized (mPackages) {
updatePermissionsLPw(newPackage.packageName, newPackage,
UPDATE_PERMISSIONS_REPLACE_PKG | (newPackage.permissions.size() > 0
? UPDATE_PERMISSIONS_ALL : 0));
// For system-bundled packages, we assume that installing an upgraded version
// of the package implies that the user actually wants to run that new code,
// so we enable the package.
PackageSetting ps = mSettings.mPackages.get(pkgName);
if (ps != null) {
if (isSystemApp(newPackage)) {
// NB: implicit assumption that system package upgrades apply to all users
if (DEBUG_INSTALL) {
Slog.d(TAG, "Implicitly enabling system package on upgrade: " + pkgName);
}
if (res.origUsers != null) {
for (int userHandle : res.origUsers) {
ps.setEnabled(COMPONENT_ENABLED_STATE_DEFAULT,
userHandle, installerPackageName);
}
}
// Also convey the prior install/uninstall state
if (allUsers != null && perUserInstalled != null) {
for (int i = 0; i < allUsers.length; i++) {
if (DEBUG_INSTALL) {
Slog.d(TAG, " user " + allUsers[i]
+ " => " + perUserInstalled[i]);
}
ps.setInstalled(perUserInstalled[i], allUsers[i]);
}
// these install state changes will be persisted in the
// upcoming call to mSettings.writeLPr().
}
}
// It's implied that when a user requests installation, they want the app to be
// installed and enabled.
int userId = user.getIdentifier();
if (userId != UserHandle.USER_ALL) {
ps.setInstalled(true, userId);
ps.setEnabled(COMPONENT_ENABLED_STATE_DEFAULT, userId, installerPackageName);
}
}
res.name = pkgName;
res.uid = newPackage.applicationInfo.uid;
res.pkg = newPackage;
// 安裝完成狀態
mSettings.setInstallStatus(pkgName, PackageSettingBase.PKG_INSTALL_COMPLETE);
mSettings.setInstallerPackageName(pkgName, installerPackageName);
res.returnCode = PackageManager.INSTALL_SUCCEEDED;
//to update install status
mSettings.writeLPr();
}
3.4 PMS創建流程
它和 AMS
的創建是類似的,啓動代碼位於frameworks/base/services/java/com/android/server/SystemServer.java
中,代碼如下:
// zygote的main入口
public static void main(String[] args) {
new SystemServer().run();
}
===> SystemServer().run
的部分代碼:
private void run() {
// Initialize the system context.
createSystemContext();
// Create the system service manager.
mSystemServiceManager = new SystemServiceManager(mSystemContext);
LocalServices.addService(SystemServiceManager.class, mSystemServiceManager);
// Start services.
try {
traceBeginAndSlog("StartServices");
//啓動引導服務
startBootstrapServices();//3
//啓動核心服務
startCoreServices();//4
//啓動其他服務
startOtherServices();//5
}
}
SystemServiceManager
啓動了ActivityManagerService
、PowerManagerService
、PackageManagerService
等服務。
startCoreServices
方法中則啓動了DropBoxManagerService
、BatteryService
、UsageStatsService
和WebViewUpdateServic
等服務。
startOtherServices
方法中啓動了CameraService
、AlarmManagerService
、VrManagerService
等服務。
這些服務的父類均爲SystemService
。
可以看出,官方把系統服務分爲了三種類型,分別是引導服務、核心服務和其他服務,PMS
屬於引導服務
===>創建PMS
的過程位於startBootstrapServices
中,使用了PackageManagerService.main
方法,代碼如下:
private void startBootstrapServices() {
...
// 創建PMS
Slog.i(TAG, "Package Manager");
mPackageManagerService = PackageManagerService.main(mSystemContext, installer,
mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore);
mFirstBoot = mPackageManagerService.isFirstBoot();
mPackageManager = mSystemContext.getPackageManager();
===>PackageManagerService.main
方法 ,這個方法部分功能爲:
- 創建PMS對象
- 將PMS註冊到ServiceManager中
public static PackageManagerService main(Context context, Installer installer,
boolean factoryTest, boolean onlyCore) {
// 1.創建PMS對象
PackageManagerService m = new PackageManagerService(context, installer,
factoryTest, onlyCore);
// 2.將PMS註冊到ServiceManager中
ServiceManager.addService("package", m);
return m;
}
==>接着看PMS
的構造過程,PMS的構造方法大概有600多行,分爲5個階段,每個階段會打印出相應的EventLog,EventLog用於打印Android系統的事件日誌。
- BOOT_PROGRESS_PMS_START(開始階段)
- BOOT_PROGRESS_PMS_SYSTEM_SCAN_START(掃描系統階段)
- BOOT_PROGRESS_PMS_DATA_SCAN_START(掃描Data分區階段)
- BOOT_PROGRESS_PMS_SCAN_END(掃描結束階段)
- BOOT_PROGRESS_PMS_READY(準備階段)
代碼如下:
3.4.1 開始階段
public PackageManagerService(Context context, Installer installer,
boolean factoryTest, boolean onlyCore) {
// 打印開始階段日誌
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_START,
SystemClock.uptimeMillis());
if (mSdkVersion <= 0) {
Slog.w(TAG, "**** ro.build.version.sdk not set!");
}
mContext = context;
mFactoryTest = factoryTest;
mOnlyCore = onlyCore;
mLazyDexOpt = "eng".equals(SystemProperties.get("ro.build.type"));
// 用於存儲屏幕的相關信息
mMetrics = new DisplayMetrics();
// Settings用於保存所有包的動態設置
mSettings = new Settings(mPackages);
// 在Settings中添加多個默認的sharedUserId//1
mSettings.addSharedUserLPw("android.uid.system", Process.SYSTEM_UID,
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
mSettings.addSharedUserLPw("android.uid.phone", RADIO_UID,
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
mSettings.addSharedUserLPw("android.uid.log", LOG_UID,
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
mSettings.addSharedUserLPw("android.uid.nfc", NFC_UID,
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
mSettings.addSharedUserLPw("android.uid.bluetooth", BLUETOOTH_UID,
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
mSettings.addSharedUserLPw("android.uid.shell", SHELL_UID,
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
...
mInstaller = installer;
// 創建Dex優化工具類
mPackageDexOptimizer = new PackageDexOptimizer(this);
mMoveCallbacks = new MoveCallbacks(FgThread.get().getLooper());
mOnPermissionChangeListeners = new OnPermissionChangeListeners(
FgThread.get().getLooper());
getDefaultDisplayMetrics(context, mMetrics);
// 得到全局系統配置信息
SystemConfig systemConfig = SystemConfig.getInstance();
// 獲取全局的groupId
mGlobalGids = systemConfig.getGlobalGids();
// 獲取系統權限
mSystemPermissions = systemConfig.getSystemPermissions();
mAvailableFeatures = systemConfig.getAvailableFeatures();
synchronized (mInstallLock) {
// 安裝APK時需要的鎖,保護所有對installd的訪問。
synchronized (mPackages) {//2
// 創建後臺線程ServiceThread
mHandlerThread = new ServiceThread(TAG,
Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/);
mHandlerThread.start();
// 創建PackageHandler綁定到ServiceThread的消息隊列
mHandler = new PackageHandler(mHandlerThread.getLooper());//3
// 將PackageHandler添加到Watchdog的檢測集中
Watchdog.getInstance().addThread(mHandler, WATCHDOG_TIMEOUT);//4
// 在Data分區創建一些目錄
File dataDir = Environment.getDataDirectory();//5
mAppDataDir = new File(dataDir, "data");
mAppInstallDir = new File(dataDir, "app");
mAppLib32InstallDir = new File(dataDir, "app-lib");
mAsecInternalPath = new File(dataDir, "app-asec").getPath();
mUserAppDataDir = new File(dataDir, "user");
mDrmAppPrivateInstallDir = new File(dataDir, "app-private");
// 創建多用戶管理服務
sUserManager = new UserManagerService(context, this,
mInstallLock, mPackages);
在開始階段中創建了很多PMS中的關鍵對象並賦值給PMS中的成員變量,下面簡單介紹這些成員變量。
mSettings
:用於保存所有包的動態設置。註釋1處將系統進程的sharedUserId
添加到Settings
中,sharedUserId
用於進程間共享數據,比如兩個App的之間的數據是不共享的,如果它們有了共同的sharedUserId
,就可以運行在同一個進程中共享數據。
mInstaller
:Installer
繼承自SystemService
,和PMS
、AMS
一樣是系統的服務(雖然名稱不像是服務),PMS
很多的操作都是由Installer
來完成的,比如APK的安裝和卸載。在Installer
內部,通過IInstalld
和installd
進行Binder通信,由位於nativie層的installd來完成具體的操作。
systemConfig
:用於得到全局系統配置信息。比如系統的權限就可以通過SystemConfig
來獲取。
mPackageDexOptimizer
: Dex優化的工具類。
mHandler
(PackageHandler
類型) :PackageHandler
繼承自Handler
,在註釋3處它綁定了後臺線程ServiceThread
的消息隊列。PMS
通過PackageHandler
驅動APK的複製和安裝工作,參考前面的APK拷貝。
PackageHandler
處理的消息隊列如果過於繁忙,有可能導致系統卡住, 因此在註釋4處將它添加到Watchdog
的監測集中。
Watchdog
主要有兩個用途,一個是定時檢測系統關鍵服務(AMS
和WMS
等)是否可能發生死鎖,還有一個是定時檢測線程的消息隊列是否長時間處於工作狀態(可能阻塞等待了很長時間)。如果出現上述問題,Watchdog
會將日誌保存起來,必要時還會殺掉自己所在的進程,也就是SystemServer
進程。
sUserManager
(UserManagerService
類型) :多用戶管理服務。
3.4.2 掃描系統階段
public PackageManagerService(Context context, Installer installer,
boolean factoryTest, boolean onlyCore) {
...
// 打印掃描系統階段日誌
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SYSTEM_SCAN_START,
startTime);
// 在/system中創建framework目錄
File frameworkDir = new File(Environment.getRootDirectory(), "framework");
// Gross hack for now: we know this file doesn't contain any
// code, so don't dexopt it to avoid the resulting log spew.
alreadyDexOpted.add(frameworkDir.getPath() + "/framework-res.apk");
//掃描/vendor/overlay目錄下的文件
File vendorOverlayDir = new File(VENDOR_OVERLAY_DIR);
scanDirLI(vendorOverlayDir, PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags | SCAN_TRUSTED_OVERLAY, 0);
//掃描/system/framework 目錄下的文件
scanDirLI(frameworkDir, PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR
| PackageParser.PARSE_IS_PRIVILEGED,
scanFlags | SCAN_NO_DEX, 0);
//掃描 /system/priv-app 目錄下的文件
final File privilegedAppDir = new File(Environment.getRootDirectory(), "priv-app");
scanDirLI(privilegedAppDir, PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR
| PackageParser.PARSE_IS_PRIVILEGED, scanFlags, 0);
//掃描/system/app 目錄下的文件
final File systemAppDir = new File(Environment.getRootDirectory(), "app");
scanDirLI(systemAppDir, PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);
// Collect all vendor packages.
File vendorAppDir = new File("/vendor/app");
try {
vendorAppDir = vendorAppDir.getCanonicalFile();
} catch (IOException e) {
// failed to look up canonical path, continue with original one
}
//掃描 /vendor/app 目錄下的文件
scanDirLI(vendorAppDir, PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);
//掃描/oem/app 目錄下的文件
final File oemAppDir = new File(Environment.getOemDirectory(), "app");
scanDirLI(oemAppDir, PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);
if (DEBUG_UPGRADE) Log.v(TAG, "Running installd update commands");
mInstaller.moveFiles();
//這個列表代表有可能有升級包的系統App
final List<String> possiblyDeletedUpdatedSystemApps = new ArrayList<String>();
if (!mOnlyCore) {
Iterator<PackageSetting> psit = mSettings.mPackages.values().iterator();
while (psit.hasNext()) {
PackageSetting ps = psit.next();
/*
* If this is not a system app, it can't be a
* disable system app.
*/
if ((ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) == 0) {
continue;
}
//這裏的mPackages的是PMS的成員變量,代表scanDirTracedLI方法掃描上面那些目錄得到的
final PackageParser.Package scannedPkg = mPackages.get(ps.name);
if (scannedPkg != null) {
if (mSettings.isDisabledSystemPackageLPr(ps.name)) {
//將這個系統App的PackageSetting從PMS的mPackages中移除
removePackageLI(ps, true);
//將升級包的路徑添加到mExpectingBetter列表中
mExpectingBetter.put(ps.name, ps.codePath);
}
continue;
}
if (!mSettings.isDisabledSystemPackageLPr(ps.name)) {
psit.remove();
logCriticalInfo(Log.WARN, "System package " + ps.name
+ " no longer exists; wiping its data");
removeDataDirsLI(null, ps.name);
} else {
final PackageSetting disabledPs = mSettings.getDisabledSystemPkgLPr(ps.name);
//這個系統App升級包信息在mDisabledSysPackages中,但是沒有發現這個升級包存在
if (disabledPs.codePath == null || !disabledPs.codePath.exists()) {
possiblyDeletedUpdatedSystemApps.add(ps.name);
}
}
}
}
...
/system
可以稱作爲System
分區,裏面主要存儲谷歌和其他廠商提供的Android系統相關文件和框架。Android系統架構分爲應用層、應用框架層、系統運行庫層(Native 層)、硬件抽象層(HAL層)和Linux內核層,除了Linux內核層在Boot分區,其他層的代碼都在System
分區。下面列出 System
分區的部分子目錄。
目錄 | 含義 |
---|---|
system/app | 存放系統App,包括了谷歌內置的App也有廠商或者運營商提供的App |
system/framework | 存放應用框架層的jar包 |
system/priv-app | 存放特權App |
system/lib | 存放so文件 |
system/fonts | 存放系統字體文件 |
system/media | 存放系統的各種聲音,比如鈴聲、提示音,以及系統啓動播放的動畫 |
系統掃描階段的主要工作有以下3點:
- 創建/system的子目錄,比如/system/framework、/system/priv-app和/system/app等等
- 掃描系統文件,比如/system/framework、/system/app等等目錄下的文件
- 對掃描到的系統文件做後續處理
3.4.3 掃描Data分區階段
// 打印掃描Data分區階段日誌
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_DATA_SCAN_START,
SystemClock.uptimeMillis());
// 掃描/data/app目錄下的文件
scanDirLI(mAppInstallDir, 0, scanFlags | SCAN_REQUIRE_KNOWN, 0);
// 掃描/data/app-private目錄下的文件
scanDirLI(mDrmAppPrivateInstallDir, PackageParser.PARSE_FORWARD_LOCK,
scanFlags | SCAN_REQUIRE_KNOWN, 0);
// 掃描完Data分區後,處理possiblyDeletedUpdatedSystemApps列表
for (String deletedAppName : possiblyDeletedUpdatedSystemApps) {
PackageParser.Package deletedPkg = mPackages.get(deletedAppName);
// 從mSettings.mDisabledSysPackages變量中移除去此應用
mSettings.removeDisabledSystemPackageLPw(deletedAppName);
String msg;
//1:如果這個系統App的包信息不在PMS的變量mPackages中,說明是殘留的App信息,後續會刪除它的數據。
if (deletedPkg == null) {
msg = "Updated system package " + deletedAppName
+ " no longer exists; wiping its data";
removeDataDirsLI(null, deletedAppName);
} else {
//2:如果這個系統App在mPackages中,說明是存在於Data分區,不屬於系統App,那麼移除其系統權限。
msg = "Updated system app + " + deletedAppName
+ " no longer present; removing system privileges for "
+ deletedAppName;
deletedPkg.applicationInfo.flags &= ~ApplicationInfo.FLAG_SYSTEM;
PackageSetting deletedPs = mSettings.mPackages.get(deletedAppName);
deletedPs.pkgFlags &= ~ApplicationInfo.FLAG_SYSTEM;
}
logCriticalInfo(Log.WARN, msg);
}
//遍歷mExpectingBetter列表
for (int i = 0; i < mExpectingBetter.size(); i++) {
final String packageName = mExpectingBetter.keyAt(i);
if (!mPackages.containsKey(packageName)) {
//得到系統App的升級包路徑
final File scanFile = mExpectingBetter.valueAt(i);
// 3:根據系統App所在的目錄設置掃描的解析參數
final int reparseFlags;
if (FileUtils.contains(privilegedAppDir, scanFile)) {
reparseFlags = PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR
| PackageParser.PARSE_IS_PRIVILEGED;
} else if (FileUtils.contains(systemAppDir, scanFile)) {
reparseFlags = PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR;
} else if (FileUtils.contains(vendorAppDir, scanFile)) {
reparseFlags = PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR;
} else if (FileUtils.contains(oemAppDir, scanFile)) {
reparseFlags = PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR;
} else {
Slog.e(TAG, "Ignoring unexpected fallback path " + scanFile);
continue;
}
//將packageName對應的包設置數據(PackageSetting)添加到mSettings的mPackages中
mSettings.enableSystemPackageLPw(packageName);
try {
//掃描系統App的升級包
scanPackageLI(scanFile, reparseFlags, scanFlags, 0, null);
} catch (PackageManagerException e) {
Slog.e(TAG, "Failed to parse original system package: "
+ e.getMessage());
}
}
}
}
mExpectingBetter.clear();
/data
可以稱爲Data
分區,它用來存儲所有用戶的個人數據和配置文件。下面列出Data
分區部分子目錄:
目錄 | 含義 |
---|---|
data/app | 存儲用戶自己安裝的App |
data/data | 存儲所有已安裝的App數據的目錄,每個App都有自己單獨的子目錄 |
data/app-private | App的私有存儲空間 |
data/app-lib | 存儲所有App的Jni庫 |
data/system | 存放系統配置文件 |
data/anr | 用於存儲ANR發生時系統生成的traces.txt文件 |
3.4.4 掃描結束階段
// 打印掃描結束階段日誌
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SCAN_END,
SystemClock.uptimeMillis());
Slog.i(TAG, "Time to scan packages: "
+ ((SystemClock.uptimeMillis()-startTime)/1000f)
+ " seconds");
// 如果當前平臺SDK版本和上次啓動時的SDK版本不同,重新更新APK的授權
int updateFlags = UPDATE_PERMISSIONS_ALL;
if (ver.sdkVersion != mSdkVersion) {
Slog.i(TAG, "Platform changed from " + ver.sdkVersion + " to "
+ mSdkVersion + "; regranting permissions for internal storage");
updateFlags |= UPDATE_PERMISSIONS_REPLACE_PKG | UPDATE_PERMISSIONS_REPLACE_ALL;
}
updatePermissionsLPw(null, null, StorageManager.UUID_PRIVATE_INTERNAL, updateFlags);
ver.sdkVersion = mSdkVersion;
//如果是第一次啓動,需要初始化所有用戶定義的默認首選App
if (!onlyCore && (mPromoteSystemApps || !mRestoredSettings)) {
for (UserInfo user : sUserManager.getUsers(true)) {
mSettings.applyDefaultPreferredAppsLPw(this, user.id);
applyFactoryDefaultBrowserLPw(user.id);
primeDomainVerificationsLPw(user.id);
}
}
//OTA後的第一次啓動,會清除代碼緩存目錄。
if (mIsUpgrade && !onlyCore) {
Slog.i(TAG, "Build fingerprint changed; clearing code caches");
for (int i = 0; i < mSettings.mPackages.size(); i++) {
final PackageSetting ps = mSettings.mPackages.valueAt(i);
if (Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, ps.volumeUuid)) {
deleteCodeCacheDirsLI(ps.volumeUuid, ps.name);
}
}
ver.fingerprint = Build.FINGERPRINT;
}
checkDefaultBrowser();
// clear only after permissions and other defaults have been updated
mExistingSystemPackages.clear();
mPromoteSystemApps = false;
// All the changes are done during package scanning.
ver.databaseVersion = Settings.CURRENT_DATABASE_VERSION;
// 把Settings的內容保存到packages.xml中
mSettings.writeLPr();
掃描結束結束階段主要做了以下幾件事:
- 如果當前平臺SDK版本和上次啓動時的SDK版本不同,重新更新APK的授權。
- 如果是第一次啓動,需要初始化所有用戶定義的默認首選App。
- OTA升級後的第一次啓動,會清除代碼緩存目錄。
- 把Settings的內容保存到packages.xml中,這樣此後PMS再次創建時會讀到此前保存的Settings的內容。
3.4.5 準備階段
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_READY,
SystemClock.uptimeMillis());
mRequiredVerifierPackage = getRequiredVerifierLPr();
mRequiredInstallerPackage = getRequiredInstallerLPr();
mInstallerService = new PackageInstallerService(context, this);//1
mIntentFilterVerifierComponent = getIntentFilterVerifierComponentNameLPr();
mIntentFilterVerifier = new IntentVerifierProxy(mContext,
mIntentFilterVerifierComponent);
} // synchronized (mPackages)
} // synchronized (mInstallLock)
// Now after opening every single application zip, make sure they
// are all flushed. Not really needed, but keeps things nice and
// tidy.
Runtime.getRuntime().gc();
// Expose private service for system components to use.
LocalServices.addService(PackageManagerInternal.class, new PackageManagerInternalImpl());//2
註釋1處創建PackageInstallerService
,PackageInstallerService
是用於管理安裝會話的服務,它會爲每次安裝過程分配一個SessionId
註釋2處將PackageManagerInternalImpl
(PackageManager
的本地服務)添加到LocalServices
中,LocalServices
用於存儲運行在當前的進程中的本地服務。
3.5 PMS解析APK
3.5.1 PackageParser引入
Android世界中有很多包,比如應用程序的APK,Android運行環境的JAR包(比如framework.jar)和組成Android系統的各種動態庫so等等,由於包的種類和數量繁多,就需要進行包管理,但是包管理需要在內存中進行,而這些包都是以靜態文件的形式存在的,就需要一個工具類將這些包轉換爲內存中的數據結構,這個工具就是包解析器PackageParser
。
再看下 前面的installPackageLI
代碼:
private void installPackageLI(InstallArgs args, PackageInstalledInfo res) {
...
PackageParser pp = new PackageParser();//1
final PackageParser.Package pkg;
try {
// 1.創建PackageParser解析APK
pkg = pp.parsePackage(tmpPackageFile, parseFlags);//2
} catch (PackageParserException e) {
res.setError("Failed parse during installPackageLI", e);
return;
}
可以看到安裝APK時,需要先在註釋1處創建PackageParser
,然後在註釋2處調用PackageParser
的parsePackage
方法來解析APK。
3.5.2 PackageParser解析APK
Android5.0引入了Split APK
機制,這是爲了解決65536上限以及APK安裝包越來越大等問題。Split APK
機制可以將一個APK,拆分成多個獨立APK。
在引入了Split APK
機制後,APK有兩種分類:
Single APK
:安裝文件爲一個完整的APK,即base APK
。Android稱其爲Monolithic
。Mutiple APK
:安裝文件在一個文件目錄中,其內部有多個被拆分的APK,這些APK由一個base APK
和一個或多個split APK
組成。Android稱其爲Cluster
。
===>先看 pp.parsePackage
方法的實現,結合前面的分類,可以知道,如果要解析的packageFile
是一個目錄,說明是Mutiple APK
,就需要調用parseClusterPackage
方法來解析,如果是Single APK
則調用parseMonolithicPackage
方法來解析,代碼如下:
public Package parsePackage(File packageFile, int flags) throws PackageParserException {
if (packageFile.isDirectory()) {
return parseClusterPackage(packageFile, flags);
} else {
return parseMonolithicPackage(packageFile, flags);
}
}
===>以相對複雜的parseClusterPackage
繼續分析,代碼如下:
private Package parseClusterPackage(File packageDir, int flags) throws PackageParserException {
final PackageLite lite = parseClusterPackageLite(packageDir, 0);//1
if (mOnlyCoreApps && !lite.coreApp) {//2
throw new PackageParserException(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
"Not a coreApp: " + packageDir);
}
final AssetManager assets = new AssetManager();
try {
// 把base和所有split都加入assets
loadApkIntoAssetManager(assets, lite.baseCodePath, flags);
if (!ArrayUtils.isEmpty(lite.splitCodePaths)) {
for (String path : lite.splitCodePaths) {
loadApkIntoAssetManager(assets, path, flags);
}
}
final File baseApk = new File(lite.baseCodePath);
final Package pkg = parseBaseApk(baseApk, assets, flags);//3
if (pkg == null) {
throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK,
"Failed to parse base APK: " + baseApk);
}
if (!ArrayUtils.isEmpty(lite.splitNames)) {
final int num = lite.splitNames.length;//4
pkg.splitNames = lite.splitNames;
pkg.splitCodePaths = lite.splitCodePaths;
pkg.splitRevisionCodes = lite.splitRevisionCodes;
pkg.splitFlags = new int[num];
pkg.splitPrivateFlags = new int[num];
for (int i = 0; i < num; i++) {
parseSplitApk(pkg, i, assets, flags);//5
}
}
pkg.codePath = packageDir.getAbsolutePath();
return pkg;
} finally {
IoUtils.closeQuietly(assets);
}
}
註釋1處調用parseClusterPackageLite
方法用於輕量級解析目錄文件,之所以要輕量級解析是因爲解析APK是一個複雜耗時的操作,這裏的邏輯並不需要APK所有的信息。parseClusterPackageLite
方法內部會通過parseApkLite
方法解析每個Mutiple APK
,得到每個Mutiple APK
對應的ApkLite
(輕量級APK信息),然後再將這些ApkLite
封裝爲一個PackageLite
(輕量級包信息)並返回。
註釋2處,mOnlyCoreApps
用來指示PackageParser
是否只解析“核心”應用,“核心”應用指的是AndroidManifest
中屬性coreApp
值爲true
,只解析“核心”應用是爲了創建一個極簡的啓動環境。mOnlyCoreApps
在創建PMS
時就一路傳遞過來,如果我們加密了設備,mOnlyCoreApps
值就爲true
註釋3處的parseBaseApk
方法用於解析base APK
,註釋4處獲取split APK
的數量,根據這個數量在註釋5處遍歷調用parseSplitApk
來解析每個split APK
===>先來看下parseClusterPackageLite
方法,它的調用流程如下:
private static PackageLite parseClusterPackageLite(File packageDir, int flags)
throws PackageParserException {
....
final ArrayMap<String, ApkLite> apks = new ArrayMap<>();
for (File file : files) {
if (isApkFile(file)) {
final ApkLite lite = parseApkLite(file, flags);
...
}
--->ApkLite 的實現:
public static class ApkLite {
public final String codePath;
public final String packageName;
public final String splitName;
public final int versionCode;
public final int revisionCode;
public final int installLocation;
public final VerifierInfo[] verifiers;
public final Signature[] signatures;
public final boolean coreApp;
public final boolean multiArch;
public final boolean extractNativeLibs;
===>再來看下parseBaseApk
方法,它的調用流程如下:
private Package parseBaseApk(File apkFile, AssetManager assets, int flags)
throws PackageParserException {
final String apkPath = apkFile.getAbsolutePath();
String volumeUuid = null;
if (apkPath.startsWith(MNT_EXPAND)) {
final int end = apkPath.indexOf('/', MNT_EXPAND.length());
volumeUuid = apkPath.substring(MNT_EXPAND.length(), end);//1
}
mParseError = PackageManager.INSTALL_SUCCEEDED;
mArchiveSourcePath = apkFile.getAbsolutePath();
if (DEBUG_JAR) Slog.d(TAG, "Scanning base APK: " + apkPath);
final int cookie = loadApkIntoAssetManager(assets, apkPath, flags);
Resources res = null;
XmlResourceParser parser = null;
try {
res = new Resources(assets, mMetrics, null);
assets.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
Build.VERSION.RESOURCES_SDK_INT);
parser = assets.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);
final String[] outError = new String[1];
final Package pkg = parseBaseApk(res, parser, flags, outError);//2
if (pkg == null) {
throw new PackageParserException(mParseError,
apkPath + " (at " + parser.getPositionDescription() + "): " + outError[0]);
}
pkg.volumeUuid = volumeUuid; // 3
pkg.applicationInfo.volumeUuid = volumeUuid; // 4
pkg.baseCodePath = apkPath;
pkg.mSignatures = null;
return pkg;
} catch (PackageParserException e) {
throw e;
} catch (Exception e) {
throw new PackageParserException(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
"Failed to read manifest from " + apkPath, e);
} finally {
IoUtils.closeQuietly(parser);
}
}
---->
註釋1處,如果APK的路徑以/mnt/expand/開頭,就截取該路徑獲取volumeUuid
註釋3處用於以後標識這個解析後的Package
註釋4處的用於標識該App所在的存儲卷UUID
註釋2處又調用了parseBaseApk
的重載方法,可以看出當前的parseBaseApk
方法主要是爲了獲取和設置volumeUuid
===>繼續看下parseBaseApk
重載方法,它的調用流程如下:
- 創建
Package
對象 - 從資源中提取自定義屬性集com.android.internal.R.styleable.AndroidManifest得到TypedArray ,並賦值給
pkg
- 讀取APK的
AndroidManifest
中的coreApp
的值 - 用來解析APK的
AndroidManifest
中的各個標籤,比如application
、permission
、uses-sdk
、feature-group
等
解析application
標籤的方法爲parseBaseApplication
,有近500行代碼,而Application
只是AndroidManifest
衆多標籤的一個,這讓我們更加理解了爲什麼此前解析APK時要使用輕量級解析了,parseBaseApk
方法主要的解析結構可以理解爲以下簡圖。
代碼如下:
private Package parseBaseApk(Resources res, XmlResourceParser parser, int flags,
String[] outError) throws XmlPullParserException, IOException {
...
// 1.創建Package對象
final Package pkg = new Package(pkgName);
boolean foundApp = false;
// 2.從資源中提取自定義屬性集com.android.internal.R.styleable.AndroidManifest得到TypedArray
TypedArray sa = res.obtainAttributes(attrs,
com.android.internal.R.styleable.AndroidManifest);
pkg.mVersionCode = pkg.applicationInfo.versionCode = sa.getInteger(
com.android.internal.R.styleable.AndroidManifest_versionCode, 0);
pkg.baseRevisionCode = sa.getInteger(
com.android.internal.R.styleable.AndroidManifest_revisionCode, 0);
...
// 3.讀取APK的AndroidManifest中的coreApp的值
pkg.coreApp = attrs.getAttributeBooleanValue(null, "coreApp", false);
//獲取資源後要回收
sa.recycle();
/* Set the global "forward lock" flag */
if ((flags & PARSE_FORWARD_LOCK) != 0) {
pkg.applicationInfo.privateFlags |= ApplicationInfo.PRIVATE_FLAG_FORWARD_LOCK;
}
/* Set the global "on SD card" flag */
if ((flags & PARSE_EXTERNAL_STORAGE) != 0) {
pkg.applicationInfo.flags |= ApplicationInfo.FLAG_EXTERNAL_STORAGE;
}
// Resource boolean are -1, so 1 means we don't know the value.
int supportsSmallScreens = 1;
int supportsNormalScreens = 1;
int supportsLargeScreens = 1;
int supportsXLargeScreens = 1;
int resizeable = 1;
int anyDensity = 1;
int outerDepth = parser.getDepth();
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
continue;
}
String tagName = parser.getName();
if (tagName.equals("application")) {
...
foundApp = true;
if (!parseBaseApplication(pkg, res, parser, attrs, flags, outError)) { // 3
return null;
}
} else if (tagName.equals("overlay")) {
pkg.mTrustedOverlay = trustedOverlay;
sa = res.obtainAttributes(attrs,
com.android.internal.R.styleable.AndroidManifestResourceOverlay);
pkg.mOverlayTarget = sa.getString(
com.android.internal.R.styleable.AndroidManifestResourceOverlay_targetPackage);
pkg.mOverlayPriority = sa.getInt(
com.android.internal.R.styleable.AndroidManifestResourceOverlay_priority,
-1);
sa.recycle();
if (pkg.mOverlayTarget == null) {
outError[0] = "<overlay> does not specify a target package";
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return null;
}
if (pkg.mOverlayPriority < 0 || pkg.mOverlayPriority > 9999) {
outError[0] = "<overlay> priority must be between 0 and 9999";
mParseError =
PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return null;
}
XmlUtils.skipCurrentTag(parser);
} else if (tagName.equals("key-sets")) {
// 4.各種解析tagname....
.....
}
if (!foundApp && pkg.instrumentation.size() == 0) {
outError[0] = "<manifest> does not contain an <application> or <instrumentation>";
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_EMPTY;
}
final int NP = PackageParser.NEW_PERMISSIONS.length;
StringBuilder implicitPerms = null;
for (int ip=0; ip<NP; ip++) {
final PackageParser.NewPermissionInfo npi
= PackageParser.NEW_PERMISSIONS[ip];
if (pkg.applicationInfo.targetSdkVersion >= npi.sdkVersion) {
break;
}
if (!pkg.requestedPermissions.contains(npi.name)) {
if (implicitPerms == null) {
implicitPerms = new StringBuilder(128);
implicitPerms.append(pkg.packageName);
implicitPerms.append(": compat added ");
} else {
implicitPerms.append(' ');
}
implicitPerms.append(npi.name);
pkg.requestedPermissions.add(npi.name);
}
}
if (implicitPerms != null) {
Slog.i(TAG, implicitPerms.toString());
}
final int NS = PackageParser.SPLIT_PERMISSIONS.length;
for (int is=0; is<NS; is++) {
final PackageParser.SplitPermissionInfo spi
= PackageParser.SPLIT_PERMISSIONS[is];
if (pkg.applicationInfo.targetSdkVersion >= spi.targetSdk
|| !pkg.requestedPermissions.contains(spi.rootPerm)) {
continue;
}
for (int in=0; in<spi.newPerms.length; in++) {
final String perm = spi.newPerms[in];
if (!pkg.requestedPermissions.contains(perm)) {
pkg.requestedPermissions.add(perm);
}
}
}
if (supportsSmallScreens < 0 || (supportsSmallScreens > 0
&& pkg.applicationInfo.targetSdkVersion
>= android.os.Build.VERSION_CODES.DONUT)) {
pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SUPPORTS_SMALL_SCREENS;
}
if (supportsNormalScreens != 0) {
pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SUPPORTS_NORMAL_SCREENS;
}
if (supportsLargeScreens < 0 || (supportsLargeScreens > 0
&& pkg.applicationInfo.targetSdkVersion
>= android.os.Build.VERSION_CODES.DONUT)) {
pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS;
}
if (supportsXLargeScreens < 0 || (supportsXLargeScreens > 0
&& pkg.applicationInfo.targetSdkVersion
>= android.os.Build.VERSION_CODES.GINGERBREAD)) {
pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SUPPORTS_XLARGE_SCREENS;
}
if (resizeable < 0 || (resizeable > 0
&& pkg.applicationInfo.targetSdkVersion
>= android.os.Build.VERSION_CODES.DONUT)) {
pkg.applicationInfo.flags |= ApplicationInfo.FLAG_RESIZEABLE_FOR_SCREENS;
}
if (anyDensity < 0 || (anyDensity > 0
&& pkg.applicationInfo.targetSdkVersion
>= android.os.Build.VERSION_CODES.DONUT)) {
pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES;
}
return pkg;
}
包被解析後,最終在內存是Package
,Package
是PackageParser
的內部類,它的部分成員變量如下所示。
/**
* Representation of a full package parsed from APK files on disk. A package
* consists of a single base APK, and zero or more split APKs.
*/
public final static class Package {
public String packageName; // 包名
/** Names of any split APKs, ordered by parsed splitName */
public String[] splitNames;
// TODO: work towards making these paths invariant
public String volumeUuid;
/**
* Path where this package was found on disk. For monolithic packages
* this is path to single base APK file; for cluster packages this is
* path to the cluster directory.
*/
public String codePath;
/** Path of base APK */
public String baseCodePath;
/** Paths of any split APKs, ordered by parsed splitName */
public String[] splitCodePaths;
/** Revision code of base APK */
public int baseRevisionCode;
/** Revision codes of any split APKs, ordered by parsed splitName */
public int[] splitRevisionCodes;
/** Flags of any split APKs; ordered by parsed splitName */
public int[] splitFlags;
/**
* Private flags of any split APKs; ordered by parsed splitName.
*
* {@hide}
*/
public int[] splitPrivateFlags;
public boolean baseHardwareAccelerated;
// For now we only support one application per package.
public final ApplicationInfo applicationInfo = new ApplicationInfo();
//4 大組件
public final ArrayList<Permission> permissions = new ArrayList<Permission>(0);
public final ArrayList<PermissionGroup> permissionGroups = new ArrayList<PermissionGroup>(0);
public final ArrayList<Activity> activities = new ArrayList<Activity>(0);
public final ArrayList<Activity> receivers = new ArrayList<Activity>(0);
public final ArrayList<Provider> providers = new ArrayList<Provider>(0);
public final ArrayList<Service> services = new ArrayList<Service>(0);
public final ArrayList<Instrumentation> instrumentation = new ArrayList<Instrumentation>(0);
public final ArrayList<String> requestedPermissions = new ArrayList<String>();
public ArrayList<String> protectedBroadcasts;
public ArrayList<String> libraryNames = null;
public ArrayList<String> usesLibraries = null;
public ArrayList<String> usesOptionalLibraries = null;
public String[] usesLibraryFiles = null;
public ArrayList<ActivityIntentInfo> preferredActivityFilters = null;
public ArrayList<String> mOriginalPackages = null;
public String mRealPackage = null;
public ArrayList<String> mAdoptPermissions = null;
// We store the application meta-data independently to avoid multiple unwanted references
public Bundle mAppMetaData = null;
// The version code declared for this package.
public int mVersionCode;
// The version name declared for this package.
public String mVersionName;
// The shared user id that this package wants to use.
public String mSharedUserId;
// The shared user label that this package wants to use.
public int mSharedUserLabel;
// Signatures that were read from the package.
public Signature[] mSignatures;
public Certificate[][] mCertificates;
// For use by package manager service for quick lookup of
// preferred up order.
public int mPreferredOrder = 0;
// For use by package manager to keep track of where it needs to do dexopt.
public final ArraySet<String> mDexOptPerformed = new ArraySet<>(4);
// For use by package manager to keep track of when a package was last used.
public long mLastPackageUsageTimeInMills;
// // User set enabled state.
// public int mSetEnabled = PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
//
// // Whether the package has been stopped.
// public boolean mSetStopped = false;
// Additional data supplied by callers.
public Object mExtras;
// Applications hardware preferences
public ArrayList<ConfigurationInfo> configPreferences = null;
// Applications requested features
public ArrayList<FeatureInfo> reqFeatures = null;
// Applications requested feature groups
public ArrayList<FeatureGroupInfo> featureGroups = null;
public int installLocation;
public boolean coreApp;
/* An app that's required for all users and cannot be uninstalled for a user */
public boolean mRequiredForAllUsers;
/* The restricted account authenticator type that is used by this application */
public String mRestrictedAccountType;
/* The required account type without which this application will not function */
public String mRequiredAccountType;
/**
* Digest suitable for comparing whether this package's manifest is the
* same as another.
*/
public ManifestDigest manifestDigest;
public String mOverlayTarget;
public int mOverlayPriority;
public boolean mTrustedOverlay;
/**
* Data used to feed the KeySetManagerService
*/
public ArraySet<PublicKey> mSigningKeys;
public ArraySet<String> mUpgradeKeySets;
public ArrayMap<String, ArraySet<PublicKey>> mKeySetMapping;
注意4大組件那裏,Activity
,Provider
, Service
都是PackageParser
的靜態內部類,例如Activity
public final static class Activity extends Component<ActivityIntentInfo> {
public final ActivityInfo info;
public Activity(final ParseComponentArgs args, final ActivityInfo _info) {
super(args, _info);
info = _info;
info.applicationInfo = args.owner.applicationInfo;
}
public void setPackageName(String packageName) {
super.setPackageName(packageName);
info.packageName = packageName;
}
public String toString() {
StringBuilder sb = new StringBuilder(128);
sb.append("Activity{");
sb.append(Integer.toHexString(System.identityHashCode(this)));
sb.append(' ');
appendComponentShortName(sb);
sb.append('}');
return sb.toString();
}
}
Package
簡圖如下:
從這個簡圖中可以發現Package
的數據結構是如何設計的:
Package
中存有許多組件,比如Acticity
、Provider
、Permission
等等,它們都繼承基類Component
。- 每個組件都包含一個
info
數據,比如Activity
類中包含了成員變量ActivityInfo
,這個ActivityInfo
纔是真正的Activity
數據。 - 四大組件的標籤內可能包含
<intent-filter>
來過濾Intent信息,因此需要IntentInfo
來保存組件的intent
信息,組件基類Component
依賴於IntentInfo
public static class Component<II extends IntentInfo> {
public final Package owner;
public final ArrayList<II> intents;
public final String className;
public Bundle metaData;
ComponentName componentName;
String componentShortName;
--->IntentInfo的代碼如下:
public static class IntentInfo extends IntentFilter {
public boolean hasDefault;
public int labelRes;
public CharSequence nonLocalizedLabel;
public int icon;
public int logo;
public int banner;
public int preferred;
}
--->IntentFilter的代碼如下:
public class IntentFilter implements Parcelable {
...
public static final String SCHEME_HTTPS = "https";
private int mPriority;
private final ArrayList<String> mActions;
private ArrayList<String> mCategories = null;
private ArrayList<String> mDataSchemes = null;
private ArrayList<PatternMatcher> mDataSchemeSpecificParts = null;
private ArrayList<AuthorityEntry> mDataAuthorities = null;
private ArrayList<PatternMatcher> mDataPaths = null;
private ArrayList<String> mDataTypes = null;
private boolean mHasPartialTypes = false;
}
IntentInfo
有三個子類ActivityIntentInfo
、ServiceIntentInfo
和ProviderIntentInfo
,不同組件依賴的IntentInfo
會有所不同,如下:
public final class Activity extends Component<ActivityIntentInfo>
public final static class Service extends Component<ServiceIntentInfo>
public final static class Provider extends Component<ProviderIntentInfo>
4.VirtualApp中PMS的實現
VirtualApp中解析後的數據都保存在PackageParser
類中,由PackageParserEx
(PackageParserEx
擴展了PackageParser
)解析,對外的數據是VPackage
類
4.1 PackageParser類簡介
VA自建的android.content.pm.PackageParse
,它其實更多的意義是佔位類,讓反射方式更優雅,它和系統的android.content.pm.PackageParse
保持一致,但實際上,系統解析得到的android.content.pm.PackageParse
,還是系統那一份。
PackageParser
類最底層數據仍是IntentInfo
,如下:
public static class IntentInfo extends IntentFilter {
public boolean hasDefault;
public int labelRes;
public CharSequence nonLocalizedLabel;
public int icon;
public int logo;
public int banner;
}
同樣有一個Component
基類,如下:
public static class Component<II extends IntentInfo> {
public Package owner;
public ArrayList<II> intents;
public String className;
public Bundle metaData;
public ComponentName getComponentName() {
return null;
}
}
用getComponentName
代替了 ComponentName componentName;
前面提到Package
中存有許多組件,比如Acticity
、Provider
、Permission
等等,它們都繼承基類Component
,另外前面提到Package
中每個組件都包含一個info
數據,info
數據纔是真正的數據,VA採用了同樣的構造方式,如下:
public final static class Activity extends Component<ActivityIntentInfo> {
public ActivityInfo info;
}
public class ActivityIntentInfo extends IntentInfo {
public Activity activity;
}
對比系統的,完全一致:
public final static class Activity extends Component<ActivityIntentInfo> {
public final ActivityInfo info;
...
}
public final static class ActivityIntentInfo extends IntentInfo {
public final Activity activity;
...
對應Package
類也 相對砍掉了不少變量,其中SigningDetails
是API28
才增加的新變量
public class Package {
public final ArrayList<Activity> activities = new ArrayList<Activity>(0);
public final ArrayList<Activity> receivers = new ArrayList<Activity>(0);
public final ArrayList<Provider> providers = new ArrayList<Provider>(0);
public final ArrayList<Service> services = new ArrayList<Service>(0);
public final ArrayList<Instrumentation> instrumentation = new ArrayList<Instrumentation>(0);
public final ArrayList<Permission> permissions = new ArrayList<Permission>(0);
public final ArrayList<PermissionGroup> permissionGroups = new ArrayList<PermissionGroup>(0);
public final ArrayList<String> requestedPermissions = new ArrayList<String>();
public Signature[] mSignatures;
public SigningDetails mSigningDetails; // API 28
public Bundle mAppMetaData;
public Object mExtras;
public String packageName;
public int mPreferredOrder;
public String mSharedUserId;
public ArrayList<String> usesLibraries;
public int mVersionCode;
public ApplicationInfo applicationInfo;
public String mVersionName;
// Applications hardware preferences
public ArrayList<ConfigurationInfo> configPreferences = null;
// Applications requested features
public ArrayList<FeatureInfo> reqFeatures = null;
public int mSharedUserLabel;
}
PackageParse
類沒有解析函數,保存一些仿系統的數據類的簡化版,對應的Parcelable
實現爲VPackage
解析代碼位於PackageParserEx
中
PackageParse
類位於android.content.pm
,這和系統保持一致
4.2 VPackage類簡介
PackageParse
類對應的Parcelable
實現爲VPackage
類,
比如PackageParse.IntentInfo
對應VPackage.IntentInfo
,VPackage.IntentInfo
提供了相應的構造函數進行轉換
public static class IntentInfo implements Parcelable {
...
public IntentInfo(PackageParser.IntentInfo info) {
this.filter = info;
this.hasDefault = info.hasDefault;
this.labelRes = info.labelRes;
if (info.nonLocalizedLabel != null) {
this.nonLocalizedLabel = info.nonLocalizedLabel.toString();
}
this.icon = info.icon;
this.logo = info.logo;
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT) {
this.banner = info.banner;
}
}
}
同樣的,PackageParse.Component
對應VPackage.Component
,VPackage.Component
提供了相應的構造函數進行轉換
public Component(PackageParser.Component component) {
this.className = component.className;
this.metaData = component.metaData;
}
同理,一一對應,VPackage
均提供了相應的構造函數進行轉換
PackageParse.ActivityIntentInfo
對應VPackage.ActivityIntentInfo
PackageParse.ServiceIntentInfo
對應VPackage.ServiceIntentInfo
PackageParse.ProviderIntentInfo
對應VPackage.ProviderIntentInfo
PackageParse.Activity
對應VPackage.ActivityComponent
PackageParse.Service
對應VPackage.ServiceComponent
PackageParse.Provider
對應VPackage.ProviderComponent
PackageParse.Instrumentation
對應VPackage.InstrumentationComponent
PackageParse.Permission
對應VPackage.PermissionComponent
PackageParse.PermissionGroup
對應VPackage.PermissionGroupComponent
4.3 PackageParserEx類簡介
PackageParserEx
繼承於PackageParse
類,類似於系統的PackageParse
類,它提供了parsePackage
接口,這個函數第一行是:
PackageParser parser = PackageParserCompat.createParser(packageFile);
VA自建的android.content.pm.PackageParse
,它其實更多的意義是佔位符,讓反射方式更優雅,它和系統的android.content.pm.PackageParse
保持一致,但實際上,系統解析得到的android.content.pm.PackageParse
,所以這裏返回的還是系統的那一份PackageParser
,不要理解爲VA的那一份
PackageParserCompat
是一個包裝類,內部通過區分APK版本號來反射系統的android.content.pm.PackageParse
parsePackage
代碼如下:
public static VPackage parsePackage(File packageFile) throws Throwable { // 59
PackageParser parser = PackageParserCompat.createParser(packageFile);
PackageParser.Package p = PackageParserCompat.parsePackage(parser, packageFile, 0);
...
return buildPackageCache(p);
}
-->PackageParserCompat.createParser代碼如下:
public static PackageParser createParser(File packageFile) {
if (API_LEVEL >= M) {
return PackageParserMarshmallow.ctor.newInstance();
...
佔位類的好處是可以直接調用系統隱藏類的變量,不能再通過反射方式去獲取了, 如下圖:
buildPackageCache
方法用於把將系統的Package.Package
轉換成可以序列化的 VPackage
類型
4.4 VAMS中安裝包的詳細實現流程
VAMS
中安裝包的函數是installPackageImpl
,以下是全流程跟進:
假定我們的安裝包路徑是:/data/app/com.hgy413.refclass-xxx==/base.apk
===> 1.利用PackageParserEx.parsePackage(packageFile);
把base.apk
解析成 VPackage pkg
。
內部仍是利用系統的android.content.pm.PackageParser
解包成PackageParser.Package
。
雖然我們定義了一個PackageParser
,但它的作用就是簡單的避免反射調用系統的android.content.pm.PackageParser
。
PackageParser.Package p = PackageParserCompat.parsePackage(parser, packageFile, 0);
然後通過buildPackageCache
把PackageParser.Package
轉成 VPackage pkg
。
最後通過addOwner
把VPackage.ActivityComponent
, VPackage.ServiceComponent
等的owner
設置爲 VPackage pkg
自身。
如果是google service
,還需要加上 flags |= ApplicationInfo.FLAG_PERSISTENT;
。
===> 2.判斷是否需要升級
PackageCacheManager
類:
它的成員 ArrayMap<String, VPackage> PACKAGE_CACHE
記錄了所有的 VPackage
, key
爲包名。
而VPackage
的mExtras
成員變量中又記錄了PackageSetting
對象,所以PackageSetting
對象和 VPackage
就關聯起來了。
升級邏輯如下:
首先去PackageCacheManager
類中查找是否已存在相同包名的 VPackage
,不存在就繼續 。
如果存在,檢查傳入的InstallOptions.updateStrategy
值,默認是COMPARE_VERSION
,也就是比較新舊安裝包的versioncode
,僅當新的>舊的才覆蓋安裝。
如果需要升級,就要調用VActivityManagerService.get().killAppByPkg
把原來的進程kill掉,並從PACKAGE_CACHE
中移除原來的VPackage
。如果不需要升級,就直接返回升級失敗。
===> 3.初始化PackageSetting ps
對象
如果PACKAGE_CACHE
中已存在VPackage
,就取VPackage
的mExtras
成員變量,否則創建一個新的PackageSetting ps
對象。
===> 4.複製abi下的so文件到virtual目錄
-
NativeLibraryHelperCompat.getSupportAbiList
通過zip解壓遍歷base.apk
得到它內部有哪些abi
,也就是base.apk
的lib
目錄下有哪些abi
,如armeabi-v7a
,x86
等。 -
如果
abi
有arm64-v8a
,x86_64
,mips64
中一個目錄,就認爲這個apk支持x64, 這種屬性通過PackageSetting ps.flag
來記錄
if (support32bit) {
if (support64bit) {
ps.flag = PackageSetting.FLAG_RUN_BOTH_32BIT_64BIT;
} else {
ps.flag = PackageSetting.FLAG_RUN_32BIT;
}
} else {
ps.flag = PackageSetting.FLAG_RUN_64BIT;
}
-
NativeLibraryHelperCompat.copyNativeBinaries
執行復制abi
操作,例如把/data/app/com.hgy413.refclass-xxx==/base.apk
中的lib/x86/libv++.so
拷貝到/data/data/io.busniess.va/virtual/data/app/com.hgy.ndk/lib/libv++.so
,系統安裝apk時,系統會把abi
解壓到data/app/com.hgy.ndk/lib
, VA以類似的方式保存在它的virtual
目錄中, 差別是它把x86
這個abi
目錄去掉了。 -
默認如果是系統安裝過的apk,則
useSourceLocationApk=true
, 這時使用外部的apk。
但如果是未被安裝的apk安裝包,則useSourceLocationApk=false
, 這時需要做apk拷貝。
例如:把/data/app/com.hgy413.refclass-xxx==/base.apk
拷貝到/data/data/io.busniess.va/virtual/data/app/com.hgy.ndk/base.apk
,並把packageFile
指向拷貝後的目錄。
===> 5.設置PackageSetting ps
對象
設置以下信息到ps
中,在前面還設置了ps.flag
ps.appMode = useSourceLocationApk ? MODE_APP_USE_OUTSIDE_APK : MODE_APP_COPY_APK;
ps.packageName = pkg.packageName;
ps.appId = VUserHandle.getAppId(mUidSystem.getOrCreateUid(pkg));
if (res.isUpdate) {
ps.lastUpdateTime = installTime;
} else {
ps.firstInstallTime = installTime;
ps.lastUpdateTime = installTime;
for (int userId : VUserManagerService.get().getUserIds()) {
boolean installed = userId == 0;// 默認用戶 userId = 0時,installed爲true
ps.setUserState(userId, false/*launched*/, false/*hidden*/, installed);
}
}
---> ps.setUserState(userId, false/*launched*/, false/*hidden*/, installed);
public class PackageUserState implements Parcelable {
public boolean launched;
public boolean hidden;
public boolean installed;
}
VUserHandle.getAppId(mUidSystem.getOrCreateUid(pkg));
仿系統的UMS
機制,默認appid
從10001
開始,依次遞增。
ps.setUserState
內部會針對每個用戶環璋生成一個PackageUserState
,並記錄這個ps
在每個用戶環境的安裝狀態。
默認用戶環境(userid=0
)是launched = false, hidden = false, installed = true;
, 其餘用戶環境是launched = false, hidden = false, installed = false;
。
簡單的說,PackageSetting
記錄了安裝時間,更新時間,每個用戶環境的安裝狀態,包名,appid
, 是否拷貝了apk, 是否支持x64,等信息。
===> 6.把VPackage pkg
寫入package.ini
及signature.ini
文件
PackageParserEx.savePackageCache(pkg);
所做的操作:
- 先獲取
virtual/data/app/packageName/package.ini
,以及virtual/data/app/packageName/signature.ini
- 以
Parcel
方式把pkg
寫入到package.ini
中:(在loadPackageInnerLocked
中會跨進程從package.ini
讀取pkg
)
pkg.writeToParcel(p, 0);
FileOutputStream fos = new FileOutputStream(cacheFile);
fos.write(p.marshall());
fos.close();
- 以
Parcel
方式把pkg.mSignatures
寫入到signature.ini
中
===> 7.把VPackage pkg
和PackageSetting ps
都存在PackageCacheManager
中
PackageParserEx.initApplicationInfoBase(ps, pkg);
PACKAGE_CACHE.put(pkg.packageName, pkg); // 存入`VPackage pkg`
pkg.mExtras = ps; // 存入`PackageSetting ps`
VPackageManagerService.get().analyzePackageLocked(pkg);
PackageParserEx.initApplicationInfoBase(ps, pkg);
就是把pkg.ApplicationInfo
根據ps
做一些初始化,如uid
,sharedLibraryFiles
===> 8. 接第7點的VPackageManagerService.get().analyzePackageLocked(pkg)
繼續分析:
主要是VPMS
保存VPackage pkg
的一些信息
- 保存pkg的
activities
到mActivities
。
mActivities
的類型是ActivityIntentResolver
,它內部的addActivity
函數如下:
public final void addActivity(VPackage.ActivityComponent a, String type) {
mActivities.put(a.getComponentName(), a);
final int NI = a.intents.size();
for (int j = 0; j < NI; j++) {
VPackage.ActivityIntentInfo intent = a.intents.get(j);
if (intent.filter.getPriority() > 0 && "activity".equals(type)) {
intent.filter.setPriority(0);
Log.w(TAG, "Package " + a.info.applicationInfo.packageName + " has activity " + a.className
+ " with priority > 0, forcing to 0");
}
addFilter(intent);
}
}
ComponentName
類由packageName
和className
組成,例如:mClass = com.hgy.ndk.MainActivity
, mPackage = com.hgy.ndk
。
所有的activity
都被ActivityIntentResolver
記錄到它的mActivities
中,key
是ComponentName
:
HashMap<ComponentName, VPackage.ActivityComponent> mActivities
所有的intent
都被ActivityIntentResolver
記錄到它的mFilters
中:
private HashSet<F> mFilters = new HashSet<F>();
同時intent
也會被解析出action
之類的字符串加入到內部mActionToFilter
, schemes
字符串加入到內部mSchemeToFilter
以獲取action
字符串爲例:
VPackage.ActivityComponent a
--->取第1個測試下:
VPackage.ActivityIntentInfo intent = a.intents.get(0);
--->
Iterator<String> i = f.filter.actionsIterator()
--->遍歷Iterator<String> i,就可以得到android.intent.action.MAIN這種字符串了,加入mActionToFilter:
register_intent_filter(f, f.filter.actionsIterator(), mActionToFilter, " Action: ");
這部分代碼具體參考了系統的PackageManagerService.ActivityIntentResolver
。
- 保存pkg的
services
到mServices
。
這部分和1非常類似,mServices
的類型是ServiceIntentResolver
,它內部的addService
函數如下:
public final void addService(VPackage.ServiceComponent s) {
mServices.put(s.getComponentName(), s);
final int NI = s.intents.size();
int j;
for (j = 0; j < NI; j++) {
VPackage.ServiceIntentInfo intent = s.intents.get(j);
addFilter(intent);
}
}
- 保存pkg的
receivers
到mReceivers
。
mReceivers
的類型同樣是ActivityIntentResolver
,所以它和1唯一的區別是傳入的type是"receiver"
。
mReceivers.addActivity(a, "receiver");
- 保存pkg的
providers
到mProviders
。
N = pkg.providers.size();
for (int i = 0; i < N; i++) {
VPackage.ProviderComponent p = pkg.providers.get(i);
if (p.info.processName == null) {
p.info.processName = p.info.packageName;
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
mProviders.addProvider(p);
}
String names[] = p.info.authority.split(";");
synchronized (mProvidersByAuthority) {
for (String name : names) {
if (!mProvidersByAuthority.containsKey(name)) {
mProvidersByAuthority.put(name, p);
}
}
}
mProvidersByComponent.put(p.getComponentName(), p);
}
mProviders
的類型是ProviderIntentResolver
, mProviders.addProvider(p);
這部分和1非常類似。
另外它要取得authority
保存到mProvidersByAuthority
中,它也會保存到mProvidersByComponent
中,這是爲了方便通過authority
或ComponentName
快速查詢到 VPackage.ProviderComponent
。
- 保存pkg的
permissions
到mPermissions
,permissionGroups
到mPermissionGroups
,例如:
<permission
android:name="android.permission.SAFE_ACCESS"
android:protectionLevel="signature" />
下面的permission.info.name
對應就是android.permission.SAFE_ACCESS
,代碼如下:
N = pkg.permissions.size();
for (int i = 0; i < N; i++) {
VPackage.PermissionComponent permission = pkg.permissions.get(i);
mPermissions.put(permission.info.name, permission);
}
N = pkg.permissionGroups.size();
for (int i = 0; i < N; i++) {
VPackage.PermissionGroupComponent group = pkg.permissionGroups.get(i);
mPermissionGroups.put(group.className, group);
}
- 保存pkg的
requestedPermissions
中的危險權限到mDangerousPermissions
,例如:
<uses-permission android:name="android.permission.WRITE_SOCIAL_STREAM" />
<uses-permission android:name="android.permission.READ_SOCIAL_STREAM" />
這裏會通過PermissionCompat.findDangerousPermissions
來確認請求的是否爲危險權限,如果是,則加入mDangerousPermissions
中,危險權限包含了這些:
public static Set<String> DANGEROUS_PERMISSION = new HashSet<String>() {{
// CALENDAR group
add(Manifest.permission.READ_CALENDAR);
add(Manifest.permission.WRITE_CALENDAR);
// CAMERA
add(Manifest.permission.CAMERA);
// CONTACTS
add(Manifest.permission.READ_CONTACTS);
add(Manifest.permission.WRITE_CONTACTS);
add(Manifest.permission.GET_ACCOUNTS);
// LOCATION
add(Manifest.permission.ACCESS_FINE_LOCATION);
add(Manifest.permission.ACCESS_COARSE_LOCATION);
// PHONE
add(Manifest.permission.READ_PHONE_STATE);
add(Manifest.permission.CALL_PHONE);
if (Build.VERSION.SDK_INT >= 16) {
add(Manifest.permission.READ_CALL_LOG);
add(Manifest.permission.WRITE_CALL_LOG);
}
add(Manifest.permission.ADD_VOICEMAIL);
add(Manifest.permission.USE_SIP);
add(Manifest.permission.PROCESS_OUTGOING_CALLS);
// SMS
add(Manifest.permission.SEND_SMS);
add(Manifest.permission.RECEIVE_SMS);
add(Manifest.permission.READ_SMS);
add(Manifest.permission.RECEIVE_WAP_PUSH);
add(Manifest.permission.RECEIVE_MMS);
add(Manifest.permission.RECORD_AUDIO);
// STORAGE
add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
if (Build.VERSION.SDK_INT >= 16) {
add(Manifest.permission.READ_EXTERNAL_STORAGE);
}
if (Build.VERSION.SDK_INT >= 20) {
// SENSORS
add(Manifest.permission.BODY_SENSORS);
}
}};
<uses-permission>
是官方定義的權限,是調用別人的東西的時候自己需要聲明的權限,<permission>
是自己定義的權限,就是別人調用這個程序時需要用<uses-permission>
來聲明。
===> 9. 把PackageCacheManager
的內容寫入到文件packages.ini
前面提到了PackageCacheManager
類包含了所有的VPackage
及PackageSetting
數據,所以這裏以Parcel寫文件的方式把PackageCacheManager
類保存到virtual/data/app/system/packages.ini
中。
後續會在VDeviceManagerService
中讀取它們。
===> 10. 如果是支持32位又未被系統安裝的apk(即sd卡下的apk安裝包),嘗試把apk解析成dex,並動態加載dex。
if (support32bit && !useSourceLocationApk) {
DexOptimizer.optimizeDex(packageFile.getPath(), VEnvironment.getOdexFile(ps.packageName).getPath());
}
以/data/app/com.hgy413.refclass-xxx==/base.apk
爲例,主體邏輯如下:
-
DexOptimizer.optimizeDex
傳入參數分別爲/data/app/com.hgy413.refclass-xxx==/base.apk
及/data/data/io.busniess.va/virtual/opt/data@[email protected]@[email protected]
。 -
判斷是否爲
art
,並且在android5.0-androi7.1之間,如果是,則調用DexOptimizer.interpretDex2Oat
,interpretDex2Oat
這個函數內部是調用了系統的dex2oat
cmd命令來生成oat
的,它被廣泛使用,如微信的tinker。 -
調用
DexFile.loadDex(dexFilePath, optFilePath, 0)
===> 11. 是否通知安裝完成。
默認在AppRepository.addVirtualApp
中初始化InstallOptions
的notify=false
。
public InstallResult addVirtualApp(AppInfoLite info) { // 174
InstallOptions options = InstallOptions.makeOptions(info.notCopyApk, false, InstallOptions.UpdateStrategy.COMPARE_VERSION);
在notify=true
時,觸發通知邏輯,允許外部獲得安裝回調,代碼如下:
private void notifyAppInstalled(PackageSetting setting, int userId) {
final String pkg = setting.packageName;
int N = mRemoteCallbackList.beginBroadcast();
while (N-- > 0) {
try {
if (userId == -1) {
mRemoteCallbackList.getBroadcastItem(N).onPackageInstalled(pkg);
mRemoteCallbackList.getBroadcastItem(N).onPackageInstalledAsUser(0, pkg);
} else {
mRemoteCallbackList.getBroadcastItem(N).onPackageInstalledAsUser(userId, pkg);
}
} catch (RemoteException e) {
e.printStackTrace();
}
}
sendInstalledBroadcast(pkg, new VUserHandle(userId));
mRemoteCallbackList.finishBroadcast();
VAccountManagerService.get().refreshAuthenticatorCache(null);
}
--->
RemoteCallbackList<IPackageObserver> mRemoteCallbackList = new RemoteCallbackList<>()
--->客戶端註冊:
public void registerObserver(IPackageObserver observer) {
try {
getService().registerObserver(observer);
} catch (RemoteException e) {
VirtualRuntime.crash(e);
}
}
mRemoteCallbackList
由VirtualCore.registerObserver
註冊。
sendInstalledBroadcast
會廣播ACTION_PACKAGE_ADDED
消息,String ACTION_PACKAGE_ADDED = "virtual." + Intent.ACTION_PACKAGE_ADDED;
,代碼如下:
public void sendBroadcastAsUser(Intent intent, VUserHandle user) { // 1150
// intent包裝一次設置成va自定義的action
// eg: intent.setAction("virtual.android.intent.action.PACKAGE_ADDED")
SpecialComponentList.protectIntent(intent);
Context context = VirtualCore.get().getContext();
if (user != null) {
intent.putExtra("_VA_|_user_id_", user.getIdentifier());
}
context.sendBroadcast(intent);
}
===> 12.客戶端發起安裝,接收安裝結果:
客戶端點擊安裝的代碼流程如下:
添加App按鈕點擊事件爲HomeActivity.onAddAppButtonClick
—>
ListAppActivity.gotoListApp(this);
請求的requestCode
爲VCommends.REQUEST_SELECT_APP
—>
ListAppActivity
爲顯示系統已安裝Apk界面,它由mViewPager
構成:
mViewPager.setAdapter(new AppPagerAdapter(getSupportFragmentManager()));
—>
AppPagerAdapter
一般是兩個ListAppFragment
組成, 第一頁是設備上已安裝的apk,和存儲中下載好的可安裝的apk包。
第一頁dirs.add(null)
; 所以可以根據是否爲null,來確認是否取得已安裝apk還是下載的apk包。
new ListAppPresenterImpl(getActivity(), this, getSelectFrom()).start();
// 獲得App列表(系統已安裝的或系統內部存儲已安裝)。
內部通過Context.getPackageManager().getInstalledPackages(PackageManager.GET_PERMISSIONS)
取得系統已安裝的Apk列表
或通過findAndParseAPKs
來遍歷SCAN_PATH_LIST
文件夾列表取得可安裝的apk包。
—>
選中後的item操作位於ListAppFragment.onViewCreated
中,
mInstallButton.setOnClickListener( v -> { // 點擊安裝軟件
Integer[] selectedIndices = mAdapter.getSelectedIndices();
ArrayList<AppInfoLite> dataList = new ArrayList<AppInfoLite>(selectedIndices.length);
for (int index : selectedIndices) {
AppInfo info = mAdapter.getItem(index);
dataList.add(new AppInfoLite(info));
}
Intent data = new Intent();
data.putParcelableArrayListExtra(VCommends.EXTRA_APP_INFO_LIST, dataList);
getActivity().setResult(Activity.RESULT_OK, data);
getActivity().finish();
});
—>
getActivity().finish()
後響應起始請求的requestCode
(VCommends.REQUEST_SELECT_APP
)
protected void onActivityResult(int requestCode, int resultCode, Intent data) { // 390
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == VCommends.REQUEST_SELECT_APP) {
if (resultCode == RESULT_OK && data != null) {
List<AppInfoLite> appList = data.getParcelableArrayListExtra(VCommends.EXTRA_APP_INFO_LIST);
if (appList != null) {
for (AppInfoLite info : appList) {
mPresenter.addApp(info);
}
}
}
}
—>
mPresenter.addApp(info);
,其中mPresenter = new HomePresenterImpl(this);
–>
HomePresenterImpl.addApp
內部使用了 jdeferred庫異步安裝,異步處理返回結果
public void addApp(AppInfoLite info) {
class AddResult {
private PackageAppData appData;
private int userId;
}
AddResult addResult = new AddResult();
ProgressDialog dialog = ProgressDialog.show(mActivity, null, mActivity.getString(R.string.tip_add_apps));
VUiKit.defer().when(() -> {
...
InstallResult res = mRepo.addVirtualApp(info);
...
}
}).then((res) -> {
addResult.appData = PackageAppDataStorage.get().acquire(info.packageName);
}).fail((e) -> {
dialog.dismiss();
}).done(res -> {
if (addResult.userId == 0) {
PackageAppData data = addResult.appData;
data.isLoading = true;
mView.addAppToLauncher(data);
handleLoadingApp(data);
} else {
// 多用戶處理
...
}
dialog.dismiss();
});
}
–>
InstallResult res = mRepo.addVirtualApp(info);
安裝,mRepo = new AppRepository(mActivity);
–>
進入AppRepository
類中:
public InstallResult addVirtualApp(AppInfoLite info) {
InstallOptions options = InstallOptions.makeOptions(info.notCopyApk, false, InstallOptions.UpdateStrategy.COMPARE_VERSION);
return VirtualCore.get().installPackageSync(info.path, options);
}
--->VirtualCore.get().installPackageSync的代碼實現:
public InstallResult installPackageSync(String apkPath, InstallOptions options) {
final ConditionVariable lock = new ConditionVariable();
final InstallResult[] out = new InstallResult[1];
installPackage(apkPath, options, new InstallCallback() {
@Override
public void onFinish(InstallResult result) {
out[0] = result;
lock.open();
}
});
lock.block();
return out[0];
}
--->installPackage的代碼實現:
public void installPackage(String apkPath, InstallOptions options, final InstallCallback callback) {
ResultReceiver receiver = new ResultReceiver(null) {
@Override
protected void onReceiveResult(int resultCode, Bundle resultData) {
resultData.setClassLoader(InstallResult.class.getClassLoader());
if (callback != null) {
InstallResult res = resultData.getParcelable("result");
callback.onFinish(res);
}
}
};
try {
getService().installPackage(apkPath, options, receiver);
} catch (RemoteException e) {
VirtualRuntime.crash(e);
}
}
getService().installPackage(apkPath, options, receiver);
會調到VAMS
中安裝包的函數是installPackageImpl
,也就是起始分析的地方。
onReceiveResult
中接收安裝結果。
4.5 VAMS中安裝包完成後交互流程
回到 HomePresenterImpl.addApp
函數, 再看一次:
public void addApp(AppInfoLite info) {
class AddResult {
private PackageAppData appData;
private int userId;
}
AddResult addResult = new AddResult();
ProgressDialog dialog = ProgressDialog.show(mActivity, null, mActivity.getString(R.string.tip_add_apps));
VUiKit.defer().when(() -> {
...
InstallResult res = mRepo.addVirtualApp(info);
...
}
}).then((res) -> {
addResult.appData = PackageAppDataStorage.get().acquire(info.packageName);
}).fail((e) -> {
dialog.dismiss();
}).done(res -> {
if (addResult.userId == 0) {// 默認用戶
PackageAppData data = addResult.appData;
data.isLoading = true;
mView.addAppToLauncher(data);
handleLoadingApp(data);
} else {
// 多用戶處理
...
}
dialog.dismiss();
});
}
在安裝完成後,會返回InstallResult res
, res.isSuccess=ture
表示成功,之後執行:
addResult.appData = PackageAppDataStorage.get().acquire(info.packageName);
PackageAppData
類描述了它在VA中顯示的名字和圖標,如下定義:
public class PackageAppData extends AppData {
public String packageName;
public String name;
public Drawable icon;
而PackageAppDataStorage
類用於緩存所有的PackageAppData
,它的acquire函數定義如下:
public PackageAppData acquire(String packageName) {
PackageAppData data;
synchronized (packageDataMap) {
data = packageDataMap.get(packageName);// 先從本地緩存取
if (data == null) {
data = loadAppData(packageName);// 如果緩存沒有,就從遠程VAMS去取
}
}
return data;
}
—>loadAppData
嘗試從VPMS
去取,如果成功,就加入本地緩存, 代碼如下:
private PackageAppData loadAppData(String packageName) { // 45
InstalledAppInfo setting = VirtualCore.get().getInstalledAppInfo(packageName, 0);
....
PackageAppData data = new PackageAppData(App.getApp(), setting);
packageDataMap.put(packageName, data);// 如果成功,就加入本地緩存
...
}
—>InstalledAppInfo setting
由VAMS
交互取得,流程如下:
--->VirtualCore類中的getInstalledAppInfo:
public InstalledAppInfo getInstalledAppInfo(String pkg, int flags) {
return getService().getInstalledAppInfo(pkg, flags);
}
--->調用到VAMS中:
public InstalledAppInfo getInstalledAppInfo(String packageName, int flags) {
synchronized (PackageCacheManager.class) {
if (packageName != null) {
PackageSetting setting = PackageCacheManager.getSetting(packageName);
if (setting != null) {
return setting.getAppInfo();
}
}
return null;
}
}
可以看到就是從前面提到的PackageCacheManager
類中取。
—>將InstalledAppInfo setting
轉換成 PackageAppData data
,PackageAppData
的構造函數如下:
public PackageAppData(Context context, InstalledAppInfo installedAppInfo) {
this.packageName = installedAppInfo.packageName;
this.isFirstOpen = !installedAppInfo.isLaunched(0);
loadData(context, installedAppInfo.getApplicationInfo(installedAppInfo.getInstalledUsers()[0]));
}
--->loadData實現如下:
private void loadData(Context context, ApplicationInfo appInfo) { // 28
PackageManager pm = context.getPackageManager();
name = appInfo.loadLabel(pm).toString();
icon = appInfo.loadIcon(pm);
}
—>loadData中第二個參數ApplicationInfo appInfo
由VPMS
交互取得,流程如下:
public ApplicationInfo getApplicationInfo(int userId) {
return VPackageManager.get().getApplicationInfo(packageName, 0, userId);
}
--->VPackageManager類中的getApplicationInfo:
public ApplicationInfo getApplicationInfo(String packageName, int flags, int userId) {
return getService().getApplicationInfo(packageName, flags, userId);
}
--->遠程VPMS中的getApplicationInfo:
public ApplicationInfo getApplicationInfo(String packageName, int flags, int userId) {
...
VPackage p = mPackages.get(packageName);
if (p != null) {
PackageSetting ps = (PackageSetting) p.mExtras;
return PackageParserEx.generateApplicationInfo(p, flags, ps.readUserState(userId),
userId);
}
}
return null;
}
--->PackageParserEx.generateApplicationInfo:
public static ApplicationInfo generateApplicationInfo(VPackage p, int flags,
PackageUserState state, int userId) {
...
ApplicationInfo ai = new ApplicationInfo(p.applicationInfo);
if ((flags & PackageManager.GET_META_DATA) != 0) {
ai.metaData = p.mAppMetaData;
}
initApplicationAsUser(ai, userId);
return ai;
}
可以看出VPMS
返回的是VPackage.applicationInfo
。
—>最後顯示圖標出來:
}).done(res -> {
if (addResult.userId == 0) {// 默認用戶
PackageAppData data = addResult.appData;
data.isLoading = true;
mView.addAppToLauncher(data); // HomeActivity
handleLoadingApp(data);
--->handleLoadingApp:
private void handleLoadingApp(AppData data) {
VUiKit.defer().when(() -> {
....
}).done((res) -> {
if (data instanceof PackageAppData) {
((PackageAppData) data).isLoading = false;
((PackageAppData) data).isFirstOpen = true;
} else if (data instanceof MultiplePackageAppData) {
((MultiplePackageAppData) data).isLoading = false;
((MultiplePackageAppData) data).isFirstOpen = true;
}
mView.refreshLauncherItem(data);
});
}
也就是通過HomeActivity
的addAppToLauncher
及refreshLauncherItem
加載和顯示安裝APK的圖標。
參考:
Android包管理機制