1.1 APK安装和install
installd服务是用来执行程序包的安装与卸载的。
1.1.1 参考资料
//各版本支持的命令
http://blog.csdn.net/lusing/article/details/52166533
//installer overview
http://blog.csdn.net/lusing/article/details/52223903
//Android中installd进程存在的意义
http://blog.csdn.net/liqingxu2005/article/details/43447941
//install源码分析4.4
http://blog.csdn.net/yangwen123/article/details/11104397
install 源码分析7.0
http://blog.csdn.net/gaugamela/article/details/52769139
//8.0 install源码有很大变化,概述见本文,详细的需要自行分析代码
//apk安装过程PMS的源码分析
http://blog.csdn.net/wocao1226/article/details/51161990
//apk安装过程overview
http://blog.csdn.net/u012481172/article/details/49981673
//4种安装方法的代码分析,代码过时,可参考分析问题的方法
http://blog.csdn.net/wh_19910525/article/details/7909686
1.1.2 初始过程的变化
在android 7.0,installd的启动设置不再放在init.rc里面了,放在frameworks\native\cmds\installd\installd.rc,并frameworks\native\cmds\installd\Android.mk 里设置这个rc文件进行编译,
LOCAL_STATIC_LIBRARIES := libdiskusage
LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.mk
LOCAL_INIT_RC :=installd.rc
LOCAL_CLANG := true
include $(BUILD_EXECUTABLE)
在android8.0,又发生了一些变化,install.rc里创建了很多目录,并将rc文件放在frameworks/native/cmds/installd/Android.bp进行编译,
cc_binary {
name:"installd",
defaults:["installd_defaults"],
srcs:["installd.cpp"],
static_libs:["libdiskusage"],
init_rc: ["installd.rc"],
}
1.1.3 IPC变化
在android7.0及之前,IPC通过socket完成;
在android8.0,IPC通过binder实现。
在下面文件可看到变化
frameworks\base\core\java\com\android\internal\os\Installer.java
frameworks\native\cmds\installd\
1.1.4 Apk安装
Apk安装有几种常见的场景:系统内置apk的安装,adb install安装,应用市场下载后点击安装或本地文件点击安装,静默安装等。
//开机PKMS扫描、解析 4.4
http://blog.csdn.net/luoshengyang/article/details/6766010
//安装源码分析,开机安装的java层 6.0
http://blog.csdn.net/qq_23547831/article/details/51203482
在7.0开始,安装apk的函数createDataDirsLI()就没有了,使用了新的逻辑,可从PackageManagerService.java的scanPackageDirtyLI继续向下分析代码。
顺便提下,6.0的scanPackageDirtyLI向下调用到installd的接口,操作文件时使用了selinux_android_setfilecon,这里和selinux的文件访问策略有关,有兴趣的可以分析下。
//安装源码分析,点击安装的java层 6.0
http://blog.csdn.net/qq_23547831/article/details/51210682
在应用层,8.0的代码发生了一些变化,下面这个控制安装界面的文件没有了,
/packages/apps/PackageInstaller/src/com/android/packageinstaller/InstallAppProgress.java
,用InstallInstalling取代了它,
主要的文件差异如下,针对不同设备进行了相应处理,处理逻辑的变化就暂不分析了,
1.1.5 8.0 apk安装
安装应用的时候会发一个intent,
PMS解析intent,调用PackageInstallerActivity来启动安装界面,
会调用到PackageInstallerActivity的onCreate—startInstallConfirm,在之后的onClick,执行startInstall,启动android O新增加的安装activity InstallInstalling,
private void startInstall() {
// Startsubactivity to actually install the application
Intent 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 != VerificationParams.NO_UID) {
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();
}
在InstallInstalling的onCreate方法里,有两个主要步骤,一个是PackageParser.parsePackageLite,他解析apk里面的部分信息出来;另一个是getPackageInstaller().createSession,它负责创建一个会话,用于后续的安装,会话是通过binder调用在PackageInstallerServices里面创建的。
PackageParser.PackageLitepkg = PackageParser.parsePackageLite(file, 0);
mSessionId =getPackageManager().getPackageInstaller().createSession(params);
在InstallInstalling.java的onResume启动一个异步task InstallingAsyncTask,
protected void onResume() {
super.onResume();
// This is thefirst onResume in a single life of the activity
if(mInstallingTask == null) {
PackageInstaller installer = getPackageManager().getPackageInstaller();
PackageInstaller.SessionInfo sessionInfo =installer.getSessionInfo(mSessionId);
if(sessionInfo != null && !sessionInfo.isActive()) {
mInstallingTask = new InstallingAsyncTask();
mInstallingTask.execute();
} else {
// we willreceive a broadcast when the install is finished
mCancelButton.setEnabled(false);
setFinishOnTouchOutside(false);
}
}
}
InstallingAsyncTask主要是操作session,session也通过binder方式来操作,主要完成的工作是通过之前建立的session,创建文件夹,将文件拷贝到安装目录下面,
protected PackageInstaller.SessiondoInBackground(Void... params) {
PackageInstaller.Session session;
try {
session =getPackageManager().getPackageInstaller().openSession(mSessionId);
} catch (IOException e) {
returnnull;
}
session.setStagingProgress(0);
try {
File file= new File(mPackageURI.getPath());
try(InputStream in = new FileInputStream(file)) {
longsizeBytes = file.length();
try(OutputStream out = session
.openWrite("PackageInstaller", 0, sizeBytes)) {
byte[] buffer = new byte[4096];
while (true) {
int numRead = in.read(buffer);
if (numRead == -1) {
session.fsync(out);
break;
}
if (isCancelled()) {
session.close();
break;
}
out.write(buffer, 0, numRead);
public IPackageInstallerSessionopenSession(int sessionId) {
try {
returnopenSessionInternal(sessionId);
} catch(IOException e) {
throwExceptionUtils.wrap(e);
}
}
通过prepareStageDir创建目录,
static void prepareStageDir(File stageDir)throws IOException {
if(stageDir.exists()) {
throw newIOException("Session dir already exists: " + stageDir);
}
try {
Os.mkdir(stageDir.getAbsolutePath(), 0755);
Os.chmod(stageDir.getAbsolutePath(), 0755);
} catch(ErrnoException e) {
// Thispurposefully throws if directory already exists
throw newIOException("Failed to prepare session dir: " + stageDir, e);
}
if(!SELinux.restorecon(stageDir)) {
throw newIOException("Failed to restorecon session dir: " + stageDir);
}
}
目前为止apk的安装都只是在PackageInstaller apk和framework的PackageInstaller框架之间交换,还没有涉及到PMS,这一点是怎么做到的呢,如果没有PMS参与,就没有参考文档里面我们了解的那些对manifest的解析、权限处理、文件拷贝和dex优化等相关流程,这里就产生了割裂感。我看到的很多文档对这点都没讲透,所以这里多写一点。
PMS在上层的接口类是APM,通过PM类在PackageInstaller和PackageInstallerSession的引用就能找到相关的关联关系,然后我们就会发现一个很重要的方法,就是session的commit方法,详细说来,
在7.0系统,InstallAppProgress的doPackageStage直接调用了session.commit(前面还有session的创建和open等),
private void doPackageStage(PackageManagerpm, PackageInstaller.SessionParams params) {
finalPackageInstaller packageInstaller = pm.getPackageInstaller();
PackageInstaller.Session session = null;
try {
final StringpackageLocation = mPackageURI.getPath();
final File file = newFile(packageLocation);
final intsessionId = packageInstaller.createSession(params);
final byte[]buffer = new byte[65536];
session =packageInstaller.openSession(sessionId);
finalInputStream in = new FileInputStream(file);
final longsizeBytes = file.length();
finalOutputStream out = session.openWrite("PackageInstaller", 0,sizeBytes);
try {
int c;
while ((c = in.read(buffer)) != -1) {
out.write(buffer, 0, c);
if(sizeBytes > 0) {
final float fraction = ((float) c / (float) sizeBytes);
session.addProgress(fraction);
}
}
session.fsync(out);
} finally {
IoUtils.closeQuietly(in);
IoUtils.closeQuietly(out);
}
// Create aPendingIntent and use it to generate the IntentSender
IntentbroadcastIntent = new Intent(BROADCAST_ACTION);
PendingIntentpendingIntent = PendingIntent.getBroadcast(
InstallAppProgress.this /*context*/,
sessionId,
broadcastIntent,
PendingIntent.FLAG_UPDATE_CURRENT);
session.commit(pendingIntent.getIntentSender());
} catch(IOException e) {
onPackageInstalled(PackageInstaller.STATUS_FAILURE);
} finally {
IoUtils.closeQuietly(session);
}
}
然后,通过binder,在PackageInstallerSession.java里,调用commit,给handler发一个消息MSG_COMMIT,
final PackageInstallObserverAdapteradapter = new PackageInstallObserverAdapter(mContext,
statusReceiver, sessionId, mIsInstallerDeviceOwner, userId);
mHandler.obtainMessage(MSG_COMMIT, adapter.getBinder()).sendToTarget();
然后Handler.CallbackmHandlerCallback再调用commitLocked(),这里就开始调用到PMS的安装接口了,然后就是PMS的流程,相关参考文档都有很详尽的说明,我就不赘述了。
private void commitLocked() throwsPackageManagerException {
…
// Unpack nativelibraries
extractNativeLibraries(mResolvedStageDir, params.abiOverride);
。。。
mRelinquished =true;
mPm.installStage(mPackageName,stageDir, stageCid, localObserver, params,
installerPackageName, installerUid, user, mCertificates);
}
在8.0上呢,没有了InstallAppProgress文件,改用InstallInstalling,但思路差不多,在异步taskInstallingAsyncTask的方法中调用commit,之后的逻辑差不多。
protected voidonPostExecute(PackageInstaller.Session session) {
if (session !=null) {
IntentbroadcastIntent = new Intent(BROADCAST_ACTION);
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);
}
}
}