Android系统在启动的过程中,会启动一个应用程序管理服务PackageManagerService,这个服务负责扫描系统中特定的目录,找到里面的应用程序文件,即以Apk为后缀的文件,然后对这些文件进解析,得到应用程序的相关信息,完成应用程序的安装过程,本文将详细分析这个过程。

一、启动安装

1、InstallStart

在开发过程中有时候需要进行APP更新,或者下载其他APP到本地让后使用以下代码进行安装,我这里单单讲解在执行startActivity之后的运行过程。

        Intent intent = new Intent(Intent.ACTION_VIEW);
//设置intent的数据类型是应用程序application  intent.setDataAndType(Uri.parse("file://" + apkfile.toString()), "application/vnd.android.package-archive");
//为这个新apk开启一个新的activity栈  intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
//开始安装  startActivity(intent);

在执行完这几行代码后会启动系统的一个APP进行APK的安装,我们可以通过源码查看在系统中有一个packageinstaller的系统应用。然后首先查看看清单文件

<activity android:name=".InstallStart"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="file" /><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="file" /><data android:scheme="package" /><data android:scheme="content" /></intent-filter><intent-filter android:priority="1"><action android:name="android.content.pm.action.CONFIRM_PERMISSIONS" /><category android:name="android.intent.category.DEFAULT" /></intent-filter></activity>

从中可以发现首先启动的是名为InstallStart的Activity,由于整个源码有点多,因此这里只是贴出部分重要方法。在这里只需要看onCreate的方法

import android.Manifest;
import android.app.Activity;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;public class InstallStart extends Activity {private static final String LOG_TAG = InstallStart.class.getSimpleName();private static final String SCHEME_CONTENT = "content";private static final String DOWNLOADS_AUTHORITY = "downloads";private IActivityManager mIActivityManager;private IPackageManager mIPackageManager;private boolean mAbortInstall = false;@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);mIPackageManager = AppGlobals.getPackageManager();Intent intent = getIntent();String callingPackage = getCallingPackage();// If the activity was started via a PackageInstaller session, we retrieve the calling// package from that sessionint sessionId = intent.getIntExtra(PackageInstaller.EXTRA_SESSION_ID, -1);if (callingPackage == null && sessionId != -1) {      //我们第三发安装sessionId为-1PackageInstaller packageInstaller = getPackageManager().getPackageInstaller();PackageInstaller.SessionInfo sessionInfo = packageInstaller.getSessionInfo(sessionId);callingPackage = (sessionInfo != null) ? sessionInfo.getInstallerPackageName() : null;}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(originatingUid);if (targetSdkVersion < 0) {Log.w(LOG_TAG, "Cannot get target sdk version for uid " + originatingUid);// Invalid originating uid supplied. Abort install.mAbortInstall = true;} else if (targetSdkVersion >= Build.VERSION_CODES.O && !declaresAppOpPermission(originatingUid, Manifest.permission.REQUEST_INSTALL_PACKAGES)) {//在现版本第三方安装APK需要申明REQUEST_INSTALL_PACKAGES权限,declaresAppOpPermission检测//是否申请改权限Log.e(LOG_TAG, "Requesting uid " + originatingUid + " needs to declare permission "+ Manifest.permission.REQUEST_INSTALL_PACKAGES);mAbortInstall = true;}}if (mAbortInstall) { //如果经过上述一系列操作没问题,当然是falsesetResult(RESULT_CANCELED);finish();return;}Intent nextActivity = new Intent(intent);nextActivity.setFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);// The the installation source as the nextActivity thinks this activity is the source, hence// set the originating UID and sourceInfo explicitlynextActivity.putExtra(PackageInstallerActivity.EXTRA_CALLING_PACKAGE, callingPackage);nextActivity.putExtra(PackageInstallerActivity.EXTRA_ORIGINAL_SOURCE_INFO, sourceInfo);nextActivity.putExtra(Intent.EXTRA_ORIGINATING_UID, originatingUid);if (PackageInstaller.ACTION_CONFIRM_PERMISSIONS.equals(intent.getAction())) {//在这里我们第三方app是不会跳转到这里的nextActivity.setClass(this, PackageInstallerActivity.class);} else {Uri packageUri = intent.getData();if (packageUri == null) {// if there's nothing to do, quietly slip into the etherIntent result = new Intent();result.putExtra(Intent.EXTRA_INSTALL_RESULT,PackageManager.INSTALL_FAILED_INVALID_URI);setResult(RESULT_FIRST_USER, result);nextActivity = null;} else {if (packageUri.getScheme().equals(SCHEME_CONTENT)) {nextActivity.setClass(this, InstallStaging.class);} else {nextActivity.setClass(this, PackageInstallerActivity.class);}}}if (nextActivity != null) {//跳转到下一个ActivitystartActivity(nextActivity);}finish();}......
}

经过一系列的解析操作之后会跳转到新的界面

2、PackageInstallerActivity

public class PackageInstallerActivity extends OverlayTouchActivity implements OnClickListener {private static final String TAG = "PackageInstaller";.....@Overrideprotected void onCreate(Bundle icicle) {super.onCreate(icicle);if (icicle != null) {mAllowUnknownSources = icicle.getBoolean(ALLOW_UNKNOWN_SOURCES_KEY);}mPm = getPackageManager(); //获取PackageManagermIpm = AppGlobals.getPackageManager();mAppOpsManager = (AppOpsManager) getSystemService(Context.APP_OPS_SERVICE);mInstaller = mPm.getPackageInstaller();//获取PackageInstallermUserManager = (UserManager) getSystemService(Context.USER_SERVICE);final Intent intent = getIntent();mCallingPackage = intent.getStringExtra(EXTRA_CALLING_PACKAGE);mSourceInfo = intent.getParcelableExtra(EXTRA_ORIGINAL_SOURCE_INFO);mOriginatingUid = intent.getIntExtra(Intent.EXTRA_ORIGINATING_UID,PackageInstaller.SessionParams.UID_UNKNOWN);mOriginatingPackage = (mOriginatingUid != PackageInstaller.SessionParams.UID_UNKNOWN)? getPackageNameForUid(mOriginatingUid) : null;final Uri packageUri;if (PackageInstaller.ACTION_CONFIRM_PERMISSIONS.equals(intent.getAction())) {//非Android的GooglePaly上的app可以直接忽略了final int sessionId = intent.getIntExtra(PackageInstaller.EXTRA_SESSION_ID, -1);final PackageInstaller.SessionInfo info = mInstaller.getSessionInfo(sessionId); //在PackageInstaller中创建一个Sessionif (info == null || !info.sealed || info.resolvedBaseCodePath == null) {Log.w(TAG, "Session " + mSessionId + " in funky state; ignoring");finish();return;}mSessionId = sessionId;//packageUri = Uri.fromFile(new File(info.resolvedBaseCodePath));mOriginatingURI = null;mReferrerURI = null;} else {mSessionId = -1;packageUri = intent.getData(); //获取安装APK安装包的路径mOriginatingURI = intent.getParcelableExtra(Intent.EXTRA_ORIGINATING_URI);mReferrerURI = intent.getParcelableExtra(Intent.EXTRA_REFERRER);}// if there's nothing to do, quietly slip into the etherif (packageUri == null) {Log.w(TAG, "Unspecified source");setPmResult(PackageManager.INSTALL_FAILED_INVALID_URI);finish();return;}if (DeviceUtils.isWear(this)) {showDialogInner(DLG_NOT_SUPPORTED_ON_WEAR);return;}boolean wasSetUp = processPackageUri(packageUri);if (!wasSetUp) {return;}// load dummy layout with OK button disabled until we override this layout in// startInstallConfirmbindUi(R.layout.install_confirm, false); //安装界面绑定checkIfAllowedAndInitiateInstall(); //检测此APK是否允许,并且初始化安装前操作界面}.....public void onClick(View v) {if (v == mOk) {//点击确定安装按钮if (mOk.isEnabled()) {if (mOkCanInstall || mScrollView == null) {if (mSessionId != -1) {mInstaller.setPermissionsResult(mSessionId, true);finish();} else {startInstall();}} else {mScrollView.pageScroll(View.FOCUS_DOWN);}}} else if (v == mCancel) {// Cancel and finishsetResult(RESULT_CANCELED);if (mSessionId != -1) {mInstaller.setPermissionsResult(mSessionId, false);}finish();}}.....private void startInstall() {// Start subactivity to actually install the applicationIntent newIntent = new Intent();newIntent.putExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO,mPkgInfo.applicationInfo);newIntent.setData(mPackageURI);newIntent.setClass(this, InstallInstalling.class);String installerPackageName = getIntent().getStringExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME);if (mOriginatingURI != null) {newIntent.putExtra(Intent.EXTRA_ORIGINATING_URI, mOriginatingURI);}if (mReferrerURI != null) {newIntent.putExtra(Intent.EXTRA_REFERRER, mReferrerURI);}if (mOriginatingUid != PackageInstaller.SessionParams.UID_UNKNOWN) {newIntent.putExtra(Intent.EXTRA_ORIGINATING_UID, mOriginatingUid);}if (installerPackageName != null) {newIntent.putExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME,installerPackageName);}if (getIntent().getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false)) {newIntent.putExtra(Intent.EXTRA_RETURN_RESULT, true);newIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);}if(localLOGV) Log.i(TAG, "downloaded app uri="+mPackageURI);startActivity(newIntent); //跳转到安装进行中的界面finish();}
.....}

在上述界面中将APK解析,并分析其中权限信息等内容。此时,点击安装就会进入到下一个界面

3、InstallInstalling

public class InstallInstalling extends Activity {private static final String LOG_TAG = InstallInstalling.class.getSimpleName();private static final String SESSION_ID = "com.android.packageinstaller.SESSION_ID";private static final String INSTALL_ID = "com.android.packageinstaller.INSTALL_ID";@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.install_installing);ApplicationInfo appInfo = getIntent().getParcelableExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO);mPackageURI = getIntent().getData();if ("package".equals(mPackageURI.getScheme())) {//查看是否是package路径Uri,发现明显不是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.initSnippetForNewApp(this, PackageUtil.getAppSnippet(this, appInfo,sourceFile), R.id.app_snippet);if (savedInstanceState != null) { //为nullmSessionId = savedInstanceState.getInt(SESSION_ID);mInstallId = savedInstanceState.getInt(INSTALL_ID);// Reregister for result; might instantly call back if result was delivered while// activity was destroyedtry {InstallEventReceiver.addObserver(this, mInstallId,this::launchFinishBasedOnResult);} catch (EventResultPersister.OutOfIdsException e) {// Does not happen}} else {PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(PackageInstaller.SessionParams.MODE_FULL_INSTALL);params.referrerUri = getIntent().getParcelableExtra(Intent.EXTRA_REFERRER);params.originatingUri = getIntent().getParcelableExtra(Intent.EXTRA_ORIGINATING_URI);params.originatingUid = getIntent().getIntExtra(Intent.EXTRA_ORIGINATING_UID,UID_UNKNOWN);File file = new File(mPackageURI.getPath());try {PackageParser.PackageLite pkg = PackageParser.parsePackageLite(file, 0);params.setAppPackageName(pkg.packageName);params.setInstallLocation(pkg.installLocation);params.setSize(PackageHelper.calculateInstalledSize(pkg, false, params.abiOverride));} catch (PackageParser.PackageParserException e) {Log.e(LOG_TAG, "Cannot parse package " + file + ". Assuming defaults.");Log.e(LOG_TAG,"Cannot calculate installed size " + file + ". Try only apk size.");params.setSize(file.length());} catch (IOException e) {Log.e(LOG_TAG,"Cannot calculate installed size " + file + ". Try only apk size.");params.setSize(file.length());}try {mInstallId = InstallEventReceiver.addObserver(this, EventResultPersister.GENERATE_NEW_ID,this::launchFinishBasedOnResult);} catch (EventResultPersister.OutOfIdsException e) {launchFailure(PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null);}try {//在PackageInstaller创建用于通信的session,关于创建Seeion过程这里不再做叙述mSessionId = getPackageManager().getPackageInstaller().createSession(params);} catch (IOException e) {launchFailure(PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null);}}mCancelButton = (Button) findViewById(R.id.cancel_button);mCancelButton.setOnClickListener(view -> {if (mInstallingTask != null) {mInstallingTask.cancel(true);}if (mSessionId > 0) {getPackageManager().getPackageInstaller().abandonSession(mSessionId);mSessionId = 0;}setResult(RESULT_CANCELED);finish();});mSessionCallback = new InstallSessionCallback();}}@Overrideprotected void onResume() {super.onResume();// This is the first onResume in a single life of the activityif (mInstallingTask == null) {PackageInstaller installer = getPackageManager().getPackageInstaller(); //获取PackageInstallerPackageInstaller.SessionInfo sessionInfo = installer.getSessionInfo(mSessionId);//根据在onCreate中创建的SeesionID获取相应session信息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);}}}private final class InstallingAsyncTask extends AsyncTask<Void, Void,PackageInstaller.Session> {volatile boolean isDone;@Overrideprotected PackageInstaller.Session doInBackground(Void... params) {PackageInstaller.Session session;try {//根据SessionId打开PackageInstaller端Sessionsession = getPackageManager().getPackageInstaller().openSession(mSessionId);} catch (IOException e) {return null;}session.setStagingProgress(0);try {File file = new File(mPackageURI.getPath());//获取APK安装包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) {//读取APK,并通过Session传递APK安装包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) 获取PackageManager,然后获取其中的PackageInstaller对象 (2)
在PackageInstaller中创建用于传输APK安装包以及安装信息的Session,并返回SessionId (3)
创建异步任务,并在异步任务中根据之前返回的SessionId打开PackageInstaller端Session (4)
通过Session将APK安装包以及相关信息传递 (5)
在异步任务中onPostExecute方法中执行session.commit(pendingIntent.getIntentSender())

二、PackageInstaller解析

在这一步操作中用到PackageInstaller,下面看一下这部分代码。由于代码太多,这里只做部分方法讲解。

1、构造函数

public PackageInstaller(IPackageInstaller installer,String installerPackageName, int userId) {mInstaller = installer;mInstallerPackageName = installerPackageName;mUserId = userId;
}

2、创建Session

public int createSession(@NonNull SessionParams params) throws IOException {try {return mInstaller.createSession(params, mInstallerPackageName, mUserId);} catch (RuntimeException e) {ExceptionUtils.maybeUnwrapIOException(e);throw e;} catch (RemoteException e) {throw e.rethrowFromSystemServer();}
}

看到此方法,发现也是通过远程对象创建的

3、打开Session

public @NonNull Session openSession(int sessionId) throws IOException {try {return new Session(mInstaller.openSession(sessionId));} catch (RuntimeException e) {ExceptionUtils.maybeUnwrapIOException(e);throw e;} catch (RemoteException e) {throw e.rethrowFromSystemServer();}
}

此方法中根据远程对象打开Session的返回值创建本地Session。
由上面最后安装界面可知,在最后创建异步任务中打开Session,打开之后就是调用openWrite方法开始进行APK安装包传输

4、打开Session后打开写

public @NonNull OutputStream openWrite(@NonNull String name, long offsetBytes,long lengthBytes) throws IOException {try {if (ENABLE_REVOCABLE_FD) {return new ParcelFileDescriptor.AutoCloseOutputStream(mSession.openWrite(name, offsetBytes, lengthBytes));} else {final ParcelFileDescriptor clientSocket = mSession.openWrite(name,offsetBytes, lengthBytes);return new FileBridge.FileBridgeOutputStream(clientSocket);}} catch (RuntimeException e) {ExceptionUtils.maybeUnwrapIOException(e);throw e;} catch (RemoteException e) {throw e.rethrowFromSystemServer();}
}

由此方法可以看出最后还是调用远程Session的openWrite方法。
在安装最后一个界面的最后的异步任务中执行了session.fsync(out)方法,下面看看这个方法

public void fsync(@NonNull OutputStream out) throws IOException {if (ENABLE_REVOCABLE_FD) {if (out instanceof ParcelFileDescriptor.AutoCloseOutputStream) {try {Os.fsync(((ParcelFileDescriptor.AutoCloseOutputStream) out).getFD());} catch (ErrnoException e) {throw e.rethrowAsIOException();}} else {throw new IllegalArgumentException("Unrecognized stream");}} else {if (out instanceof FileBridge.FileBridgeOutputStream) {((FileBridge.FileBridgeOutputStream) out).fsync();} else {throw new IllegalArgumentException("Unrecognized stream");}}
}

这个方法就是将读取到内存中的文件保存到本地。

5、最后页面的commit

public void commit(@NonNull IntentSender statusReceiver) {try {mSession.commit(statusReceiver, false);} catch (RemoteException e) {throw e.rethrowFromSystemServer();}
}

最后调用的还是远程的commit方法,下面看一看远程的PackageInstallerSession

三、PackageInstallerSession方法解析

1、PackageInstallerSession的commit方法

@Override
public void commit(@NonNull IntentSender statusReceiver, boolean forTransfer) {Preconditions.checkNotNull(statusReceiver);final boolean wasSealed;synchronized (mLock) {assertCallerIsOwnerOrRootLocked();assertPreparedAndNotDestroyedLocked("commit");final PackageInstallObserverAdapter adapter = new PackageInstallObserverAdapter(mContext, statusReceiver, sessionId, isInstallerDeviceOwnerLocked(), userId);mRemoteObserver = adapter.getBinder();if (forTransfer) {mContext.enforceCallingOrSelfPermission(Manifest.permission.INSTALL_PACKAGES, null);if (mInstallerUid == mOriginalInstallerUid) {throw new IllegalArgumentException("Session has not been transferred");}} else {if (mInstallerUid != mOriginalInstallerUid) {throw new IllegalArgumentException("Session has been transferred");}}wasSealed = mSealed;if (!mSealed) {try {sealAndValidateLocked();} catch (IOException e) {throw new IllegalArgumentException(e);} catch (PackageManagerException e) {destroyInternal();// Cannot call dispatchFinal synchronous as this might be called from inside the// system server on the main thread. Hence the call back scheduled in// dispachFinal has to be scheduled on a different thread.mHandler.obtainMessage(MSG_SESSION_FINISHED_WITH_EXCEPTION, e).sendToTarget();return;}}// Client staging is fully done at this pointmClientProgress = 1f;computeProgressLocked(true);// This ongoing commit should keep session active, even though client// will probably close their end.mActiveCount.incrementAndGet();mCommitted = true;mHandler.obtainMessage(MSG_COMMIT).sendToTarget();}if (!wasSealed) {// Persist the fact that we've sealed ourselves to prevent// mutations of any hard links we create. We do this without holding// the session lock, since otherwise it's a lock inversion.mCallback.onSessionSealedBlocking(this);}
}

由此方法可以,最后发送了一个MSG_COMMIT方法。下面看一下定义的Handler。

2、PackageInstallerSession的Handler

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;case MSG_SESSION_FINISHED_WITH_EXCEPTION:PackageManagerException e = (PackageManagerException) msg.obj;dispatchSessionFinished(e.error, ExceptionUtils.getCompleteMessage(e),null);break;}return true;}
};

此方法也是相当简单,只需要看commitLocker()方法。

3、PackageInstallerSession的commitLocker方法

private void commitLocked()throws PackageManagerException {if (mDestroyed) {throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, "Session destroyed");}if (!mSealed) {throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, "Session not sealed");}Preconditions.checkNotNull(mPackageName);Preconditions.checkNotNull(mSignatures);Preconditions.checkNotNull(mResolvedBaseFile);.....mRelinquished = true;mPm.installStage(mPackageName, stageDir, stageCid, localObserver, params,mInstallerPackageName, mInstallerUid, user, mCertificates);
}

至此,PackageInstallerSession的工作就完成了。最后,将接力棒给了PackageManagerServices去执行。

四、PackageManagerService解析

1、APK拷贝初始化

1.1、installStage

void installStage(String packageName, File stagedDir, String stagedCid,IPackageInstallObserver2 observer, PackageInstaller.SessionParams sessionParams,String installerPackageName, int installerUid, UserHandle user,Certificate[][] certificates) {.....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, certificates, installReason);params.setTraceMethod("installStage").setTraceCookie(System.identityHashCode(params));msg.obj = params;.....mHandler.sendMessage(msg);
}

从此方法中可以看出发送了一个INIT_COPY消息

1.2、INIT_COPY

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);// If a bind was already initiated we dont really// need to do anything. The pending install// will be processed later on.if (!mBound) {Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "bindingMCS",System.identityHashCode(mHandler));// If this is the only one pending we might// have to bind to the service again.if (!connectToService()) {Slog.e(TAG, "Failed to bind to media container service");params.serviceError();Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "bindingMCS",System.identityHashCode(mHandler));if (params.traceMethod != null) {Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, params.traceMethod,params.traceCookie);}return;} else {// Once we bind to the service, the first// pending request will be processed.mPendingInstalls.add(idx, params);}} else {mPendingInstalls.add(idx, params);// Already bound to the service. Just make// sure we trigger off processing the first request.if (idx == 0) {mHandler.sendEmptyMessage(MCS_BOUND);}}break;}'' '' '}'

再次方法中可以看出首先需要执行连接服务操作,让后将参数添加到mPendingInstalls变量中。首先我们可以看连接服务的方法

private boolean connectToService() {if (DEBUG_SD_INSTALL) Log.i(TAG, "Trying to bind to" +" DefaultContainerService");Intent service = new Intent().setComponent(DEFAULT_CONTAINER_COMPONENT);Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);if (mContext.bindServiceAsUser(service, mDefContainerConn,Context.BIND_AUTO_CREATE, UserHandle.SYSTEM)) {Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);mBound = true;return true;}Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);return false;
}

使用mDefaultContainerConn连接DefaultContainerServer服务,然后看一下这个变量定义处

final private DefaultContainerConnection mDefContainerConn =new DefaultContainerConnection();
class DefaultContainerConnection implements ServiceConnection {public void onServiceConnected(ComponentName name, IBinder service) {if (DEBUG_SD_INSTALL) Log.i(TAG, "onServiceConnected");final IMediaContainerService imcs = IMediaContainerService.Stub.asInterface(Binder.allowBlocking(service));mHandler.sendMessage(mHandler.obtainMessage(MCS_BOUND, imcs));}public void onServiceDisconnected(ComponentName name) {if (DEBUG_SD_INSTALL) Log.i(TAG, "onServiceDisconnected");}
}

可以看出连接成功之后会发送一个MCS_BOUND消息,后边看这个消息的处理过程

1.3、MCS_BOUND

void doHandleMessage(Message msg) {switch (msg.what) {.....case MCS_BOUND: {if (DEBUG_INSTALL) Slog.i(TAG, "mcs_bound");if (msg.obj != null) {mContainerService = (IMediaContainerService) msg.obj;Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "bindingMCS",System.identityHashCode(mHandler));}if (mContainerService == null) {if (!mBound) {// Something seriously wrong since we are not bound and we are not// waiting for connection. Bail out.Slog.e(TAG, "Cannot bind to media container service");for (HandlerParams params : mPendingInstalls) {// Indicate service bind errorparams.serviceError();Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "queueInstall",System.identityHashCode(params));if (params.traceMethod != null) {Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER,params.traceMethod, params.traceCookie);}return;}mPendingInstalls.clear();} else {Slog.w(TAG, "Waiting to connect to media container service");}} else if (mPendingInstalls.size() > 0) {HandlerParams params = mPendingInstalls.get(0);if (params != null) {Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "queueInstall",System.identityHashCode(params));Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "startCopy");if (params.startCopy()) {  //进行APK文件的拷贝// We are done...  look for more work or to// go idle.if (DEBUG_SD_INSTALL) Log.i(TAG,"Checking for more work or unbind...");// Delete pending installif (mPendingInstalls.size() > 0) {mPendingInstalls.remove(0);}if (mPendingInstalls.size() == 0) {if (mBound) {if (DEBUG_SD_INSTALL) Log.i(TAG,"Posting delayed MCS_UNBIND");removeMessages(MCS_UNBIND);Message ubmsg = obtainMessage(MCS_UNBIND);// Unbind after a little delay, to avoid// continual thrashing.sendMessageDelayed(ubmsg, 10000);}} else {// There are more pending requests in queue.// Just post MCS_BOUND message to trigger processing// of next pending install.if (DEBUG_SD_INSTALL) Log.i(TAG,"Posting MCS_BOUND for next work");mHandler.sendEmptyMessage(MCS_BOUND);}}Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);}} else {// Should never happen ideally.Slog.w(TAG, "Empty queue");}break;}.....}

在这里面params.startCopy()这行代码就是要开始APK安装包的拷贝过程,可以查看InstallParams类中的startcopy方法。可以看到InstallParams继承HandlerParams,startCopy()方法的实现在HandlerParams类中。接下来看看HandlerParams中startCopy()方法

final boolean startCopy() {boolean res;.....handleStartCopy();//进行拷贝文件.....handleReturnCode();//处理拷贝操作返回的操作return res;
}

这个方法中主要有两个方法,一个是进行APK拷贝文件的操作,一个是处理拷贝文件后的操作。首先看拷贝文件的操作

2、apk拷贝操作

2.1.1、handleStartCopy

从源码中可以看出在HandleParams类中并没有实现,是一个抽象方法。再进入PackageManageService开始创建的是InstallParams对象,可以看InstallParams中handleStartCopy()方法实现
public void handleStartCopy() throws RemoteException {

    int ret = PackageManager.INSTALL_SUCCEEDED;......if (ret == PackageManager.INSTALL_SUCCEEDED) {.....}final InstallArgs args = createInstallArgs(this); //创建用于实际拷贝APK安装包的对象mArgs = args;if (ret == PackageManager.INSTALL_SUCCEEDED) {......if (!origin.existing && requiredUid != -1&& isVerificationEnabled(verifierUser.getIdentifier(), installFlags, installerUid)) { //判断是否有权限......}} else {ret = args.copyApk(mContainerService, true); //拷贝APK安装包}}mRet = ret;
}

从此方法可以看出APK的拷贝工作又交给另外的对象进行处理,看一下InstallArgs的创建时根据变量来创建不同的对象。这里不用看其他的,直接看FileInstallArgs中的copyApk()方法。

2.1.2、copyApk

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 {if (origin.staged) { //在创建的时候设置为ture,因为在一开始已经通过Session拷贝APKif (DEBUG_INSTALL) Slog.d(TAG, origin.file + " already staged; skipping copy");codeFile = origin.file;resourceFile = origin.file;return PackageManager.INSTALL_SUCCEEDED;}.....
}

在copyApk()中又调用doCopyApk()方法进行拷贝工作。在新版本中主要是通过Session拷贝APK,因此在doCopyApk()方法中首先进行判断,而此状态变量在一开始创建的时候已经初始化为true。我感觉有必要看一下次变量初始化的地方

static OriginInfo fromStagedFile(File file) {return new OriginInfo(file, null, true, false);
}
static OriginInfo fromStagedContainer(String cid) {return new OriginInfo(null, cid, true, false);
}
private OriginInfo(File file, String cid, boolean staged, boolean existing) {this.file = file;this.cid = cid;this.staged = staged;this.existing = existing;''''''
}

由此可见,拷贝APK的工作在doCopyApk()开始之初已经返回。接下来看拷贝APK操作之后操作。

2.2.1、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);}
}

擦,直接调用了另外一个方法

2.2.2processPendingInstall

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 returnedPackageInstalledInfo res = new PackageInstalledInfo();res.setReturnCode(currentStatus);res.uid = -1;res.pkg = null;res.removedInfo = null;if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {args.doPreInstall(res.returnCode);//1、apk安装前操作,删除安装残留文件synchronized (mInstallLock) {installPackageTracedLI(args, res); //2、apk安装}args.doPostInstall(res.returnCode, res.uid);//3、}// A restore should be performed at this point if (a) the install// succeeded, (b) the operation is not an update, and (c) the new// package has not opted out of backup participation.final boolean update = res.removedInfo != null&& res.removedInfo.removedPackage != null;final int flags = (res.pkg == null) ? 0 : res.pkg.applicationInfo.flags;boolean doRestore = !update&& ((flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0);......if (res.returnCode == PackageManager.INSTALL_SUCCEEDED && doRestore) {//从前面可知update和doRestore都为false......}if (!doRestore) {......Message msg = mHandler.obtainMessage(POST_INSTALL, token, 0);//发送消息mHandler.sendMessage(msg);}}});
}

此方法中又四步操作

(1)doPreInstall方法进行安装前操作,删除安装残留文件
(2)installPackageTracedLI方法将apk进行解析,然后进行dex到opt文件操作
(3)doPostInstall方法也是将一些残余文件的删除
(4)最后发送POST_INSTALL消息

从上述操作还是到handler中,查看doHandlerMessage消息

void doHandleMessage(Message msg) {switch (msg.what) {.....case POST_INSTALL: {if (DEBUG_INSTALL) Log.v(TAG, "Handling post-install for " + msg.arg1);PostInstallData data = mRunningInstalls.get(msg.arg1);final boolean didRestore = (msg.arg2 != 0);mRunningInstalls.delete(msg.arg1);if (data != null) {......handlePackagePostInstall(parentRes, grantPermissions, killApp,virtualPreload, grantedPermissions, didRestore,args.installerPackageName, args.observer);// Handle the child packagesfinal int childCount = (parentRes.addedChildPackages != null)? parentRes.addedChildPackages.size() : 0;for (int i = 0; i < childCount; i++) {PackageInstalledInfo childRes = parentRes.addedChildPackages.valueAt(i);handlePackagePostInstall(childRes, grantPermissions, killApp,virtualPreload, grantedPermissions, false /*didRestore*/,args.installerPackageName, args.observer);}......} break;......}

在此处可以看到执行handlePackagePosetInstall()方法

2.2.3、handlePackagePosetInstall

private void handlePackagePostInstall(PackageInstalledInfo res, boolean grantPermissions,boolean killApp, boolean virtualPreload, String[] grantedPermissions,boolean launchedForRestore, String installerPackage,IPackageInstallObserver2 installObserver) {if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {// Send the removed broadcastsif (res.removedInfo != null) {res.removedInfo.sendPackageRemovedBroadcasts(killApp);}// Now that we successfully installed the package, grant runtime// permissions if requested before broadcasting the install. Also// for legacy apps in permission review mode we clear the permission// review flag which is used to emulate runtime permissions for// legacy apps.if (grantPermissions) {//此处表示已经安装成功,现在在发送广播之前赋予运行权限grantRequestedRuntimePermissions(res.pkg, res.newUsers, grantedPermissions);}......// Send installed broadcasts if the package is not a static shared lib.if (res.pkg.staticSharedLibName == null) {mProcessLoggingHandler.invalidateProcessLoggingBaseApkHash(res.pkg.baseCodePath);// Send added for users that see the package for the first time// sendPackageAddedForNewUsers also deals with system appsint appId = UserHandle.getAppId(res.uid);boolean isSystem = res.pkg.applicationInfo.isSystemApp();sendPackageAddedForNewUsers(packageName, isSystem || virtualPreload,virtualPreload /*startReceiver*/, appId, firstUsers); //发送APK安装成功的消息// Send added for users that don't see the package for the first timeBundle extras = new Bundle(1);extras.putInt(Intent.EXTRA_UID, res.uid);if (update) {extras.putBoolean(Intent.EXTRA_REPLACING, true);}sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,extras, 0 /*flags*/,null /*targetPackage*/, null /*finishedReceiver*/, updateUsers);//发送APK安装成功的消息......for (int userId : firstUsers) {PackageInfo info = getPackageInfo(packageName, /*flags*/ 0, userId);// There's a race currently where some install events may interleave with an uninstall.// This can lead to package info being null (b/36642664).if (info != null) {mDexManager.notifyPackageInstalled(info, userId);}}}// If someone is watching installs - notify themif (installObserver != null) {try {Bundle extras = extrasForInstallResult(res);installObserver.onPackageInstalled(res.name, res.returnCode,res.returnMsg, extras);} catch (RemoteException e) {Slog.i(TAG, "Observer no longer exists.");}}
}

此函数主要是受运行权限,并且发送已经安装成功广播。至此,此安装过程完成。附上函数调用时序图

【Android】APK应用安装过程源码解析相关推荐

  1. java apk安装路径_android apk安装过程源码解析

    前言: 前一篇博客分析了一下PackageManagerService是如何解析apk的以及我们如何解析未安装apk中的androidManifest.xml文件.解析完肯定要安装的,索性写一篇关于a ...

  2. Android Launcher启动应用程序流程源码解析

    带着问题看源码 点击桌面Launcher图标后做了哪些工作? 应用程序什么时候被创建的? Application和MainActivity的onCreate()方法什么时候被调用的? 概述 在Andr ...

  3. android 系统按键音framework流程源码解析

    android 系统按键音framework源码解析(基于android 9.0) 今天来看下android中按键音的处理,首先看下按键是在那里开启的.然后再看看当按下按键后一个按键音是怎么播放出来的 ...

  4. android系统加载主题的流程,详解Android布局加载流程源码

    一.首先看布局层次 看这么几张图 我们会发现DecorView里面包裹的内容可能会随着不同的情况而变化,但是在Decor之前的层次关系都是固定的.即Activity包裹PhoneWindow,Phon ...

  5. Android服务注册完整过程源码分析

    前面从不同片段分析了Android的Binder通信机制,本文结合前面介绍的内容,对整个Android的Binder通信过程进行一次完整的分析.分析以AudioService服务的注册过程为例. 由于 ...

  6. Android多线程之ArrayBlockingQueue源码解析

    阻塞队列系列 Android多线程之LinkedBlockingQueue源码解析 Android多线程之SynchronousQueue源码解析 Andorid多线程之DelayQueue源码分析 ...

  7. android 闪退解决方案,Android apk无法安装及闪退问题解决办法

    Android apk无法安装及闪退问题 app在部分手机上(低版本)打不开或打开就闪退的问题 之前做项目集成的是 环信的sdk ,环信的sdk 确实很好,客服 也很给力.但是在集成的过程中发现,ap ...

  8. Android多线程之IntentService源码解析

    想要了解 IntentService 的工作原理需要先对 Android 系统中以 Handler.Looper.MessageQueue 组成的异步消息处理机制以及 HandlerThread 有所 ...

  9. Android服务查询完整过程源码分析

    Android服务注册完整过程源码分析中从上到下详细分析了Android系统的服务注册过程,本文同样针对AudioService服务来介绍Android服务的查询过程. 客户端进程数据发送过程 pri ...

  10. Android 数据Parcel序列化过程源码分析

    在Android系统中,所有的服务都必须注册到ServiceManger中,当客户进程需要请求某一服务时,首先从服务管家ServiceManger中查找出该服务,然后通过RPC远程调用的方式使用该服务 ...

最新文章

  1. Nginx的location、root、alias指令用法和区别
  2. 拖着3个箱子,跨越太平洋,求学美帝 那一年我19岁
  3. java 数组中某个数出现的概率_剑指Offer解题报告(Java版)——排序数组中某个数的个数 38...
  4. python中递归函数写法_Python之递归函数
  5. ES6(ECMAScript2015)/01/ES6简介
  6. Spring的IoC理解,代码进行详解
  7. python pop函数 索引_[python] 字典和列表中的pop()函数
  8. NX拉伸实体实例 UF_MODL_create_extruded
  9. 小米电视联网后显示无法解析小米电视服务器,小米电视连上无线不能上网怎么回事?教你解决办法...
  10. 排序算法——梳排序 Comb sort
  11. At least one JAR was scanned for TLDs解决办法
  12. 华为鸿蒙系统有没有畅玩7c,华为荣耀畅玩7C有什么新功能
  13. 使用Qt合并图片的算法
  14. 韩寒郭敬明开启音乐精准营销时代
  15. 计算机实验word,实验六计算机基础——word段落设置
  16. Unity内存管理的原理
  17. 高中教师计算机面试什么时候,高中信息技术教师资格证备考经验分享(面试篇)...
  18. C#入门4——计算自由落体运动
  19. ORM-Dapper学习二.关于Dapper
  20. Win7下,使用VM虚拟机,安装苹果Mac OS经验分享

热门文章

  1. 显示前半内容后半内容用省略号_省略号前后的标点用法
  2. SSL单向认证和双向认证
  3. ByteBuffer的原理和使用详解
  4. 【LTE基础】SRVCC(Single Radio Voice Call Continuity 双模单待无线语音呼叫连续性)技术研究背景
  5. 以太坊蜜罐智能合约分析
  6. 入门学习计算机第十三天—初识指针
  7. 数据仓库项目实例(马蜂窝数据仓库)
  8. 什么是 480i、576i、480p、1080i、720p?什么是 HDTV?
  9. HashMap的put方法
  10. android reboot 消息,android reboot 流程