首先我们要清楚总体可以分成如下几种安装的情况
- 系统开机的应用安装,安装的是系统级别的应用,用户在没有获取到root权限的情况下无法卸载的应用
- adb安装的应用,没有安装界面
- 第三方市场下载的应用,此处要分情况,部分是通过电脑的客户端安装的没有安装的界面,部分是手机上的市场安装的,会有安装的界面
那么我们就对几种情况一一分析它的安装流程
开机安装
-
首先在开机的时候systemServer会启动PackageMangerService来
具体是通过systemServer的main()->init1()->init2()->new ServerThread()->构建PMS
以上都是SystemServer.cpp中的代码,了解即可。我们前往PMS查看接下去的逻辑:在PMS的构造方法中我们看到了一大坨的逻辑
,源码过于长,我们直接在构造方法里面搜索如下这两个方法:
scanDirTracedLI //最后还是会调用下面的方法,只是对下面的方法做一下跟踪
scanDirLI
就是通过这两个方法进行应用的安装,从scanDirLI开始追踪:
private void scanDirLI(File dir, final int parseFlags, int scanFlags, long currentTime) {
final File[] files = dir.listFiles();
...
for (File file : files) {
final boolean isPackage = (isApkFile(file) || file.isDirectory())
&& !PackageInstallerService.isStageName(file.getName());
if (!isPackage) {
// Ignore entries which are not packages
continue;
}
try {
scanPackageTracedLI(file, parseFlags | PackageParser.PARSE_MUST_BE_APK,
scanFlags, currentTime, null);
} catch (PackageManagerException e) {
Slog.w(TAG, "Failed to parse " + file + ": " + e.getMessage());
// Delete invalid userdata apps
if ((parseFlags & PackageParser.PARSE_IS_SYSTEM) == 0 &&
e.error == PackageManager.INSTALL_FAILED_INVALID_APK) {
logCriticalInfo(Log.WARN, "Deleting invalid package at " + file);
removeCodePathLI(file);
}
}
}
}
继续看scanPackageTracedLI方法:
private PackageParser.Package scanPackageLI(File scanFile, int parseFlags, int scanFlags,
long currentTime, UserHandle user) throws PackageManagerException {
if (DEBUG_INSTALL) Slog.d(TAG, "Parsing: " + scanFile);
PackageParser pp = new PackageParser();
pp.setSeparateProcesses(mSeparateProcesses);
pp.setOnlyCoreApps(mOnlyCore);
pp.setDisplayMetrics(mMetrics);
if ((scanFlags & SCAN_TRUSTED_OVERLAY) != 0) {
parseFlags |= PackageParser.PARSE_TRUSTED_OVERLAY;
}
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parsePackage");
final PackageParser.Package pkg;
try {
pkg = pp.parsePackage(scanFile, parseFlags);
} catch (PackageParserException e) {
throw PackageManagerException.from(e);
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
return scanPackageLI(pkg, scanFile, parseFlags, scanFlags, currentTime, user);
}
我们看到从这个方法开始parsePackage, 通过PackageParser解析apk文件,我们可以去到PackageParser的文件中看到这个parsePackage方法就是把安装包中的配置文件解析出来,并且保存到Package中返回出来,中间辗转反侧对于配置文件各种分情况进行parse,最最主要的是parseBaseApkCommon方法,这个方法对manifast文件的标签进行解析,而其中还调用了parseBaseApplication方法对application标签进行解析。
然后再scanPackageLI->scanPackageDirtyLI,在后者的方法中,我们会把之前解析出来的
PackageParser.Provider p = pkg.providers.get(i);
p.info.processName = fixProcessName(pkg.applicationInfo.processName,
p.info.processName, pkg.applicationInfo.uid);
mProviders.addProvider(p);
PackageParser.Service s = pkg.services.get(i);
s.info.processName = fixProcessName(pkg.applicationInfo.processName,
s.info.processName, pkg.applicationInfo.uid);
mServices.addService(s);
等等,我们会把相应的provider,service,receiver,activity全都保存到PMS的成员集合类中去.
至此系统应用的安装算是全部完成了,也许你会说我们并有看到什么install的什么方法,其实最最精髓的就是上面最后那一坨1000+行的方法,这里面把所有parser出来的信息全部保存到PMS中去了,而这就是所谓的安装,安装就是一个吧apk中的信息解析出来保存给PMS的过程,然后在launcher上生成一个图标,以供用户打开,仅此而已。
从网络上下载应用安装
最终都是通过如下的方式去进行安装的
String fileName = "/mnt/usb/sda4/test.apk";
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(new File(fileName)), "application/vnd.android.package-archive");
startActivity(intent);
启动的就是PackageInstallerActivity,而这个activity是Android的系统应用之一,源码在package/app/packageinstaller中,具体源码可以前往下面链接查看:
https://github.com/android/platform_packages_apps_packageinstaller/blob/master/src/com/android/packageinstaller/PackageInstallerActivity.java
我们在其onCreate方法中看到之后会调用checkIfAllowedAndInitiateInstall->initiateInstall-> startInstallConfirm,会处理有关权限的一些问题
之后如果我们点击下方的ok按钮,会进入到安装流程:
public void onClick(View v) {
if (v == mOk) {
if (mOkCanInstall || mScrollView == null) {
if (mSessionId != -1) {
mInstaller.setPermissionsResult(mSessionId, true);
clearCachedApkIfNeededAndFinish();
} else {
startInstall();
}
} else {
mScrollView.pageScroll(View.FOCUS_DOWN);
}
} else if (v == mCancel) {
...
}
我们接着去startInstall方法看:
private void startInstall() {
// Start subactivity to actually install the application
Intent newIntent = new Intent();
...
newIntent.setClass(this, InstallAppProgress.class);
...
startActivity(newIntent);
finish();
}
跳到InstallAppProgress这个activity,很明显就是我们点击安装之后进入的安装界面,有个进度条一直在闪的那个正在安装的界面。
onCreate()->initView():
void initView() {
setContentView(R.layout.op_progress);
...
if ("package".equals(mPackageURI.getScheme())) {
try {
pm.installExistingPackage(mAppInfo.packageName);
onPackageInstalled(PackageInstaller.STATUS_SUCCESS);
} catch (PackageManager.NameNotFoundException e) {
onPackageInstalled(PackageInstaller.STATUS_FAILURE_INVALID);
}
} else {
...
mInstallHandler.post(new Runnable() {
@Override
public void run() {
doPackageStage(pm, params);
}
});
}
}
精简了一下,主要是这两个方法。
installExistingPackage
一个是pm.installExistingPackage,我们知道实际上调用的就是ApplicationPackageManager的installExistingPackage
@Override
public int installExistingPackage(String packageName) throws NameNotFoundException {
return installExistingPackageAsUser(packageName, mContext.getUserId());
}
@Override
public int installExistingPackageAsUser(String packageName, int userId)
throws NameNotFoundException {
try {
int res = mPM.installExistingPackageAsUser(packageName, userId);
if (res == INSTALL_FAILED_INVALID_URI) {
throw new NameNotFoundException("Package " + packageName + " doesn't exist");
}
return res;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
继续,我们前往PMS的installExistingPackageAsUser方法,具体方法不贴出,实际的意思就是这个安装包已经装过了,只是当前用户下还没有安装,就会对后台的设置做一下处理,实际并不涉及到安装的东西的。我们来看另外一个方法
doPackageStage
private void doPackageStage(PackageManager pm, PackageInstaller.SessionParams params) {
final PackageInstaller packageInstaller = pm.getPackageInstaller();
PackageInstaller.Session session = null;
try {
session = packageInstaller.openSession(sessionId);
...
session.commit(pendingIntent.getIntentSender());
} catch (IOException e) {
onPackageInstalled(PackageInstaller.STATUS_FAILURE);
} finally {
IoUtils.closeQuietly(session);
}
}
当中对session做了很多操作,我们只看最主要的最后一个session.commit,
- PackageInstaller.Session
这个session是在5.0之后新加进来的一种安装处理的方式,我们可以把它理解为一种记录,就和浏览器中的session的作用是一样的,它用来保存应用安装的所有信息,当我们的手机如果因为异常只安装了一半退出了之后,当我们下回再次打开手机的时候,可以继续上次的安装。
ok,我们接着进到PackageInstaller.java看session的commit方法
public void commit(@NonNull IntentSender statusReceiver) {
try {
mSession.commit(statusReceiver);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
这里的mSession我们看到是
private IPackageInstallerSession mSession;
/** {@hide} */
public Session(IPackageInstallerSession session) {
mSession = session;
}
万恶的binder,回到上面看session的定义:
session = packageInstaller.openSession(sessionId);
找openSession方法
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();
}
}
mInstaller是什么,
private final IPackageInstaller mInstaller;
又来一个binder,我们就不继续一步一步贴了,相信读者也可以自己找到最终的实现是在PackageInstallerSession
@Override
public void commit(IntentSender statusReceiver) {
...
final PackageInstallObserverAdapter adapter = new PackageInstallObserverAdapter(mContext,
statusReceiver, sessionId, mIsInstallerDeviceOwner, userId);
mHandler.obtainMessage(MSG_COMMIT, adapter.getBinder()).sendToTarget();
}
省去部分直接看到最后给handler发了条消息
mHandler = new Handler(looper, mHandlerCallback);
private final Handler.Callback mHandlerCallback = new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
// Cache package manager data without the lock held
final PackageInfo pkgInfo = mPm.getPackageInfo(
params.appPackageName, PackageManager.GET_SIGNATURES /*flags*/, userId);
final ApplicationInfo appInfo = mPm.getApplicationInfo(
params.appPackageName, 0, userId);
synchronized (mLock) {
if (msg.obj != null) {
mRemoteObserver = (IPackageInstallObserver2) msg.obj;
}
try {
commitLocked(pkgInfo, appInfo);
} catch (PackageManagerException e) {
final String completeMsg = ExceptionUtils.getCompleteMessage(e);
Slog.e(TAG, "Commit of session " + sessionId + " failed: " + completeMsg);
destroyInternal();
dispatchSessionFinished(e.error, completeMsg, null);
}
return true;
}
}
};
重要的就是那句commitLocked,这个方法比较长,依然是做一些权限和配置方面的事情,能在方法的最后看到
mPm.installStage(mPackageName, stageDir, stageCid, localObserver, params,
installerPackageName, installerUid, user, mCertificates);
我们去到PMS的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 InstallParams params = new InstallParams(origin, null, observer,
sessionParams.installFlags, installerPackageName, sessionParams.volumeUuid,
verificationInfo, user, sessionParams.abiOverride,
sessionParams.grantedRuntimePermissions, certificates);
...
msg.obj = params;
...
mHandler.sendMessage(msg);
}
我们把整个过程精简到上面三局代码,发送一个what为INIT_COPY的消息
case INIT_COPY: {
HandlerParams params = (HandlerParams) msg.obj;
int idx = mPendingInstalls.size();
if (DEBUG_INSTALL) Slog.i(TAG, "init_copy idx=" + idx + ": " + params);
// 此处为了连接服务,初始情况都为false,待服务脸上之后就会把mBound设置为true,就可以进入下面条件
// 因此这里我们直接进入else的条件
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;
}
直接在最后看到发送了MCS_BOUND消息
case MCS_BOUND: {
...
//服务已经连上,直接跳到else if
if (mContainerService == null) {
...
}
//等待安装的队列肯定大于0
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");
//开始进入到copy
if (params.startCopy()) {
// 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 install
if (mPendingInstalls.size() > 0) {
mPendingInstalls.remove(0);
}
//如果已经把要安装的都处理完了,那么久断开服务连接
//否则就再发送一个MCS_BOUND信息,继续循环处理
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;
}
我们直接进入到startCopy
final boolean startCopy() {
boolean res;
try {
if (DEBUG_INSTALL) Slog.i(TAG, "startCopy " + mUser + ": " + this);
//尝试的次数超过了最大尝试次数,就宣告失败
if (++mRetries > MAX_RETRIES) {
Slog.w(TAG, "Failed to invoke remote methods on default container service. Giving up");
mHandler.sendEmptyMessage(MCS_GIVE_UP);
handleServiceError();
return false;
} else {
//开始进行拷贝
handleStartCopy();
res = true;
}
} catch (RemoteException e) {
if (DEBUG_INSTALL) Slog.i(TAG, "Posting install MCS_RECONNECT");
mHandler.sendEmptyMessage(MCS_RECONNECT);
res = false;
}
//处理return code
handleReturnCode();
return res;
}
我们先来看一下handleStartCopy方法,发现时抽象方法,具体的实现,我们网上可以找到是 InstallParams。
public void handleStartCopy() throws RemoteException {
int ret = PackageManager.INSTALL_SUCCEEDED;
// 决定是安装在手机内还是sdcard中,设置对应标志位
if (origin.staged) {
if (origin.file != null) {
installFlags |= PackageManager.INSTALL_INTERNAL;
installFlags &= ~PackageManager.INSTALL_EXTERNAL;
} else if (origin.cid != null) {
installFlags |= PackageManager.INSTALL_EXTERNAL;
installFlags &= ~PackageManager.INSTALL_INTERNAL;
} else {
throw new IllegalStateException("Invalid stage location");
}
}
...
// 检查APK的安装位置是否正确
if (onInt && onSd) {
// Check if both bits are set.
Slog.w(TAG, "Conflicting flags specified for installing on both internal and external");
ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
} else if (onSd && ephemeral) {
Slog.w(TAG, "Conflicting flags specified for installing ephemeral on external");
ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
} else {
...
}
...
// createInstallArgs用于创建一个安装参数对象
final InstallArgs args = createInstallArgs(this);
if (ret == PackageManager.INSTALL_SUCCEEDED) {
...
// 调用InstallArgs的copyApk函数
ret = args.copyApk(mContainerService, true);
}
}
mRet = ret;
}
主要工作就是复制apk消息到指定位置
再来看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);
}
}
我们一路看,processPendingInstall()->installPackageTracedLI()->installPackageLI(),整个方法比较长,主要做了清单文件解析和获取证书,提取签名的工作,你可以从这个方法中找到如下代码段,就是这段代码处理了安装
try (PackageFreezer freezer = freezePackageForInstall(pkgName, installFlags,
"installPackageLI")) {
if (replace) {
// 4.更新已经存在的packages
replacePackageLIF(pkg, parseFlags, scanFlags | SCAN_REPLACING, args.user,
installerPackageName, res);
} else {
// 5.安装新的packages
installNewPackageLIF(pkg, parseFlags, scanFlags | SCAN_DELETE_DATA_ON_FAILURES,
args.user, installerPackageName, volumeUuid, res);
}
}
我们继续
/*
* Install a non-existing package.
*/
private void installNewPackageLIF(PackageParser.Package pkg, final int policyFlags,
int scanFlags, UserHandle user, String installerPackageName, String volumeUuid,
PackageInstalledInfo res) {
...
try {
PackageParser.Package newPackage = scanPackageTracedLI(pkg, policyFlags, scanFlags,
System.currentTimeMillis(), user);
updateSettingsLI(newPackage, installerPackageName, null, res, user);
if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
prepareAppDataAfterInstallLIF(newPackage);
} else {
// Remove package from internal structures, but keep around any
// data that might have already existed
deletePackageLIF(pkgName, UserHandle.ALL, false, null,
PackageManager.DELETE_KEEP_DATA, res.removedInfo, true, null);
}
} catch (PackageManagerException e) {
res.setError("Package couldn't be installed in " + pkg.codePath, e);
}
...
}
经过删减省略,我们看到了我们熟悉的方法scanPackageTracedLI,再具体的步骤见上面系统应用安装。
最后我们再来看一下另外一种ADB的安装
adb安装
当我们在在命令行窗口输入adb install的时候实际上,系统会帮我们运行如下的程序
db_commandline
install_app_legacy or install_app
pm_command
send_shell_command
Pm.runInstall()
这个过程会把apk文件copy到data/local/tmp/目录下,然后向shell服务发送pm命令安装apk,最后调用Pm.runInstall()方法来安装apk。
我们可以看到这个方法:
private int runInstall() throws RemoteException {
...
final int sessionId = doCreateSession(params.sessionParams,
params.installerPackageName, params.userId);
try {
if (inPath == null && params.sessionParams.sizeBytes == 0) {
System.err.println("Error: must either specify a package size or an APK file");
return 1;
}
if (doWriteSession(sessionId, inPath, params.sessionParams.sizeBytes, "base.apk",
false /*logSuccess*/) != PackageInstaller.STATUS_SUCCESS) {
return 1;
}
if (doCommitSession(sessionId, false /*logSuccess*/)
!= PackageInstaller.STATUS_SUCCESS) {
return 1;
}
System.out.println("Success");
return 0;
} finally {
try {
mInstaller.abandonSession(sessionId);
} catch (Exception ignore) {
}
}
}
我们依然省略着看,把上面这一段略去,直接在最下面看到三个重要的方法,doCreateSession,doWriteSession,doCommitSession,直接点开最后一个
private int doCommitSession(int sessionId, boolean logSuccess) throws RemoteException {
PackageInstaller.Session session = null;
try {
session = new PackageInstaller.Session(
mInstaller.openSession(sessionId));
final LocalIntentReceiver receiver = new LocalIntentReceiver();
session.commit(receiver.getIntentSender());
final Intent result = receiver.getResult();
final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
PackageInstaller.STATUS_FAILURE);
if (status == PackageInstaller.STATUS_SUCCESS) {
if (logSuccess) {
System.out.println("Success");
}
} else {
System.err.println("Failure ["
+ result.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE) + "]");
}
return status;
} finally {
IoUtils.closeQuietly(session);
}
}
看到熟悉的session.commit了么,
总结
下面是有安装界面的app安装流程,中途插入adb install以及没有安装界面的系统安装。
- PackageInstallerActivity的onCreate();
- 根据从Intent中获取的Data的scheme的不同,调用不同的处理逻辑安装App;
- 解析App文件到Package对象;
- initInstall()函数获取apk中的权限信息;
- onClick()监听用户点击同意安装按钮;
- 启动InstallAppProcess Activity;
- onCrete()函数调用
- initView()函数;
- 继续调用doPackageStage()函数,做session.commit();
此处插入adb install 1.Pm.runInstall() 2.Pm.doCommitSession()
- 通过Handler机制启动了startCopy()函数;
- startCopy()调用了启动了handlerStartCopy()将apk拷贝到/data/app、pkg-name/和把.so文件拷贝到/data/app/pkg-name/lib/的
- handlerReturnCode()函数;
- handlerReturnCode调用了installPackageLI()函数;
- 在installPackageLI()中,首先调用了collectCertificates()函数对apk的签名做了校验,确保apk没有被非法修改(修改其中的文件);
接着,调用collectManifestDigest()函数计算了Manifest.xml的文件摘要,并且存在了Package的manifestDigest成员下;
接着,将从apk文件中提提取的manifest计算摘要和之前installer解析到的进行比较,若不匹配,则抛出异常。
然后,对App升级或者使用了共享属性sharedUid的情况校验待安装的App的证书是否匹配,并就升级方式做了具体处理,这里有两个问题,就是升级过程中的Upgrade keysets升级方式和Signature[]的获得不明朗。 - 根据是升级App还是安装新App,调用不同的函数进行处理环节。
此处插入系统应用安装 1.scanPackageTracedLI 2.scanPackageLI 3.scanPackageDirtyLI
- 最后在scanPackageDirtyLI方法中,把所有的provider,service,receiver,activity从parser中拿出来保存到PMS中。