- 這是 Android 10 源碼分析系列的第 2 篇
- 分支:android-10.0.0_r14
- 全文閱讀大概 5 分鐘
上一篇文章介紹了0xA01 ASOP應用框架:Apk是如何生成的,這篇文章接着介紹如何安裝Apk,須要說一下Android 10 及更高版本中, 安裝器PackageInstaller源碼位置有所變更
java
PackageInstaller是系統內置的應用程序,用於安裝和卸載應用
android
在 Android 9 及更低版本中,軟件包安裝和權限控制功能包含在 PackageInstaller 軟件包 (//packages/apps/PackageInstaller) 中。在 Android 10 及更高版本中,權限控制功能位於單獨的軟件包 PermissionController (//packages/apps/PermissionController),這兩個軟件包在 Android 10 中的位置以下圖所示,更多信息點擊這裏前往Android 權限git
Android 9 及更低版本中 :github
軟件包安裝和權限控制功能源碼路徑:packages/apps/PackageInstaller算法
Android 10 及更高版本:緩存
AOSP-PackageInstaller: 包含了安裝器PackageInstaller(7.1.二、8.1.0、9.0.0、10.0.0)的源碼,能夠切換分之查看,跟隨 Android 版本更新,你永遠能夠看到最新的源代碼 bash
aospxref:這是一個在線查看Android源碼網站,服務器在阿里雲訪問速度很快,文末有關這個網站的介紹服務器
googlesource-PackageInstaller:這是安裝器PackageInstaller在googlesource上的地址session
安裝Apk主要分爲如下三種場景數據結構
public static PackageManagerService main(Context context, Installer installer,
boolean factoryTest, boolean onlyCore) {
// Self-check for initial settings.
PackageManagerServiceCompilerMapping.checkProperties();
PackageManagerService m = new PackageManagerService(context, installer,
factoryTest, onlyCore);
m.enableSystemUserPackages();
ServiceManager.addService("package", m);
final PackageManagerNative pmn = m.new PackageManagerNative();
ServiceManager.addService("package_native", pmn);
return m;
}
複製代碼
全部安裝方式大體相同,最終就是回到PackageManagerService中,安裝一個APK的大體流程以下:
本文主要來分析經過安裝器PackageInstaller安裝Apk,這是用戶最經常使用的一種方式
下面代碼必定不會很陌生,這就是咱們經常使用的安裝Apk的代碼(PS: 關於靜默安裝我會後續分享在逆向開發相關的文章)
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
/*
* 自Android N開始,是經過FileProvider共享相關文件,可是Android Q對公
* 有目錄 File API進行了限制,只能經過Uri來操做
*/
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q){
// filePath是經過ContentResolver獲得的
intent.setDataAndType(Uri.parse(filePath) ,"application/vnd.android.package-archive");
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
}else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
Uri contentUri = FileProvider.getUriForFile(mContext, "com.dhl.file.fileProvider", file);
intent.setDataAndType(contentUri, "application/vnd.android.package-archive");
} else {
intent.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive");
}
startActivity(intent);
// 須要在AndroidManifest添加權限
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
複製代碼
經過intent.setDataAndType方法指定Intent的數據類型爲application/vnd.android.package-archive,隱式匹配的Activity爲InstallStart: frameworks/base/packages/PackageInstaller/AndroidManifest.xml
<activity android:name=".InstallStart"
android:theme="@android:style/Theme.Translucent.NoTitleBar"
android:exported="true"
android:excludeFromRecents="true">
<intent-filter android:priority="1">
<action android:name="android.intent.action.VIEW" />
<action android:name="android.intent.action.INSTALL_PACKAGE" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="content" />
<data android:mimeType="application/vnd.android.package-archive" />
</intent-filter>
<intent-filter android:priority="1">
<action android:name="android.intent.action.INSTALL_PACKAGE" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="package" />
<data android:scheme="content" />
</intent-filter>
<intent-filter android:priority="1">
<action android:name="android.content.pm.action.CONFIRM_INSTALL" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
複製代碼
經過上面方式找到了入口Activity,下面咱們來查看一下Apk是如何安裝的
主要工做:
當咱們調用上面安裝代碼來安裝Apk時。會跳轉到InstallStart, 並調用它的onCreate方法: frameworks/base/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
final boolean isSessionInstall =
PackageInstaller.ACTION_CONFIRM_INSTALL.equals(intent.getAction());
...
final ApplicationInfo sourceInfo = getSourceInfo(callingPackage);
final int originatingUid = getOriginatingUid(sourceInfo);
boolean isTrustedSource = false;
// 判斷是否勾選「未知來源」選項
if (sourceInfo != null
&& (sourceInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0) {
isTrustedSource = intent.getBooleanExtra(Intent.EXTRA_NOT_UNKNOWN_SOURCE, false);
}
if (!isTrustedSource && originatingUid != PackageInstaller.SessionParams.UID_UNKNOWN) {
final int targetSdkVersion = getMaxTargetSdkVersionForUid(this, originatingUid);
// 若是targetSdkVerison小於0停止安裝
if (targetSdkVersion < 0) {
Log.w(LOG_TAG, "Cannot get target sdk version for uid " + originatingUid);
mAbortInstall = true;
// 若是targetSdkVersion大於等於26(8.0), 且獲取不到REQUEST_INSTALL_PACKAGES權限停止安裝
} else if (targetSdkVersion >= Build.VERSION_CODES.O && !declaresAppOpPermission(
originatingUid, Manifest.permission.REQUEST_INSTALL_PACKAGES)) {
Log.e(LOG_TAG, "Requesting uid " + originatingUid + " needs to declare permission "
+ Manifest.permission.REQUEST_INSTALL_PACKAGES);
mAbortInstall = true;
}
}
...
// 若是設置了ACTION_CONFIRM_PERMISSIONS,則調用PackageInstallerActivity。
if (isSessionInstall) {
nextActivity.setClass(this, PackageInstallerActivity.class);
} else {
Uri packageUri = intent.getData();
// 判斷Uri的Scheme協議是不是content
if (packageUri != null && packageUri.getScheme().equals(
ContentResolver.SCHEME_CONTENT)) {
// [IMPORTANT] This path is deprecated, but should still work.
// 這個路徑已經被起用了,可是仍然能夠工做
// 調用InstallStaging來拷貝file/content,防止被修改
nextActivity.setClass(this, InstallStaging.class);
} else if (packageUri != null && packageUri.getScheme().equals(
PackageInstallerActivity.SCHEME_PACKAGE)) {
// 若是Uri中包含package,則調用PackageInstallerActivity
nextActivity.setClass(this, PackageInstallerActivity.class);
} else {
// Uri不合法
Intent result = new Intent();
result.putExtra(Intent.EXTRA_INSTALL_RESULT,
PackageManager.INSTALL_FAILED_INVALID_URI);
setResult(RESULT_FIRST_USER, result);
nextActivity = null;
}
}
if (nextActivity != null) {
startActivity(nextActivity);
}
finish();
}
複製代碼
根據Uri的Scheme協議,如果content則調用InstallStaging,查看InstallStaging的onResume方法: frameworks/base/packages/PackageInstaller/src/com/android/packageinstaller/InstallStaging.java
@Override
protected void onResume() {
super.onResume();
if (mStagingTask == null) {
if (mStagedFile == null) {
// 建立臨時文件 mStagedFile 用來存儲數據
try {
mStagedFile = TemporaryFileManager.getStagedFile(this);
} catch (IOException e) {
showError();
return;
}
}
// 啓動 StagingAsyncTask,並傳入了content協議的Uri
mStagingTask = new StagingAsyncTask();
mStagingTask.execute(getIntent().getData());
}
}
複製代碼
private final class StagingAsyncTask extends AsyncTask<Uri, Void, Boolean> {
@Override
protected Boolean doInBackground(Uri... params) {
...
Uri packageUri = params[0];
try (InputStream in = getContentResolver().openInputStream(packageUri)) {
...
// 將packageUri(content協議的Uri)的內容寫入到mStagedFile中
try (OutputStream out = new FileOutputStream(mStagedFile)) {
byte[] buffer = new byte[1024 * 1024];
int bytesRead;
while ((bytesRead = in.read(buffer)) >= 0) {
// Be nice and respond to a cancellation
if (isCancelled()) {
return false;
}
out.write(buffer, 0, bytesRead);
}
}
} catch (IOException | SecurityException | IllegalStateException e) {
Log.w(LOG_TAG, "Error staging apk from content URI", e);
return false;
}
return true;
}
@Override
protected void onPostExecute(Boolean success) {
if (success) {
// 若是寫入成功,調用DeleteStagedFileOnResult
Intent installIntent = new Intent(getIntent());
installIntent.setClass(InstallStaging.this, DeleteStagedFileOnResult.class);
installIntent.setData(Uri.fromFile(mStagedFile));
...
startActivity(installIntent);
InstallStaging.this.finish();
} else {
showError();
}
}
}
複製代碼
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState == null) {
// 啓動PackageInstallerActivity
Intent installIntent = new Intent(getIntent());
installIntent.setClass(this, PackageInstallerActivity.class);
installIntent.setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
startActivityForResult(installIntent, 0);
}
}
複製代碼
通過分析InstallStaging主要起了中轉做用,將content協議的Uri轉換爲File協議,最後跳轉到PackageInstallerActivity
主要工做:
PackageInstallerActivity纔是應用安裝器PackageInstaller真正的入口Activity,查看它的onCreate方法: frameworks/base/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
protected void onCreate(Bundle icicle) {
getWindow().addSystemFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
super.onCreate(null);
// 初始化安裝須要用到的對象
mPm = getPackageManager();
mIpm = AppGlobals.getPackageManager();
mAppOpsManager = (AppOpsManager) getSystemService(Context.APP_OPS_SERVICE);
mInstaller = mPm.getPackageInstaller();
mUserManager = (UserManager) getSystemService(Context.USER_SERVICE);
// 根據Uri的Scheme作不一樣的處理
boolean wasSetUp = processPackageUri(packageUri);
if (!wasSetUp) {
return;
}
// 顯示安裝界面
bindUi();
// 檢查是否容許安裝包,若是容許則啓動安裝。若是不容許顯示適當的對話框
checkIfAllowedAndInitiateInstall();
}
複製代碼
主要作了對象的初始化,解析Uri的Scheme,初始化界面,安裝包檢查等等工做,接着查看一下processPackageUri方法
private boolean processPackageUri(final Uri packageUri) {
mPackageURI = packageUri;
final String scheme = packageUri.getScheme();
// 根據這個Scheme協議分別對package協議和file協議進行處理
switch (scheme) {
case SCHEME_PACKAGE: {
try {
// 經過PackageManager對象獲取指定包名的包信息
mPkgInfo = mPm.getPackageInfo(packageUri.getSchemeSpecificPart(),
PackageManager.GET_PERMISSIONS
| PackageManager.MATCH_UNINSTALLED_PACKAGES);
} catch (NameNotFoundException e) {
}
if (mPkgInfo == null) {
Log.w(TAG, "Requested package " + packageUri.getScheme()
+ " not available. Discontinuing installation");
showDialogInner(DLG_PACKAGE_ERROR);
setPmResult(PackageManager.INSTALL_FAILED_INVALID_APK);
return false;
}
mAppSnippet = new PackageUtil.AppSnippet(mPm.getApplicationLabel(mPkgInfo.applicationInfo),
mPm.getApplicationIcon(mPkgInfo.applicationInfo));
} break;
case ContentResolver.SCHEME_FILE: {
// 根據packageUri建立一個新的File
File sourceFile = new File(packageUri.getPath());
// 解析Apk獲得Apk的信息,PackageParser.Package存儲了Apk的全部信息
PackageParser.Package parsed = PackageUtil.getPackageInfo(this, sourceFile);
if (parsed == null) {
Log.w(TAG, "Parse error when parsing manifest. Discontinuing installation");
showDialogInner(DLG_PACKAGE_ERROR);
setPmResult(PackageManager.INSTALL_FAILED_INVALID_APK);
return false;
}
// 根據PackageParser.Package獲得的Apk信息,生成PackageInfo
mPkgInfo = PackageParser.generatePackageInfo(parsed, null,
PackageManager.GET_PERMISSIONS, 0, 0, null,
new PackageUserState());
mAppSnippet = PackageUtil.getAppSnippet(this, mPkgInfo.applicationInfo, sourceFile);
} break;
default: {
throw new IllegalArgumentException("Unexpected URI scheme " + packageUri);
}
}
return true;
}
複製代碼
主要對Scheme協議分別對package協議和file協議進行處理
SCHEME_PACKAGE:
SCHEME_FILE:
接着往下走,都解析完成以後,回到onCreate方法,繼續調用checkIfAllowedAndInitiateInstall方法
private void checkIfAllowedAndInitiateInstall() {
// 首先檢查安裝應用程序的用戶限制,若是有限制並彈出彈出提示Dialog或者跳轉到設置界面
final int installAppsRestrictionSource = mUserManager.getUserRestrictionSource(
UserManager.DISALLOW_INSTALL_APPS, Process.myUserHandle());
if ((installAppsRestrictionSource & UserManager.RESTRICTION_SOURCE_SYSTEM) != 0) {
showDialogInner(DLG_INSTALL_APPS_RESTRICTED_FOR_USER);
return;
} else if (installAppsRestrictionSource != UserManager.RESTRICTION_NOT_SET) {
startActivity(new Intent(Settings.ACTION_SHOW_ADMIN_SUPPORT_DETAILS));
finish();
return;
}
// 判斷若是容許安裝未知來源或者根據Intent判斷得出該APK不是未知來源
if (mAllowUnknownSources || !isInstallRequestFromUnknownSource(getIntent())) {
initiateInstall();
} else {
// 檢查未知安裝源限制,若是有限制彈出Dialog,顯示相應的信息
final int unknownSourcesRestrictionSource = mUserManager.getUserRestrictionSource(
UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES, Process.myUserHandle());
final int unknownSourcesGlobalRestrictionSource = mUserManager.getUserRestrictionSource(
UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY, Process.myUserHandle());
final int systemRestriction = UserManager.RESTRICTION_SOURCE_SYSTEM
& (unknownSourcesRestrictionSource | unknownSourcesGlobalRestrictionSource);
if (systemRestriction != 0) {
showDialogInner(DLG_UNKNOWN_SOURCES_RESTRICTED_FOR_USER);
} else if (unknownSourcesRestrictionSource != UserManager.RESTRICTION_NOT_SET) {
startAdminSupportDetailsActivity(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES);
} else if (unknownSourcesGlobalRestrictionSource != UserManager.RESTRICTION_NOT_SET) {
startAdminSupportDetailsActivity(
UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY);
} else {
// 處理未知來源的APK
handleUnknownSources();
}
}
}
複製代碼
主要檢查安裝應用程序的用戶限制,當Apk文件不對或者安裝有限制則調用showDialogInner方法,彈出dialog提示用戶,顯示相應的錯誤信息,來看一下都有那些錯誤信息
// Dialog identifiers used in showDialog
private static final int DLG_BASE = 0;
// package信息錯誤
private static final int DLG_PACKAGE_ERROR = DLG_BASE + 2;
// 存儲空間不夠
private static final int DLG_OUT_OF_SPACE = DLG_BASE + 3;
// 安裝錯誤
private static final int DLG_INSTALL_ERROR = DLG_BASE + 4;
// 用戶限制的未知來源
private static final int DLG_UNKNOWN_SOURCES_RESTRICTED_FOR_USER = DLG_BASE + 5;
private static final int DLG_ANONYMOUS_SOURCE = DLG_BASE + 6;
// 在wear上不支持
private static final int DLG_NOT_SUPPORTED_ON_WEAR = DLG_BASE + 7;
private static final int DLG_EXTERNAL_SOURCE_BLOCKED = DLG_BASE + 8;
// 安裝限制用戶使用的應用程序
private static final int DLG_INSTALL_APPS_RESTRICTED_FOR_USER = DLG_BASE + 9;
複製代碼
若是用戶容許安裝未知來源,會調用initiateInstall方法
private void initiateInstall() {
String pkgName = mPkgInfo.packageName;
// 檢查設備上是否存在相同包名的Apk
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;
}
// 檢查package是否已安裝, 若是已經安裝則顯示對話框提示用戶是否替換。
try {
mAppInfo = mPm.getApplicationInfo(pkgName,
PackageManager.MATCH_UNINSTALLED_PACKAGES);
if ((mAppInfo.flags&ApplicationInfo.FLAG_INSTALLED) == 0) {
mAppInfo = null;
}
} catch (NameNotFoundException e) {
mAppInfo = null;
}
// 初始化確認安裝界面
startInstallConfirm();
}
複製代碼
根據包名獲取應用程序的信息,調用startInstallConfirm方法初始化安裝確認界面後,當用戶點擊確認按鈕以後發生了什麼,接着查看確認按鈕點擊事件
private void bindUi() {
...
// 點擊確認按鈕,安裝Apk
mAlert.setButton(DialogInterface.BUTTON_POSITIVE, getString(R.string.install),
(ignored, ignored2) -> {
if (mOk.isEnabled()) {
if (mSessionId != -1) {
mInstaller.setPermissionsResult(mSessionId, true);
finish();
} else {
// 啓動Activity來完成應用的安裝
startInstall();
}
}
}, null);
// 點擊取消按鈕,取消這次安裝
mAlert.setButton(DialogInterface.BUTTON_NEGATIVE, getString(R.string.cancel),
(ignored, ignored2) -> {
// Cancel and finish
setResult(RESULT_CANCELED);
if (mSessionId != -1) {
mInstaller.setPermissionsResult(mSessionId, false);
}
finish();
}, null);
setupAlert();
mOk = mAlert.getButton(DialogInterface.BUTTON_POSITIVE);
mOk.setEnabled(false);
}
複製代碼
當用戶點擊確認按鈕調用了startInstall方法,啓動子Activity完成Apk的安裝
private void startInstall() {
// 啓動子Activity來完成應用的安
Intent newIntent = new Intent();
newIntent.putExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO,
mPkgInfo.applicationInfo);
newIntent.setData(mPackageURI);
newIntent.setClass(this, InstallInstalling.class);
...
if(localLOGV) Log.i(TAG, "downloaded app uri="+mPackageURI);
startActivity(newIntent);
finish();
}
複製代碼
startInstall方法用來跳轉到InstallInstalling,並關閉掉當前的PackageInstallerActivity
主要工做:
InstallInstalling首先向包管理器發送包的信息,而後等待包管理器處理結果,並在方法InstallSuccess 和 方法InstallFailed 進行成功和失敗的處理,查看InstallInstalling的onCreate方法: frameworks/base/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
// 判斷安裝的應用是否已經存在
if ("package".equals(mPackageURI.getScheme())) {
try {
getPackageManager().installExistingPackage(appInfo.packageName);
launchSuccess();
} catch (PackageManager.NameNotFoundException e) {
launchFailure(PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null);
}
} else {
final File sourceFile = new File(mPackageURI.getPath());
PackageUtil.AppSnippet as = PackageUtil.getAppSnippet(this, appInfo, sourceFile);
...
if (savedInstanceState != null) {
// 若是savedInstanceState 不爲空,獲取已經存在mSessionId 和mInstallId 從新註冊
mSessionId = savedInstanceState.getInt(SESSION_ID);
mInstallId = savedInstanceState.getInt(INSTALL_ID);
try {
// 根據mInstallId向InstallEventReceiver註冊一個觀察者,launchFinishBasedOnResult會接收到安裝事件的回調
InstallEventReceiver.addObserver(this, mInstallId,
this::launchFinishBasedOnResult);
} catch (EventResultPersister.OutOfIdsException e) {
}
} else {
// 若是爲空建立SessionParams,表明安裝會話的參數
// 解析Apk, 並將解析的參數賦值給SessionParams
PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
PackageInstaller.SessionParams.MODE_FULL_INSTALL);
...
try {
// 註冊InstallEventReceiver,並在launchFinishBasedOnResult會接收到安裝事件的回調
mInstallId = InstallEventReceiver
.addObserver(this, EventResultPersister.GENERATE_NEW_ID,
this::launchFinishBasedOnResult);
} catch (EventResultPersister.OutOfIdsException e) {
launchFailure(PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null);
}
try {
// createSession 內部經過IPackageInstaller與PackageInstallerService進行進程間通訊,
// 最終調用的是PackageInstallerService的createSession方法來建立並返回mSessionId
mSessionId = getPackageManager().getPackageInstaller().createSession(params);
} catch (IOException e) {
launchFailure(PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null);
}
}
...
}
}
複製代碼
protected void onResume() {
super.onResume();
if (mInstallingTask == null) {
PackageInstaller installer = getPackageManager().getPackageInstaller();
// 根據mSessionId 獲取SessionInfo, 表明安裝會話的詳細信息
PackageInstaller.SessionInfo sessionInfo = installer.getSessionInfo(mSessionId);
if (sessionInfo != null && !sessionInfo.isActive()) {
mInstallingTask = new InstallingAsyncTask();
mInstallingTask.execute();
} else {
// 安裝完成後會收到廣播
mCancelButton.setEnabled(false);
setFinishOnTouchOutside(false);
}
}
}
複製代碼
獲得SessionInfo建立並建立InstallingAsyncTask,InstallingAsyncTask的doInBackground方法設置安裝進度條,並將Apk信息寫入PackageInstaller.Session,寫入完成以後,在InstallingAsyncTask的onPostExecute進行成功與失敗的處理,接着查看onPostExecute方法
protected void onPostExecute(PackageInstaller.Session session) {
if (session != null) {
Intent broadcastIntent = new Intent(BROADCAST_ACTION);
broadcastIntent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
broadcastIntent.setPackage(getPackageName());
broadcastIntent.putExtra(EventResultPersister.EXTRA_ID, mInstallId);
PendingIntent pendingIntent = PendingIntent.getBroadcast(
InstallInstalling.this,
mInstallId,
broadcastIntent,
PendingIntent.FLAG_UPDATE_CURRENT);
session.commit(pendingIntent.getIntentSender());
mCancelButton.setEnabled(false);
setFinishOnTouchOutside(false);
} else {
getPackageManager().getPackageInstaller().abandonSession(mSessionId);
if (!isCancelled()) {
launchFailure(PackageManager.INSTALL_FAILED_INVALID_APK, null);
}
}
}
複製代碼
建立了broadcastIntent,並經過PackageInstaller.Session的commit方法發送出去,經過broadcastIntent構造方法指定的Intent的Action爲BROADCAST_ACTION,而BROADCAST_ACTION是一個常量值
private static final String BROADCAST_ACTION =
"com.android.packageinstaller.ACTION_INSTALL_COMMIT";
複製代碼
回到InstallInstalling.OnCreate方法,在OnCreate方法註冊InstallEventReceiver,而InstallEventReceiver繼承自BroadcastReceiver,而使用BroadcastReceiver須要在AndroidManifest.xml註冊,接着查看AndroidManifest.xml: /frameworks/base/packages/PackageInstaller/AndroidManifest.xml
<receiver android:name=".InstallEventReceiver"
android:permission="android.permission.INSTALL_PACKAGES"
android:exported="true">
<intent-filter android:priority="1">
<action android:name="com.android.packageinstaller.ACTION_INSTALL_COMMIT" />
</intent-filter>
</receiver>
複製代碼
安裝結束以後,會在觀察者InstallEventReceiver註冊的回調方法launchFinishBasedOnResult處理安裝事件的結果,接着查看launchFinishBasedOnResult
private void launchFinishBasedOnResult(int statusCode, int legacyStatus, String statusMessage) {
if (statusCode == PackageInstaller.STATUS_SUCCESS) {
launchSuccess();
} else {
launchFailure(legacyStatus, statusMessage);
}
}
private void launchSuccess() {
Intent successIntent = new Intent(getIntent());
successIntent.setClass(this, InstallSuccess.class);
successIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
startActivity(successIntent);
finish();
}
private void launchFailure(int legacyStatus, String statusMessage) {
Intent failureIntent = new Intent(getIntent());
failureIntent.setClass(this, InstallFailed.class);
failureIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
failureIntent.putExtra(PackageInstaller.EXTRA_LEGACY_STATUS, legacyStatus);
failureIntent.putExtra(PackageInstaller.EXTRA_STATUS_MESSAGE, statusMessage);
startActivity(failureIntent);
finish();
}
複製代碼
安裝成功和失敗,都會啓動一個新的Activity(InstallSuccess、InstallFailed)將結果展現給用戶,而後finish掉InstallInstalling
總結一下PackageInstaller安裝APK的過程:
在包android.content.pm下
在包android.content.pm下
在包android.content下
在包com.android.server.pm下
到了這裏整個Apk的安裝過程結束了,另外在系統目錄 「/data/system」下有個packages.xml,記錄了系統中全部安裝的應用信息,瞭解這個文件對咱們後續在開發中會有很大的幫助
在Andorid系統目錄 「/data/system」 下保存不少系統文件,主要介紹packages.xml文件
系統啓動的時候會經過PackageManagerServcie讀取這個文件加載系統中全部安裝的應用,這個文件在開發中也是很是有幫助的,不一樣廠商會對Android源碼有不一樣的修改,若是咱們須要分析系統App的源碼,就經過這個packages.xml找到目標Apk,dump出來分析源碼
如下是packages.xml文件部份內容
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<packages>
<version sdkVersion="27" databaseVersion="3" fingerprint="Meizu/meizu_M1822_CN/M1822:8.1.0/OPM1.171019.026/1539943691:user/release-keys" />
<version volumeUuid="primary_physical" sdkVersion="27" databaseVersion="27" fingerprint="Meizu/meizu_M1822_CN/M1822:8.1.0/OPM1.171019.026/1539943691:user/release-keys" />
<meizu_version meizu_fingerprint="8.1.0-1541573178_stable" />
<permission-trees />
<permissions>
<item name="com.meizu.voiceassistant.push.permission.MESSAGE" package="com.meizu.voiceassistant" protection="2" />
<item name="com.meizu.safe.alphame.permission.DATA" package="com.meizu.safe" protection="18" />
<item name="android.permission.REAL_GET_TASKS" package="android" protection="18" />
......
<item name="android.permission.MODIFY_PHONE_STATE" granted="true" flags="0" />
<item name="com.android.launcher.permission.INSTALL_SHORTCUT" granted="true" flags="0" />
<item name="android.permission.WAKE_LOCK" granted="true" flags="0" />
</perms>
<proper-signing-keyset identifier="1" />
</package>
<package name="com.android.providers.telephony" codePath="/system/priv-app/TelephonyProvider" nativeLibraryPath="/system/priv-app/TelephonyProvider/lib" primaryCpuAbi="arm64-v8a" publicFlags="1007402501" privateFlags="8" ft="11e8dc5d800" it="11e8dc5d800" ut="11e8dc5d800" version="27" sharedUserId="1001" isOrphaned="true" forceFull="true">
<sigs count="1">
<cert index="0" />
</sigs>
<perms>
<item name="android.permission.SEND_RECEIVE_STK_INTENT" granted="true" flags="0" />
<item name="android.permission.BIND_INCALL_SERVICE" granted="true" flags="0" />
......
<item name="android.permission.UPDATE_APP_OPS_STATS" granted="true" flags="0" />
</perms>
<proper-signing-keyset identifier="1" />
</package>
複製代碼
aospxref是weishu大神搭建一個在線查看在線查看Android源碼網站, 訪問速度很是快
在這以前我經常使用的在線查看Android源碼的網站androidxref,訪問速度不只慢,並且更新也不及時,如今Android 10發佈了,這個網站到如今提供的最新的代碼仍是Andorid 9
aospxref提供了與 androidxref 徹底同樣的源碼瀏覽和交叉索引功能;除此以外,它還有一些別的優勢:
關於更多詳細的信息能夠點擊這裏閱讀原文
致力於分享一系列的Android系統源碼、逆向分析、算法相關的文章,每篇文章都會反覆檢查以後纔會發表出來,若是你同我同樣喜歡研究Android源碼,一塊兒來學習,期待與你一塊兒成長