androidP: apk安装流程

article/2025/11/8 7:24:19

一、Android应用安装有如下四种方式
1 系统应用安装――开机时完成,没有安装界面
2.网络下载应用安装――通过market应用完成,没有安装界面
3.ADB工具安装――没有安装界面。
4. 第三方应用安装――通过SD卡里的APK文件安装,有安装界面,由packageinstaller.apk应用处理安装及卸载过程的界面。
在这里插入图片描述

二、安装相关
1、应用安装的流程及路径
(1)应用安装涉及到如下几个目录:
system/app
系统自带的应用程序,无法删除

(2)data/app
用户程序安装的目录,有删除权限。

(3)安装时把apk文件复制到此目录

(4)data/data
存放应用程序的数据

(5)Data/dalvik-cache
将apk中的dex文件安装到dalvik-cache目录下(dex文件是dalvik虚拟机的可执行文件,其大小约为原始apk文件大小的四分之一)

2、安装过程:复制APK安装包到data/app目录下,解压并扫描安装包,把dex文件(Dalvik字节码)保存到dalvik-cache目录,并data/data目录下创建对应的应用数据目录。

3、 卸载过程:删除安装过程中在上述三个目录下创建的文件及目录。

三、代码调用流程(第三方应用安装)
1、安装apk应用代码:

  //MainActivity.javavoid install thirdAPK() {Log.d(TAG,"path===========" + this.getFilesDir());File file = new File(this.getFilesDir() + "/apk"+"/AlgoTestSigned.apk");Log.d(TAG,"file.path ============" + file.getAbsolutePath());Intent intent_apk = new Intent(Intent.ACTION_VIEW);if (Build.VERSION.SDK_INT > 24) {//大于7.0使用此方法Log.d(TAG,"testtest ===== sdk > 24");Uri apkUri = FileProvider.getUriForFile(this, "com.htkj.photo.fileprovider", file);intent_apk.setDataAndType(apkUri, "application/vnd.android.package-archive");} else {Log.d(TAG,"testtest ===== sdk < 24");// 由于没有在Activity环境下启动Activity,设置下面的标签intent_apk.setDataAndType(Uri.fromFile(file),"application/vnd.android.package-archive");}intent_apk.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);intent_apk.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);startActivity(intent_apk);
}//AndroidMenifest.xml
<manifest>...<application>...<activity android:name=".MainActivity"android:label="@string/app_name"android:launchMode="singleTask"android:theme="@style/AppTheme.NoActionBar"><intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER" /></intent-filter></activity><provider<!--android:authorities="包名.fileprovider"-->android:authorities="com.htkj.photo.fileprovider"android:name="androidx.core.content.FileProvider"android:exported="false"android:grantUriPermissions="true"><meta-dataandroid:name="android.support.FILE_PROVIDER_PATHS"android:resource="@xml/path"></meta-data></provider>...</application>...<!--安装第三方apk,报错日志com.android.packageinstaller E/InstallStart: Requesting uid 10105 needs to declare permission android.permission.REQUEST_INSTALL_PACKAGES,添加下面权限--><uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
</manifest>//在ret下面新建xml文件夹,新建path.xml文件
<?xml version="1.0" encoding="utf-8"?>
<paths><files-pathname="/AlgoTestSigned.apk"path="/apk"></files-path>
</paths>

path.xml文件注意问题:
(1)name字段应该可以随便定义(不确定)。
(2)path定义需要根据待装apk路径定义。例如,我的待装apk放到的完成路径为
/data/user/0/com.htkj.photo/files/apk/AlgoTestSigned.apk,标签files-path 的路径与java代码中 this.getFilesDir()获取的值相同均为“ /data/user/0/com.htkj.photo/files”,因此path至为“/apk”补全路径。
所有path标签如下:

<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:android="http://schemas.android.com/apk/res/android"><paths><external-pathname="external_storage_root"path=""><!--Environment.getExternalStorageDirectory()--><!--/storage/emulated/0--><!--存储的根目录--></external-path><files-path><!--context.getFilesDir()--><!--内部存储的files目录--><!--/data/user/0/com.stone.testdemo/files--></files-path><cache-path><!--context.getCacheDir()--><!--内部存储的cache目录--><!--/data/user/0/com.stone.testdemo/cache--></cache-path><external-files-path><!--ContextCompat.getExternalFilesDirs--><!--外部私有存储的files目录--><!--/storage/emulated/0/Android/data/com.stone.testdemo/files--></external-files-path><external-cache-path><!--ContextCompat.getExternalCacheDirs--><!--外部私有存储的cache目录--><!--/storage/emulated/0/Android/data/com.stone.testdemo/cache--></external-cache-path><external-media-path><!--context.getExternalMediaDir--><!--/storage/emulated/0/Android/media/com.stone.testdemo--></external-media-path></paths>
</resources>

开始安装后,首先进入PackageInstallerActivity界面

2021-12-22 09:27:18.744 5175-5729/system_process I/ActivityTaskManager: Displayed com.android.packageinstaller/.InstallStaging: +199ms
2021-12-22 09:27:18.783 14141-14141/com.android.packageinstaller W/ActivityThread: handleWindowVisibility: no activity for token android.os.BinderProxy@863c479
2021-12-22 09:27:18.796 5175-9721/system_process I/ActivityTaskManager: START u0 {act=android.intent.action.VIEW dat=file:///data/user_de/0/com.android.packageinstaller/no_backup/package6786596049368288088.apk flg=0x10000 cmp=com.android.packageinstaller/.PackageInstallerActivity (has extras)} from uid 10037

在这里插入图片描述
2、升级流程
上述代码运行之后,跳转到PackageInstallerActivity界面,代码首先跳转到InstallStaging.java类中

// ../packages/apps/PackageInstaller/src/com/android/packageinstalle/InstallStaging.java
/*** If a package gets installed from an content URI this step loads the package and turns it into* and installation from a file. Then it re-starts the installation as usual.*/
public class InstallStaging extends Activity {private static final String LOG_TAG = InstallStaging.class.getSimpleName();private static final String STAGED_FILE = "STAGED_FILE";/** Currently running task that loads the file from the content URI into a file */private @Nullable StagingAsyncTask mStagingTask;/** The file the package is in */private @Nullable File mStagedFile;@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.install_staging);if (savedInstanceState != null) {mStagedFile = new File(savedInstanceState.getString(STAGED_FILE));if (!mStagedFile.exists()) {mStagedFile = null;}}findViewById(R.id.cancel_button).setOnClickListener(view -> {if (mStagingTask != null) {mStagingTask.cancel(true);}setResult(RESULT_CANCELED);finish();});}@Overrideprotected void onResume() {super.onResume();// This is the first onResume in a single life of the activityif (mStagingTask == null) {// File does not exist, or became invalidif (mStagedFile == null) {// Create file delayed to be able to show errortry {//这里不懂,后面分析mStagedFile = TemporaryFileManager.getStagedFile(this);} catch (IOException e) {showError();return;}}mStagingTask = new StagingAsyncTask();mStagingTask.execute(getIntent().getData());}}@Overrideprotected void onSaveInstanceState(Bundle outState) {super.onSaveInstanceState(outState);outState.putString(STAGED_FILE, mStagedFile.getPath());}@Overrideprotected void onDestroy() {if (mStagingTask != null) {mStagingTask.cancel(true);}super.onDestroy();}/*** Show an error message and set result as error.*/private void showError() {(new ErrorDialog()).showAllowingStateLoss(getFragmentManager(), "error");Intent result = new Intent();result.putExtra(Intent.EXTRA_INSTALL_RESULT,PackageManager.INSTALL_FAILED_INVALID_APK);setResult(RESULT_FIRST_USER, result);}/*** Dialog for errors while staging.*/public static class ErrorDialog extends DialogFragment {private Activity mActivity;@Overridepublic void onAttach(Context context) {super.onAttach(context);mActivity = (Activity) context;}@Overridepublic Dialog onCreateDialog(Bundle savedInstanceState) {AlertDialog alertDialog = new AlertDialog.Builder(mActivity).setMessage(R.string.Parse_error_dlg_text).setPositiveButton(R.string.ok,(dialog, which) -> mActivity.finish()).create();alertDialog.setCanceledOnTouchOutside(false);return alertDialog;}@Overridepublic void onCancel(DialogInterface dialog) {super.onCancel(dialog);mActivity.finish();}}private final class StagingAsyncTask extends AsyncTask<Uri, Void, Boolean> {@Overrideprotected Boolean doInBackground(Uri... params) {if (params == null || params.length <= 0) {return false;}Uri packageUri = params[0];try (InputStream in = getContentResolver().openInputStream(packageUri)) {// Despite the comments in ContentResolver#openInputStream the returned stream can// be null.if (in == null) {return false;}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 cancellationif (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;}@Overrideprotected void onPostExecute(Boolean success) {if (success) {// Now start the installation again from a fileIntent installIntent = new Intent(getIntent());//打开DeleteStagedFileOnResult.java类installIntent.setClass(InstallStaging.this, DeleteStagedFileOnResult.class);installIntent.setData(Uri.fromFile(mStagedFile));if (installIntent.getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false)) {installIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);}installIntent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);startActivity(installIntent);InstallStaging.this.finish();} else {showError();}}}
}// ../packages/apps/PackageInstaller/src/com/android/packageinstaller/DeleteStagedFileOnResult.java
public class DeleteStagedFileOnResult extends Activity {@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);if (savedInstanceState == null) {Intent installIntent = new Intent(getIntent());//透传intent打开PackageInstallerActivity.java类installIntent.setClass(this, PackageInstallerActivity.class);installIntent.setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);startActivityForResult(installIntent, 0);}}@Overrideprotected void onActivityResult(int requestCode, int resultCode, Intent data) {File sourceFile = new File(getIntent().getData().getPath());sourceFile.delete();setResult(resultCode, data);finish();}
}

InstallStaging.java中主要工作:开启AsyncTask 读取 apk文件,然后 启动了activity DeleteStagedFileOnResult,这是个跳板页面,里面直接透传 intent 启动了 PackageInstallerActivity。

// ../frameworks/base/core/java/android/content/pm/PackageInstaller.java
//../packages/apps/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
@Overrideprotected void onCreate(Bundle icicle) {super.onCreate(null);...//解析apkboolean wasSetUp = processPackageUri(packageUri);if (!wasSetUp) {return;}bindUi(R.layout.install_confirm, false);checkIfAllowedAndInitiateInstall();...
}//“未知来源apk”升级提示界面
private void bindUi(int layout, boolean enableOk) {setContentView(layout);mOk = (Button) findViewById(R.id.ok_button);mCancel = (Button)findViewById(R.id.cancel_button);mOk.setOnClickListener(this);mCancel.setOnClickListener(this);mEnableOk = enableOk;mOk.setEnabled(enableOk);PackageUtil.initSnippetForNewApp(this, mAppSnippet, R.id.app_snippet);}/*** Check if it is allowed to install the package and initiate install if allowed. If not allowed* show the appropriate dialog.* 检查是否允许未知来源apk安装,允许则安装,不允许,跳转到设置界面。*/private void checkIfAllowedAndInitiateInstall() {...if (mAllowUnknownSources || !isInstallRequestFromUnknownSource(getIntent())) {initiateInstall();} else {// Check for unknown sources restrictionfinal int unknownSourcesRestrictionSource = mUserManager.getUserRestrictionSource(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES, Process.myUserHandle());if ((unknownSourcesRestrictionSource & UserManager.RESTRICTION_SOURCE_SYSTEM) != 0) {showDialogInner(DLG_UNKNOWN_SOURCES_RESTRICTED_FOR_USER);} else if (unknownSourcesRestrictionSource != UserManager.RESTRICTION_NOT_SET) {startActivity(new Intent(Settings.ACTION_SHOW_ADMIN_SUPPORT_DETAILS));finish();} else {handleUnknownSources();}}...}//界面跳转到安装提示界面
private void initiateInstall() {...startInstallConfirm();...
}public void onClick(View v) {...startInstall();...
}//进到InstallInstalling类
private void startInstall() {...Intent newIntent = new Intent();newIntent.setClass(this, InstallInstalling.class);startActivity(newIntent);finish();...
}

整体逻辑:
(1) 在 PackageInstallerActivity 的 onCreate() 初始化mPm、mInstaller;
(2) 使用 PackageUtil.getPackageInfo() 解析APK权限信息(可用于安装前展示给用户),注意参数flags 是 PackageManager.GET_PERMISSIONS;使用PackageUtil.getAppSnippet()获取apk摘要:图标、名字;
(3) 然后使用 checkIfAllowedAndInitiateInstall() 是检查APK来源,展示"未知来源APK安装"的对话框,当点击"settings"按钮后跳转到设置页;
(4) 打开允许未知来源安装 后回到 PackageInstallerActivity,在 onActivityResult()中,展示"确认安装"提示,点击”安装“,跳转 InstallInstalling - 开始安装。

//../packages/apps/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java
public class InstallInstalling extends Activity {@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {ApplicationInfo appInfo = getIntent().getParcelableExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO);mPackageURI = getIntent().getData();...PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(PackageInstaller.SessionParams.MODE_FULL_INSTALL);...//安装结果监听。在收到安装结果的广播后 会调用此 ObservermInstallId = InstallEventReceiver.addObserver(this, EventResultPersister.GENERATE_NEW_ID,this::launchFinishBasedOnResult);...//IPC:PackageInstaller 内部会通过 IPackageInstaller 走到PackageInstallerService 的 createSession 方法来创建mSessionId = getPackageManager().getPackageInstaller().createSession(params);}@Overrideprotected void onResume() {super.onResume();// This is the first onResume in a single life of the activityif (mInstallingTask == null) {PackageInstaller installer = getPackageManager().getPackageInstaller();PackageInstaller.SessionInfo sessionInfo = installer.getSessionInfo(mSessionId);if (sessionInfo != null && !sessionInfo.isActive()) {mInstallingTask = new InstallingAsyncTask();mInstallingTask.execute();} else {// we will receive a broadcast when the install is finishedmCancelButton.setEnabled(false);setFinishOnTouchOutside(false);}}}/*** Send the package to the package installer and then register a event result observer that* will call {@link #launchFinishBasedOnResult(int, int, String)}*/private final class InstallingAsyncTask extends AsyncTask<Void, Void,PackageInstaller.Session> {volatile boolean isDone;@Overrideprotected PackageInstaller.Session doInBackground(Void... params) {PackageInstaller.Session session;try {session = getPackageManager().getPackageInstaller().openSession(mSessionId);} catch (IOException e) {return null;}session.setStagingProgress(0);try {File file = new File(mPackageURI.getPath());try (InputStream in = new FileInputStream(file)) {long sizeBytes = file.length();try (OutputStream out = session.openWrite("PackageInstaller", 0, sizeBytes)) {byte[] buffer = new byte[1024 * 1024];while (true) {int numRead = in.read(buffer);if (numRead == -1) {session.fsync(out);break;}if (isCancelled()) {session.close();break;}out.write(buffer, 0, numRead);if (sizeBytes > 0) {float fraction = ((float) numRead / (float) sizeBytes);session.addProgress(fraction);}}}}return session;} catch (IOException | SecurityException e) {Log.e(LOG_TAG, "Could not write package", e);session.close();return null;} finally {synchronized (this) {isDone = true;notifyAll();}}}@Overrideprotected void onPostExecute(PackageInstaller.Session session) {if (session != null) {Intent broadcastIntent = new Intent(BROADCAST_ACTION);broadcastIntent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);broadcastIntent.setPackage( getPackageManager().getPermissionControllerPackageName());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);}}}}
}

安装流程:
(1) onCreate 中通过 PackageInstaller 创建Session 并返回 mSessionId;
(2) onResume 中开启InstallingAsyncTask,把包信息写入mSessionId对应的session,然后提交;
(3) onCreate 中添加了 安装结果的监听,在收到安装结果的广播后 会调用此 跳转到对应结果页面;

InstallInstalling.java 类onCreate() 中,PackageInstaller,APK安装器,是在ApplicationPackageManager的 getPackageInstaller()中创建:

// ../frameworks/base/core/java/android/app/ApplicationPackageManager.java@Overridepublic PackageInstaller getPackageInstaller() {synchronized (mLock) {if (mInstaller == null) {try {// mPM.getPackageInstaller() 获取的 IPackageInstaller实例的封装,而 IPackageInstaller 在系统服务端的具体实现是 PackageInstallerService。mInstaller = new PackageInstaller(mPM.getPackageInstaller(),mContext.getPackageName(), mContext.getUserId());} catch (RemoteException e) {throw e.rethrowFromSystemServer();}}return mInstaller;}}// ../frameworks/base/core/java/android/content/pm/PackageInstaller.java/** {@hide} */public PackageInstaller(IPackageInstaller installer,String installerPackageName, int userId) {mInstaller = installer;mInstallerPackageName = installerPackageName;mUserId = userId;}public int createSession(@NonNull SessionParams params) throws IOException {try {final String installerPackage;if (params.installerPackageName == null) {installerPackage = mInstallerPackageName;} else {installerPackage = params.installerPackageName;}return mInstaller.createSession(params, installerPackage, mUserId);} catch (RuntimeException e) {ExceptionUtils.maybeUnwrapIOException(e);throw e;} catch (RemoteException e) {throw e.rethrowFromSystemServer();}}

mInstaller = new PackageInstaller(mPM.getPackageInstaller(),mContext.getPackageName(),mContext.getUserId())中
mPM.getPackageInstaller() 获取的 IPackageInstaller实例的封装,而 IPackageInstaller 在系统服务端的具体实现是 PackageInstallerService。

且 PackageInstallerService 的实例是在 PMS 的构造方法中创建的,初始化时会读取 /data/system 目录下的 install_sessions.xml 文件,这个文件中保存了系统中未完成的 Install Session。PMS 会根据文件的内容创建 PackageInstallerSession 对象并插入到 mSessions 中。

//../frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
final PackageInstallerService mInstallerService;mInstallerService = new PackageInstallerService(context, this);@Overridepublic void systemReady() {mInstallerService.systemReady();}//../frameworks/base/services/core/java/com/android/server/pm/PackageInstallerService.javapublic void systemReady() {synchronized (mSessions) {readSessionsLocked();// Ignore stages and icons claimed by active sessionsfor (int i = 0; i < mSessions.size(); i++) {final PackageInstallerSession session = mSessions.valueAt(i);unclaimedIcons.remove(buildAppIconFile(session.sessionId));}}}@GuardedBy("mSessions")private final SparseArray<PackageInstallerSession> mSessions = new SparseArray<>();@GuardedBy("mSessions")private void readSessionsLocked() {if (valid) {mSessions.put(session.sessionId, session);} else {// Since this is early during boot we don't send// any observer events about the session, but we// keep details around for dumpsys.addHistoricalSessionLocked(session);}mAllocatedSessions.put(session.sessionId, true);}@Overridepublic IPackageInstallerSession openSession(int sessionId) {try {return openSessionInternal(sessionId);} catch (IOException e) {throw ExceptionUtils.wrap(e);}}private IPackageInstallerSession openSessionInternal(int sessionId) throws IOException {synchronized (mSessions) {final PackageInstallerSession session = mSessions.get(sessionId);if (session == null || !isCallingUidOwner(session)) {throw new SecurityException("Caller has no access to session " + sessionId);}session.open();return session;}}

PackageInstaller 提供了 应用安装、更新、移除的能力,当然具体实现是 IPC 到了 PackageInstallerService中。

Session,是与 mSessionId绑定的安装会话,代表一个进行中的安装。Session类是对 IPackageInstaller.openSession(sessionId) 获取的 PackageInstallerSession(系统服务端)的封装。调用位置在InstallInstalling.java 类InstallingAsyncTask()方法中。Session 管理安装的参数,并提供将安装包临时复制到特定路径(data/app-staging)的能力。

Session的创建和打开 具体实现是在 PackageInstallerService中,主要是 初始化apk的安装信息及环境,并创建一个sessionId,将安装Session与sessionId 进行绑定。(代码较多,就不放了)

我们重点看 把包信息写入mSessionId对应的session,然后提交,回到 InstallInstalling 中,看到在 InstallingAsyncTask 中执行了对 APK包文件 的读取和写入操作,写入的输出流是 通过Session获取,也即是把 将APK文件写入了Session 。完成后 又调用了 session.commit(),具体实现是在PackageInstallerSession, 来看看:

//../packages/apps/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.javaprivate final class InstallingAsyncTask extends AsyncTask<Void, Void,PackageInstaller.Session> {PackageInstaller.Session session;try {session =  getPackageManager().getPackageInstaller().openSession(mSessionId);} catch (IOException e) {return null;}@Overrideprotected void onPostExecute(PackageInstaller.Session session) {session.commit(pendingIntent.getIntentSender());}}//../frameworks/base/core/java/android/content/pm/PackageInstaller.javaprivate IPackageInstallerSession mSession;/** {@hide} */public Session(IPackageInstallerSession session) {mSession = session;}public void commit(@NonNull IntentSender statusReceiver) {try {mSession.commit(statusReceiver, false);} catch (RemoteException e) {throw e.rethrowFromSystemServer();}}// ../frameworks/base/core/java/android/content/pm/IPackageInstallerSession.aidl//实现类 ../frameworks/base/services/core/java/com/android/server/pm/PackageInstallerSession.java@Overridepublic void commit(@NonNull IntentSender statusReceiver, boolean forTransfer) {...mHandler.obtainMessage(MSG_COMMIT).sendToTarget();...}private final Handler.Callback mHandlerCallback = new Handler.Callback() {@Overridepublic boolean handleMessage(Message msg) {switch (msg.what) {...case MSG_COMMIT:synchronized (mLock) {try {commitLocked();} catch (PackageManagerException e) {final String completeMsg = ExceptionUtils.getCompleteMessage(e);Slog.e(TAG,"Commit of session " + sessionId + " failed: " + completeMsg);destroyInternal();dispatchSessionFinished(e.error, completeMsg, null);}}break;...}return true;}};private final PackageManagerService mPm;
@GuardedBy("mLock")
private void commitLocked() throws PackageManagerException {...final IPackageInstallObserver2 localObserver = new IPackageInstallObserver2.Stub() {@Overridepublic void onUserActionRequired(Intent intent) {throw new IllegalStateException();}@Overridepublic void onPackageInstalled(String basePackageName, int returnCode, String msg,Bundle extras) {destroyInternal();dispatchSessionFinished(returnCode, msg, extras);}};//安装走到了 PMS 中mPm.installStage(mPackageName, stageDir, localObserver, params,mInstallerPackageName, mInstallerUid, user, mSigningDetails);
}

最后,安装过程走到了 PMS 的 installStage()。

目前为止,只是通过 PackageInstaller 维持了安装 Session,把安装包写入到 Session中,真正的安装过程是 PMS 来执行。

//../frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java```java
void installStage(String packageName, File stagedDir,IPackageInstallObserver2 observer, PackageInstaller.SessionParams sessionParams,String installerPackageName, int installerUid, UserHandle user,PackageParser.SigningDetails signingDetails) {final Message msg = mHandler.obtainMessage(INIT_COPY);final int installReason = fixUpInstallReason(installerPackageName, installerUid,sessionParams.installReason);final InstallParams params = new InstallParams(origin, null, observer,sessionParams.installFlags, installerPackageName, sessionParams.volumeUuid,verificationInfo, user, sessionParams.abiOverride,sessionParams.grantedRuntimePermissions, signingDetails, installReason);params.setTraceMethod("installStage").setTraceCookie(System.identityHashCode(params));msg.obj = params;Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "installStage",System.identityHashCode(msg.obj));Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "queueInstall",System.identityHashCode(msg.obj));mHandler.sendMessage(msg);
}void doHandleMessage(Message msg) {case INIT_COPY:mPendingInstalls.add(idx, params);mHandler.sendEmptyMessage(MCS_BOUND);break;case MCS_BOUND:HandlerParams params = mPendingInstalls.get(0);params.startCopy();break;}final boolean startCopy() {handleStartCopy();handleReturnCode();}public void handleStartCopy() throws RemoteException {int ret = PackageManager.INSTALL_SUCCEEDED;...PackageInfoLite pkgLite = null;//解析包 返回最小的细节:pkgName、versionCode、安装所需空间大小、获取安装位置等pkgLite = mContainerService.getMinimalPackageInfo(origin.resolvedPath, installFlags,packageAbiOverride);...if (ret == PackageManager.INSTALL_SUCCEEDED) {//以下是 获取安装位置失败情况int loc = pkgLite.recommendedInstallLocation;if (loc == PackageHelper.RECOMMEND_FAILED_INVALID_LOCATION) {ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;} else if (loc == PackageHelper.RECOMMEND_FAILED_ALREADY_EXISTS) {ret = PackageManager.INSTALL_FAILED_ALREADY_EXISTS;} else if (loc == PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE) {ret = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;} else if (loc == PackageHelper.RECOMMEND_FAILED_INVALID_APK) {ret = PackageManager.INSTALL_FAILED_INVALID_APK;} else if (loc == PackageHelper.RECOMMEND_FAILED_INVALID_URI) {ret = PackageManager.INSTALL_FAILED_INVALID_URI;} else if (loc == PackageHelper.RECOMMEND_MEDIA_UNAVAILABLE) {ret = PackageManager.INSTALL_FAILED_MEDIA_UNAVAILABLE;} ...}final InstallArgs args = createInstallArgs(this);mArgs = args;ret = args.copyApk(mContainerService, true);mRet = ret;}//解析包返回最小的细节:pkgName、versionCode、安装所需空间大小;确认包安装位置;校验APK完整性。获取结果mRet。int copyApk(IMediaContainerService imcs, boolean temp) throws RemoteException {Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "copyApk");try {return doCopyApk(imcs, temp);} finally {Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);}}private int doCopyApk(IMediaContainerService imcs, boolean temp) throws RemoteException      {//获取拷贝文件路径:/data/apptry {final boolean isEphemeral = (installFlags & PackageManager.INSTALL_INSTANT_APP) != 0;final File tempDir =mInstallerService.allocateStageDirLegacy(volumeUuid, isEphemeral);codeFile = tempDir;resourceFile = tempDir;} catch (IOException e) {Slog.w(TAG, "Failed to create copy file: " + e);return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;}//拷贝apkint ret = PackageManager.INSTALL_SUCCEEDED;ret = imcs.copyPackage(origin.file.getAbsolutePath(), target);if (ret != PackageManager.INSTALL_SUCCEEDED) {Slog.e(TAG, "Failed to copy package");return ret;}//拷贝Native代码 即 .so文件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;
}

在FileInstallArgs中的copyApk()走到doCopyApk(),先获取了拷贝文件路径:/data/app,使用PackageManagerServiceUtils.copyPackage()进行APK拷贝,接着是 .so文件的拷贝。也就是说,把发送到 Session暂存目录 data/app-staging 的APK 拷贝到了 /data/app。然后看 handleReturnCode(),

 @Overridevoid 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);}}private void processPendingInstall(final InstallArgs args, final int currentStatus) {installPackageTracedLI(args, res);Message msg = mHandler.obtainMessage(POST_INSTALL, token, 0);mHandler.sendMessage(msg);
}private void installPackageTracedLI(InstallArgs args, PackageInstalledInfo res) {installPackageLI(args, res);
}private void installPackageLI(InstallArgs args, PackageInstalledInfo res) {}void doHandleMessage(Message msg) {case POST_INSTALL: handlePackagePostInstall(parentRes, grantPermissions, killApp,virtualPreload, grantedPermissions, didRestore,args.installerPackageName, args.observer);break;
}

mHandler 使用 PMS的 handlePackagePostInstall()方法处理 POST_INSTALL:

根据安装结果 发送 Intent.ACTION_PACKAGE_ADDED 等广播,桌面Launcher 收到广播后就会在桌上增加App的Icon

调用 PackageInstallSession 中保存的IPackageInstallObserver2实例的onPackageInstalled()方法,最后发送安装成功的通知显示在通知栏,通过 IntentSender 发送 在 InstallInstalling 中就定义好的广播,最后 InstallInstalling页面 根据结果展示 安装成功/安装失败 。
还有一些安装过程解析apk,apk校验等细节过程,后面需要再那恭喜看吧,内容太多。具体看下面的链接。
https://blog.csdn.net/u010479969/article/details/48292237
本文借鉴链接:
https://mp.weixin.qq.com/s/eACbMYJ042YKglg7fpEb2w
安全提醒界面
在这里插入图片描述
设置界面打开 安全开关
在这里插入图片描述

在这里插入图片描述
安装失败
在这里插入图片描述


http://chatgpt.dhexx.cn/article/Cyq4YtJa.shtml

相关文章

APK安装流程及详细步骤

往期推荐 APK打包流程 APK文件结构 Android开发之数据储存 1.1 安装方式 1.安装APK文件共有4种方式&#xff1a; &#xff08;1&#xff09;系统程序安装 &#xff08;2&#xff09;通过Android市场安装 &#xff08;3&#xff09;手机自带安装 &#xff08;4&#xff09…

2021年宏观经济十大趋势展望

新冠肺炎疫情无疑是一只黑天鹅&#xff0c;对2020年世界经济带来了巨大的冲击。临近年末&#xff0c;可以从很多角度回顾2020年中国宏观经济。如果从内外循环的角度概括一下&#xff0c;我们认为今年中国经济有两个“超预期”&#xff1a;外贸超预期&#xff0c;外资超预期。 …

2010-2020年农业农村重要经济指标

数据集名称&#xff1a;农业农村重要经济指标 时间范围&#xff1a;2010-2020年 数据来源&#xff1a;农业农村部 相关说明&#xff1a; 农业农村重要经济指标&#xff0c;覆盖6大类别&#xff0c;170多张统计表 全球及主要国家大豆产量(万吨)&#xff08;2015-2019&#…

金融数据分析(十)人均国内生产总值的增长率

案例&#xff08;四&#xff09;宏观金融数据分析 项目一&#xff1a;利用世界银行公开数据平台提供的宏观经济数据比较最近40年间A国与B国的人均国内生产总值的增长率&#xff08;图表输出&#xff09; 数据可通过此网页中的下载链接获取&#xff1a;https://data.worldbank…

【指数编制系列六】景气指数编制方法

景气是经济景气的简称&#xff0c;指经济总体的运行发展态势和活跃程度&#xff0c;是用来分析经济活跃程度的经济概念。经济活动发展具有周期性扩张的特征&#xff0c;因而常用景气波动、景气循环等术语来表达经济周期波动的特点。   经济景气周期波动理论是研究产业景气周期…

持续有效的风险指标:动荡指数

量化投资与机器学习微信公众号&#xff0c;是业内垂直于量化投资、对冲基金、Fintech、人工智能、大数据等领域的主流自媒体。公众号拥有来自公募、私募、券商、期货、银行、保险、高校等行业30W关注者&#xff0c;荣获2021年度AMMA优秀品牌力、优秀洞察力大奖&#xff0c;连续…

消费与储蓄的决定-中国视角下的宏观经济

消费与储蓄的决定 – 潘登同学的宏观经济学笔记 文章目录 消费与储蓄的决定 -- 潘登同学的宏观经济学笔记 中国消费不足、储蓄过剩的事实有关中国经济需求面的关键问题 拉姆齐模型单期单个消费者的优化问题单期多个消费者的优化问题仅居民积累资本仅企业积累资本企业和居民同时…

关于指标的理解

文章目录 1.**纵向分析和横向分析**2.**电商行业**3.**广告投放**4.**基本指标**5. **经销商资金利用效果高低的指标是库销比**6. **业务分析模型**7.**可视化图表**8.**业务分析报告**9.父子级关系10.使用数据 1.纵向分析和横向分析 纵向比较同一事物在不同时期的形式&#xf…

如何理解宏观经济

从投研的视角&#xff0c;宏观经济是资产配置的基础。宏观经济研究是一种至上而下的研究方法&#xff0c;先判断整个国家的宏观经济增长的情况&#xff0c;再研究行业或者产业的变化&#xff0c;然后选择某个代表性企业进行研究&#xff0c;最后判断这个企业或者股票的资产价格…

常见经济指标学习

央行统计数据&#xff1a;http://www.pbc.gov.cn/diaochatongjisi/116219/116319/3959050/3959052/index.html 资产负债表&#xff1a;可以看出央行在扩表还是缩表 央行中的资产代表着“基础货币”&#xff0c;基础货币又称“货币基数”&#xff0c;它具有使货币供应量成倍扩…

中国经济供给面的分析-中国视角下的宏观经济

中国经济供给面的分析 – 潘登同学的宏观经济学笔记 文章目录 中国经济供给面的分析 -- 潘登同学的宏观经济学笔记 分析中国经济问题的经济学方法供需分析两个例子 数学语言 从国民财富到GDP什么是国民财富为什么要关心国民财富GDP包含价值判断三种计算GDP的方法 经济增长的供给…

宏观经济指标分析与数据可视化——PMI

PMI是一个很有意思的东西&#xff0c;一般都会认为PMI是一个领先指标。PMI中文名叫做采购经理人指数&#xff0c;听起来有点不知所云&#xff0c;说白了就是一个问卷调查。 1、PMI是怎么来的&#xff1f; 我们来假设一个场景。话说在小天才幼儿园里面&#xff0c;老师很关心小…

宏观经济分析

第三章 宏观经济分析 熟悉宏观经济分析的意义&#xff1b;熟悉总量分析和结构分析的定义、特点和关系&#xff1b;熟悉宏观分析资料的搜集与处理方法。   了解国民经济总体指标、投资指标、消费指标、金融指标以及财政指标的主要内容&#xff1b;熟悉各项指标变动对宏观经济…

第一章 宏观经济指标

1. 国内生产总值GDP 定义&#xff1a;关于产出的指标&#xff0c;指的谁一个国家或者地区在给定时间内新增的最终产品和最终服务的总价值。 &#xff08;1&#xff09;收集数据的对象&#xff1a;产品和服务 产品&#xff1a;有具体物理形态的产出。比如一支笔、一本书。服务&…

宏观经济指标

第一节、主要经济指标 一、国内生产总值 国内生产总值&#xff08;GDPGross Domestic Product&#xff09;是指一个国家或者地区所有常驻单位在一定时期内生产的所有最终产品和劳务的市场价值。GDP是国民经济核算的核心指标&#xff0c;也是衡量一个国家的总体经济状况重…

宏观经济学_宏观经济指标

目录 1.1国内生产总值 1.2从支出角度看GDP 1.3价格指数 1.4价格指数的运用 1.5就业 1.6失业的不同种类 1.1国内生产总值 定义&#xff1a;一个国家或地区在给定时间内新增的最终产品和最终服务的总价值。 特点&#xff1a; 判断是否应该加入GDP中计算&#xff0c;如果一…

常见的宏观经济指标介绍

收益曲线中&#xff0c;1年期以下的为短端利率&#xff0c;5年以上为长端利率&#xff0c;中间的是中端利率。 久期策略&#xff1a;根据基本价值评估。经济环境和市场风险评估&#xff0c;以及基金债券投资对风险收益的特定要求&#xff0c;确定债券组合的久期配置。 社融&a…

宏观经济的基本指标及其衡量

目录 国内生产总值及其衡量 GDP的计算方法 与GDP相关的经济指标 Y C I G X Z GNP GDP 海外净收入流入 - 海外净收入流出 GDP 消费 投资 政府支出 出口 - 进口 π2&#xff08;P₂-P₁/P₁&#xff09;100% 失业及其衡量 一、人口的分类 失业的类型 充分就…

《宏观经济学》第一章

第一章 宏观经济指标 1.1 国内生产总值GDP 定义&#xff1a;一个国家或者地区在给定时间内新增的最终产品和最终服务的总价值。 收集的对象&#xff1a;产品和服务 产品&#xff1a;有具体物理形态的产出。比如一支笔、一本书。 服务&#xff1a;没有物质形态的产出。比如…

IntelliJ IDEA代码格式化,代码超出规定宽度自动换行

格式化快捷键&#xff1a;CtrlAltL 一、设置 二、效果