【Android】APK应用安装过程源码解析
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应用安装过程源码解析相关推荐
- java apk安装路径_android apk安装过程源码解析
前言: 前一篇博客分析了一下PackageManagerService是如何解析apk的以及我们如何解析未安装apk中的androidManifest.xml文件.解析完肯定要安装的,索性写一篇关于a ...
- Android Launcher启动应用程序流程源码解析
带着问题看源码 点击桌面Launcher图标后做了哪些工作? 应用程序什么时候被创建的? Application和MainActivity的onCreate()方法什么时候被调用的? 概述 在Andr ...
- android 系统按键音framework流程源码解析
android 系统按键音framework源码解析(基于android 9.0) 今天来看下android中按键音的处理,首先看下按键是在那里开启的.然后再看看当按下按键后一个按键音是怎么播放出来的 ...
- android系统加载主题的流程,详解Android布局加载流程源码
一.首先看布局层次 看这么几张图 我们会发现DecorView里面包裹的内容可能会随着不同的情况而变化,但是在Decor之前的层次关系都是固定的.即Activity包裹PhoneWindow,Phon ...
- Android服务注册完整过程源码分析
前面从不同片段分析了Android的Binder通信机制,本文结合前面介绍的内容,对整个Android的Binder通信过程进行一次完整的分析.分析以AudioService服务的注册过程为例. 由于 ...
- Android多线程之ArrayBlockingQueue源码解析
阻塞队列系列 Android多线程之LinkedBlockingQueue源码解析 Android多线程之SynchronousQueue源码解析 Andorid多线程之DelayQueue源码分析 ...
- android 闪退解决方案,Android apk无法安装及闪退问题解决办法
Android apk无法安装及闪退问题 app在部分手机上(低版本)打不开或打开就闪退的问题 之前做项目集成的是 环信的sdk ,环信的sdk 确实很好,客服 也很给力.但是在集成的过程中发现,ap ...
- Android多线程之IntentService源码解析
想要了解 IntentService 的工作原理需要先对 Android 系统中以 Handler.Looper.MessageQueue 组成的异步消息处理机制以及 HandlerThread 有所 ...
- Android服务查询完整过程源码分析
Android服务注册完整过程源码分析中从上到下详细分析了Android系统的服务注册过程,本文同样针对AudioService服务来介绍Android服务的查询过程. 客户端进程数据发送过程 pri ...
- Android 数据Parcel序列化过程源码分析
在Android系统中,所有的服务都必须注册到ServiceManger中,当客户进程需要请求某一服务时,首先从服务管家ServiceManger中查找出该服务,然后通过RPC远程调用的方式使用该服务 ...
最新文章
- Nginx的location、root、alias指令用法和区别
- 拖着3个箱子,跨越太平洋,求学美帝 那一年我19岁
- java 数组中某个数出现的概率_剑指Offer解题报告(Java版)——排序数组中某个数的个数 38...
- python中递归函数写法_Python之递归函数
- ES6(ECMAScript2015)/01/ES6简介
- Spring的IoC理解,代码进行详解
- python pop函数 索引_[python] 字典和列表中的pop()函数
- NX拉伸实体实例 UF_MODL_create_extruded
- 小米电视联网后显示无法解析小米电视服务器,小米电视连上无线不能上网怎么回事?教你解决办法...
- 排序算法——梳排序 Comb sort
- At least one JAR was scanned for TLDs解决办法
- 华为鸿蒙系统有没有畅玩7c,华为荣耀畅玩7C有什么新功能
- 使用Qt合并图片的算法
- 韩寒郭敬明开启音乐精准营销时代
- 计算机实验word,实验六计算机基础——word段落设置
- Unity内存管理的原理
- 高中教师计算机面试什么时候,高中信息技术教师资格证备考经验分享(面试篇)...
- C#入门4——计算自由落体运动
- ORM-Dapper学习二.关于Dapper
- Win7下,使用VM虚拟机,安装苹果Mac OS经验分享