preface
- This is the second article in the Android 10 source code analysis series
- Branches: android – 10.0.0 _r14
- Read the full text for about 10 minutes
In Android 10 and higher, the installer PackageInstaller source code has been changed. In Android 10 and higher, the installer source code has been changed
PackageInstaller source code location
PackageInstaller is a built-in application used to install and uninstall applications
In Android 9 and a lower version, the package installation and access control functions included in PackageInstaller package (/ / packages/apps/PackageInstaller). 10 and higher in the Android version, the access control function is located in a separate package PermissionController (/ / packages/apps/PermissionController), The location of these two packages in Android 10 is shown below. For more information, click here to go to Android Permissions
In Android 9 and later:
Package installation and access control function source code path: packages/apps/PackageInstaller
Android 10 and later:
- Access control function PermissionController source path: packages/apps/PermissionController /
- Installer PackageInstaller source path: frameworks/base/packages/PackageInstaller /
Android stores different types of applications in different directories
- /system/framwork: Stores resource-based applications that are used to package resource files
- /system/app: saves the built-in applications of the system
- /data/app: saves the applications installed by the user
- /data/data: indicates the application data directory
- /data/app-private: saves DRM protected private applications
- /vendor/app: saves the application provided by the device vendor
View the PackageInstaller source code
- Aosc-packageinstaller: Contains the source code of the installer PackageInstaller(7.1.2, 8.1.0, 9.0.0, 10.0.0), which can be viewed with a switch. You can always see the latest source code with the Android version update
- Aospxref: this is an online view of Android source website, the server in Ali Cloud access speed is very fast, at the end of the article about this website introduction
- Googlesource -PackageInstaller: This is the address of the installer PackageInstaller on Googlesource
1. Installation method of APK
APK installation can be performed in the following scenarios
- After the installation system application: system invoked PackageManagerService. The main () to initialize the register analytical installation work
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;
}
Copy the code
- Adb Installation: Through the PM parameter, call the runInstall method of PM, enter the PackageManagerService installation work
- Install using the System installer PackageInstaller: Start PackageInstallActivity by calling the startInstall method of PackageInstallActivity. Click OK to enter PackageManagerService to complete copy parsing installation
All installation methods are roughly the same, and eventually you go back to PackageManagerService. The general process of installing an APK is as follows:
- Copy APK files to the specified directory
- Decompress APK, copy files, and create an application data directory
- Parse the androidmanifest.xml file of APK
- Apply for a create shortcut to the Launcher application
This article mainly analyzes the APK installation through the installer PackageInstaller, which is the most commonly used by users
2. Access the PackageInstaller
The following code should be familiar, and this is the code we usually use to install APK (PS: SILENT installation will be shared in a future article on reverse development).
Intent intent = new Intent(Intent.ACTION_VIEW); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); /* * Since Android N, the File provider is used to share related files, but Android Q has restricted the public * directory File API. */ if(build.version.sdk_int >= build.version_codes.q){// filePath is obtained by 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); / / need to add permissions in AndroidManifest < USES - permission android: name = "android. Permission. REQUEST_INSTALL_PACKAGES" / >Copy the code
Through intent. SetDataAndType method to specify intent data type to application/VND. Android. Package – archive, implicit InstallStart matching Activity to: 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>Copy the code
- In 8.0, 9.0, 10.0, etc., implicitly matched Activity is InstallStart, and 7.0 implicitly matched Activity is PackageInstallerActivity
- The entry Activity of the PackageInstaller is InstallStart, which defines two schemes: Content and Package
3. APK installation process
Now let’s see how APK is installed
3.1 InstallStart
Main Work:
- Check whether unknown source is selected. If not, the page for setting unknown source is displayed
- If the Android version is greater than or equal to 8.0, the system checks whether the installation permission is applied for. If the installation permission is not applied, the system interrupts the installation
- Determine the Scheme protocol of the Uri and call InstallStaging in the case of Content or PackageInstallerActivity in the case of package
When we call the setup code above to install APK. Jumps to InstallStart and calls its onCreate method: 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); If (targetSdkVersion < 0) {log.w (LOG_TAG, "Cannot get target sdk version for uid " + originatingUid); mAbortInstall = true; // If targetSdkVersion is greater than or equal to 26 (8.0), } 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; }}... // If ACTION_CONFIRM_PERMISSIONS is set, PackageInstallerActivity is called. if (isSessionInstall) { nextActivity.setClass(this, PackageInstallerActivity.class); } else { Uri packageUri = intent.getData(); If (packageUri! = null && packageUri.getScheme().equals( ContentResolver.SCHEME_CONTENT)) { // [IMPORTANT] This path is deprecated, // Call InstallStaging to copy file/content. Nextactivity.setclass (this, InstallStaging. Class); } else if (packageUri ! = null && packageUri. GetScheme (.) the equals (PackageInstallerActivity. SCHEME_PACKAGE)) {/ / if the Uri contains package, Call the PackageInstallerActivity nextActivity. SetClass (this, PackageInstallerActivity. Class); 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(); }Copy the code
If content is content, call InstallStaging. Check the InstallStaging onResume method at any time. frameworks/base/packages/PackageInstaller/src/com/android/packageinstaller/InstallStaging.java
@Override protected void onResume() { super.onResume(); If (mStagingTask == null) {if (mStagedFile == null) {// Create a temporary file mStagedFile to store data try {mStagedFile = TemporaryFileManager.getStagedFile(this); } catch (IOException e) { showError(); return; MStagingTask = new StagingAsyncTask(); mStagingTask = new StagingAsyncTask(); mStagingTask.execute(getIntent().getData()); }}Copy the code
- Create a temporary mStagedFile to store data
- StagingAsyncTask is started and the Uri of the Content protocol is passed in
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)) { ... 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(); }}}Copy the code
- The doInBackground method writes the contents of the packageUri (Uri of the Content protocol) to the mStagedFile
- If the write succeeds, call the OnCreate method of DeleteStagedFileOnResult:
frameworks/base/packages/PackageInstaller/src/com/android/packageinstaller/DeleteStagedFileOnResult.java
protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); If (savedInstanceState == null) {// Start PackageInstallerActivity Intent installIntent = new Intent(getIntent()); installIntent.setClass(this, PackageInstallerActivity.class); installIntent.setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION); startActivityForResult(installIntent, 0); }}Copy the code
After analysis, InstallStaging mainly plays a transfer role. It converts the Uri of content protocol to File protocol, and finally jumps to PackageInstallerActivity
3.2 PackageInstallerActivity
Main Work:
- Display the installation page
- Initialize the installation of various objects, such as PackageManager, IPackageManager, AppOpsManager, UserManager, PackageInstaller, and so on
- Different processing is done according to the Scheme protocol that is passed
- Check whether the installation is allowed and initialized
- Before the installation, check the application list to determine whether the application is installed. If yes, a message is displayed indicating that the application is installed. The user decides whether to replace the application
- On the installation screen, extract permission information from APK and display it
- After clicking the OK button to confirm the installation, startInstall is called to begin the installation
PackageInstallerActivity is the actual entry to the PackageInstaller. Check its onCreate method: 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); Boolean wasSetUp = processPackageUri(packageUri); if (! wasSetUp) { return; } // display the installation interface bindUi(); // Check whether installation packages are allowed and start the installation if so. If not allowed to display the appropriate dialog checkIfAllowedAndInitiateInstall (); }Copy the code
Initialization of objects, Scheme for Uri resolution, initialization interface, installation package check and so on, then look at the processPackageUri method
private boolean processPackageUri(final Uri packageUri) { mPackageURI = packageUri; final String scheme = packageUri.getScheme(); Switch (Scheme) {case SCHEME_PACKAGE: {the try {/ / specify the package name package information captured by the PackageManager object 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: {// Create a new File according to packageUri File sourceFile = new File(packageuri.getPath ()); // Parse APK to get information about APK, PackageParser. All of the information Package to store the 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; } // APK from packageParser. Package, Generate 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; }Copy the code
The package protocol and file protocol are processed respectively by Scheme protocol
SCHEME_PACKAGE:
- In package agreement calls the PackageManager. GetPackageInfo method generates PackageInfo, PackageInfo is the package data (Activities, Receivers, Services, Providers, Permissions, and so on) passed across processes that contain all APK information
SCHEME_FILE:
- In the file protocol processing calls the PackageUtil. GetPackageInfo method, Within a method call PackageParser. ParsePackage () put the APK manifest and signature information parsing is complete and stored in the Package, the Package contains all the information that the APK
- Call PackageParser generatePackageInfo PackageInfo generated
Then go down, after parsing is complete, return to the onCreate method, continue to call checkIfAllowedAndInitiateInstall method
Private void checkIfAllowedAndInitiateInstall () {/ / first checks to install the application user limits, If there is a limit and popup hint Dialog pops up or jump to set the final interface 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; } / / determine if allowed to install the unknown sources or in accordance with the Intent to judge the APK is not unknown source if (mAllowUnknownSources | |! isInstallRequestFromUnknownSource(getIntent())) { initiateInstall(); } else {/ / to check if the unknown restriction on the installation source, restricted the pop-up Dialog, display the corresponding information 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 {// Handle unknown APK handleUnknownSources(); }}}Copy the code
If the APK file is not correct or the installation is limited, the showDialogInner method will be called, and the dialog will be displayed to prompt the user
// Dialog identifiers used in showDialog private static final int DLG_BASE = 0; Private static final int DLG_PACKAGE_ERROR = DLG_BASE + 2; 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_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; Private static final int DLG_NOT_SUPPORTED_ON_WEAR = DLG_BASE + 7; 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;Copy the code
If the user allows an unknown source to be installed, the initiateInstall method is called
private void initiateInstall() { String pkgName = mPkgInfo.packageName; / / check whether there is the same on the equipment package name 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; } // Check whether the package is installed, if so, a dialog box will be displayed to prompt the user whether to replace. try { mAppInfo = mPm.getApplicationInfo(pkgName, PackageManager.MATCH_UNINSTALLED_PACKAGES); if ((mAppInfo.flags&ApplicationInfo.FLAG_INSTALLED) == 0) { mAppInfo = null; } } catch (NameNotFoundException e) { mAppInfo = null; } // Initialize the installation confirmation screen startInstallConfirm(); }Copy the code
After getting information about the application based on the package name, calling the startInstallConfirm method to initialize the installation confirmation screen, what happens when the user clicks the confirm button and then sees the confirm button click event
private void bindUi() { ... // Click ok, Install APK malert. setButton(dialogInterface.button_positive, getString(r.tring.install), (ignored, ignored2) -> { if (mOk.isEnabled()) { if (mSessionId ! = -1) { mInstaller.setPermissionsResult(mSessionId, true); finish(); } else {// Start the Activity to complete the app installation startInstall(); } } }, null); // Click cancel button, 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); }Copy the code
When the user clicks ok to invoke the startInstall method, the child Activity is launched to complete the APK installation
Private void startInstall() {// Start a child Activity to complete the application Intent newIntent = newIntent (); 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(); }Copy the code
The startInstall method is used to jump to InstallInstalling and turn off the current PackageInstallerActivity
3.3 InstallInstalling
Main Work:
- Send the package information to the package manager and wait for the package manager to process the result
- Sign up for an observer InstallEventReceiver and receive a successful and failed setup callback
- Create a sync stack in onResume, open setup Session, and set the setup progress bar
InstallInstalling first sends the package information to the package manager, then waits for the package manager to process the result, and processes the success and failure in methods InstallSuccess and InstallFailed. Check the onCreate method of InstallInstalling: 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 ! // If savedInstanceState is not null, For existing mSessionId and mInstallId register mSessionId = savedInstanceState. Get int (SESSION_ID); mInstallId = savedInstanceState.getInt(INSTALL_ID); // Register an observer with InstallEventReceiver according to mInstallId, LaunchFinishBasedOnResult receives to install event callback InstallEventReceiver. The addObserver (this, mInstallId, this::launchFinishBasedOnResult); } the catch (EventResultPersister OutOfIdsException e) {}} else {/ / create SessionParams if is empty, // Parses APK, And will parse the parameters of the assigned to SessionParams PackageInstaller. SessionParams params = new PackageInstaller. SessionParams ( PackageInstaller.SessionParams.MODE_FULL_INSTALL); . // Register InstallEventReceiver, And installed in launchFinishBasedOnResult will receive the event callback mInstallId = InstallEventReceiver. AddObserver (this, EventResultPersister.GENERATE_NEW_ID, this::launchFinishBasedOnResult); } catch (EventResultPersister.OutOfIdsException e) { launchFailure(PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null); } try {// createSession communicates internally with PackageInstallerService via IPackageInstaller. // The PackageInstallerService createSession method is called to create and return mSessionId mSessionId = getPackageManager().getPackageInstaller().createSession(params); } catch (IOException e) { launchFailure(PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null); }}... }}Copy the code
- Eventually will register an observer InstallEventReceiver, and installed in launchFinishBasedOnResult will receive event callback, including InstallEventReceiver inherited from BroadcastReceiver, Used to receive installation events and call back to EventResultPersister
- CreateSession internally communicates with the PackageInstallerService through the IPackageInstaller. The createSession method of PackageInstallerService is called to create and return the mSessionId
- Next create InstallingAsyncTask in the onResume method to perform the APK installation, and then look at the onResume method
protected void onResume() { super.onResume(); if (mInstallingTask == null) { PackageInstaller installer = getPackageManager().getPackageInstaller(); // Get SessionInfo according to mSessionId, For detailed information installation session PackageInstaller. SessionInfo SessionInfo = installer. GetSessionInfo (mSessionId); if (sessionInfo ! = null && ! sessionInfo.isActive()) { mInstallingTask = new InstallingAsyncTask(); mInstallingTask.execute(); } else {McAncelbutton.setenabled (false);} else {McAncelbutton.setenabled (false); setFinishOnTouchOutside(false); }}}Copy the code
The InstallingAsyncTask doInBackground method sets the installation progress bar. Write the APK information to PackageInstaller.Session. After that, check the success and failure of InstallingAsyncTask onPostExecute and check the onPostExecute method
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);
}
}
}
Copy the code
Create the broadcastIntent and send it via the Commit method of PackageInstaller.Session. The Intent constructor specifies an Intent Action as BROADCAST_ACTION, which is a constant value
private static final String BROADCAST_ACTION =
"com.android.packageinstaller.ACTION_INSTALL_COMMIT";
Copy the code
Back to InstallInstalling OnCreate method, registered in the OnCreate method InstallEventReceiver, while InstallEventReceiver inherited from BroadcastReceiver, To use BroadcastReceiver, you need to register with 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>
Copy the code
After installation, you will be registered in observer InstallEventReceiver callback methods launchFinishBasedOnResult processing installation of events as a result, and then view the 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();
}
Copy the code
A new Activity (InstallSuccess, InstallFailed) will be started to show the results to the user, and then finish to remove InstallInstalling
4. To summarize
To summarize the process of installing APK with PackageInstaller:
- Find the entry InstallStart according to the Scheme Uri
- InstallStart does different processing based on the Scheme protocol of the Uri
- Will be called PackageInstallerActivity, then respectively for package agreement and file Uri for processing
- PackageInstallerActivity Checks for unknown installation source limits and displays a Dialog if they are installed
- After clicking the OK button to confirm the installation, startInstall is called to begin the installation
- If the user allows the installation, then go to InstallInstalling and start the APK installation
- In InstallInstalling, you send the package information to the package manager, then register an observer InstallEventReceiver and receive a successful and failed installation callback
5. About packages. The XML
The Andorid system directory “/data/system” saves many system files, mainly introduces the packs.xml file
- Packes. XML: records all installed application information in the system, including basic information, signatures and permissions, APK file paths, and native library storage paths
This file is also very helpful in the development process. Different manufacturers will have different modifications to the Android source code. If we need to analyze the source code of the system App, XML to find the target APK and dump the source code for analysis
The following is part of the packes.xml file
<? 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 - the keys" / > < version volumeUuid="primary_physical" sdkVersion="27" databaseVersion="27" Fingerprint = "Meizu/meizu_M1822_CN/M1822:8.1.0 / OPM1.171019.026/1539943691: user/release - the 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>Copy the code
5.1. Package Indicates package information
- Name Indicates the application package name
- CodePath represents the path to the APK file
- NativeLibraryPath Indicates the storage path of the application native library
- It indicates when the application was installed
- Ut indicates the time when the last change was applied
- Version Indicates the version number of an application
- UserId Indicates the ID to which the user belongs
5.2. Sign Indicates the application signature
- Count indicates how many certificates are contained in the tag
- Cert indicates the value of the specific certificate
5.3. Perms indicates the permission that the application claims to use. Each sublabel represents a permission
6. Amway is an online source site for Android
Aospxref is a website built by Weishu to view Android source code online, access very fast
Before this, I used to check Android source code online website AndroidXref, access speed is not only slow, and the update is not timely, now Android 10 released, this website provides the latest code is Andorid 9
Aospxref provides the same source browsing and cross-indexing functionality as AndroidXREF; In addition, it has some other advantages:
- Follow the Android version updates, you can always see the latest source code.
- The server is in Ali Cloud, and the domestic access speed is fast.
- Opengrok is of a higher version, and there is an automatic prompt when you check the code.
- Partial optimization of the page, more convenient to use; For example, you can jump to the home page from any interface.
reference
- Android package management summary
- Amway a look at the Android source code site
- The Android permission
- (1) Initialization of the PackageInstaller
conclusion
Dedicated to share a series of Android system source code, reverse analysis, algorithm, translation, Jetpack source code related articles, is trying to write a better article, if this article is helpful to you to give a star, what is not written in the article clearly, or have better advice, welcome to leave a message, welcome to learn together, Moving forward together on the technological road.
Plan to establish a most complete and latest AndroidX Jetpack related components of the actual combat project and related components of the principle of analysis article, is gradually increasing Jetpack new members, the warehouse continues to update, you can go to check: Androidx-jetpack-practice, if this warehouse is helpful to you, please give me a thumbs up and I will finish more project practices for new members of Jetpack one after another.
algorithm
Since LeetCode has a large question bank, hundreds of questions can be selected for each category. Due to the limited energy of each person, it is impossible to brush all the questions. Therefore, I sorted the questions according to the classic types and the difficulty of the questions.
- Data structures: arrays, stacks, queues, strings, linked lists, trees…
- Algorithms: Search algorithm, search algorithm, bit operation, sorting, mathematics,…
Each problem will be implemented in Java and Kotlin, and each problem has its own solution ideas, time complexity and space complexity. If you like algorithms and LeetCode like me, you can pay attention to my LeetCode problem solution on GitHub: Leetcode-Solutions-with-Java-And-Kotlin, come to learn together And look forward to growing with you.
Android 10 source code series
I’m writing a series of Android 10 source code analysis articles. Knowing the system source code is not only helpful in analyzing problems, but also very helpful in the interview process. If you like to study Android source code as MUCH as I do, You can follow my Android10-source-Analysis on GitHub, and all articles will be synchronized to this repository.
- How is APK generated
- APK installation process
- 0xA03 Android 10 source code analysis: APK loading process of resource loading
- Android 10 source code: APK
- Dialog loading and drawing process and use in Kotlin, DataBinding
- More……
Tool series
- Shortcuts to AndroidStudio that few people know
- Shortcuts to AndroidStudio that few people know
- All you need to know about ADB commands
- How to get video screenshots efficiently
- 10 minutes introduction to Shell scripting
- How to package Kotlin + Android Databinding in your project
The reverse series
- Dynamically debug APP based on Smali file Android Studio
- The Android Device Monitor tool cannot be found in Android Studio 3.2