Hi,我是小余。 本文已收录到GitHub Androider-Planet中。这儿有 Android 进阶生长常识体系,关注大众号 [小余的自习室] ,在成功的路上不迷路!
前言
前面一篇文章咱们解说了PKMS的发动进程。
PKMS发动进程中首要做了以下事情:
- 1.会对某些配置文件进行解析扫描,放到PKMS方针内存中
- 2.会对体系中的运用包括:overlay,system,vendor,app等途径下的运用进行扫描,假如发现有版别更新,则进行运用更新操作。
- 3.初始化保证理进程中需求运用到一些环境方针等。
接下面咱们再来解说下第三方运用的装置进程
运用装置进程
运用装置的办法有如下几种:
1.普通装置办法
在7.0之后,为了进一步提高文件读写的安全性,Android结构履行的StrictMode API方针禁止在您的运用外部揭露file://URI。 假如一项包括文件URI的intent离开您的运用,则运用呈现故障,并呈现FileUriExposedException反常。
这个时分需求运用FileProvider来授权外部文件读写权限。
FileProvider
详细运用办法如下:
-
1.在AndroidManifest文件中定义:
<provider android:authorities="${applicationId}.fileprovider" android:name="androidx.core.content.FileProvider" android:exported="false" android:grantUriPermissions="true" > <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/update_files" /> </provider>
-
2.在xml中定义文件update_files.xml:
<?xml version="1.0" encoding="utf-8"?> <paths xmlns:android="http://schemas.android.com/apk/res/android"> <external-path name="external_storage_install" path="yuhb/install"/> </paths>
-
3.在代码中调用
/** 普通运用装置办法 7.0今后需求运用FileProvider进行恳求 @param apkFile @param context */ public static void generateInstall(File apkFile, Context context){ if(!apkFile.exists()){ return; } Intent intent = getInstallIntent(apkFile, context); context.startActivity(intent); } //获取装置运用的Intent private static Intent getInstallIntent(File apkFile, Context context) { Uri data; Intent intent = new Intent(Intent.ACTION_VIEW); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); data = getInstallUri(context,apkFile); //7.0今后运用FileProvider处理 if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.N){ intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);//授权其他运用的读权限 intent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);//防止app加固下呈现授权失利状况 // intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);//授权其他运用写权限 } intent.setDataAndType(data,"application/vnd.android.package-archive"); return intent; } //获取装置文件的uri private static Uri getInstallUri(Context context,File apkFile) { Uri data; //7.0今后运用FileProvider处理 if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.N){ data = FileProvider.getUriForFile(context,context.getPackageName()+".fileprovider",apkFile); }else { data = Uri.fromFile(apkFile); } return data; }
2.静默装置办法(需求有root权限)
你是不是尝试了N种办法,打了N个debug,然后得到的却是各式各样的装置失利 ~ 首要类似静默功用一般是被体系所禁止的,只要厂商在自已平台才会开发权限(比如小米的体系运用,默许完结了静默功用,可是假如小米运用移植到vivo就无效了)。
详细运用办法如下:
/**静默装置办法,一般需求root权限或许是厂商自己的体系运用。
@param context
@param apkFilePath
*/
public static void silenceInstallApk(Context context,String apkFilePath) {
/*apkFilePath:这儿咱们首要传入的是装置包的途径 installObserver:自定义装置的回调,不需求能够删了*/
File apkFile = new File(apkFilePath);
//判别途径下的文件是否存在
if (!apkFile.exists()) {
Log.e(TAG, "apkFile is null...");
return;
}
String packageName = "";
//获取装置包的信息
PackageInfo packageInfo = context.getPackageManager().getPackageArchiveInfo(apkFilePath,
PackageManager.GET_ACTIVITIES | PackageManager.GET_SERVICES);
if (packageInfo != null) {
packageName = packageInfo.packageName;
String versionName = packageInfo.versionName;
}
//获取packageInstaller,后面用来创立PackageInstaller.Session
PackageInstaller packageInstaller = context.getPackageManager().getPackageInstaller();
//获取创立PackageInstaller.Session的参数
PackageInstaller.SessionParams sessionParams = new PackageInstaller.SessionParams(
PackageInstaller.SessionParams.MODE_FULL_INSTALL);
/*指示将在此会话中交付的一切APK的总巨细(以字节为单位),体系能够运用它来确保在持续之前存在足够的磁盘空间,
或许估计装置在外部存储上的容器巨细*/
sessionParams.setSize(apkFile.length());
PackageInstaller.Session session = null;
try {
//代表一个session的仅有ID,这儿我是在全局变量中声明,因为后面的别的一个办法用到了这个sessionId
int mSessionId = packageInstaller.createSession(sessionParams);
if (mSessionId != -1) {
//也便是在这个外部的onTransfesApkFile()办法中,将会用到sessionId
boolean copySuccess = onTransfesApkFile(mSessionId,context,apkFilePath, packageName);
if (copySuccess) {
session = context.getPackageManager().getPackageInstaller().openSession(mSessionId);
//设置装置完结后需求发送的一个自定义装置成果广播,这儿我设置了App的NAME,VERSION,PACKAGE
Intent intent = new Intent(context,
InstallResultReceiver.class);
intent.setAction(PackageInstaller.EXTRA_STATUS);
intent.putExtra("APP_VERSION", "1.0");
intent.putExtra("APP_PACKAGE", "com.allinpay.manager");
//履行完毕后,发送intent
PendingIntent pendingIntent = PendingIntent.getBroadcast(context,1,
intent, PendingIntent.FLAG_UPDATE_CURRENT);
//这儿终究进行session的提交
session.commit(pendingIntent.getIntentSender());
} else {
//此处是装置失利的回调,不需求能够删去
// if (installObserver != null) {
// installObserver.observer(false, apkFilePath, packageName);
// }
}
}
} catch (Exception exception) {
Log.e(TAG, "installApk exception = " + exception.getLocalizedMessage());
} finally {
if (null != session) {
session.close();
}
//装置完结需求删去文件
if (apkFile != null && apkFile.exists()) {
// apkFile.delete();
}
}
}
private static boolean onTransfesApkFile(int mSessionId,Context context,String apkFilePath, String packageName) {
InputStream in = null;
OutputStream out = null;
PackageInstaller.Session session = null;
boolean success = false;
try {
File apkFile = new File(apkFilePath);
//依据sessionId来获取其代表的session
session = context.getPackageManager().getPackageInstaller().openSession(mSessionId);
//向session中写入文件数据
out = session.openWrite(packageName + "_base.apk", 0, apkFile.length());
in = new FileInputStream(apkFile);
int total = 0;
int len;
byte[] buffer = new byte[1024];
while ((len = in.read(buffer)) != -1) {
total += len;
out.write(buffer, 0, len);
}
session.fsync(out);
success = true;
} catch (IOException exception) {
exception.printStackTrace();
} finally {
if (null != session) {
session.close();
}
try {
if (null != out) {
out.close();
}
if (null != in) {
in.close();
}
} catch (IOException exception) {
exception.printStackTrace();
}
}
return success;
}
这儿咱们来剖析下办法一(普通运用更新办法)
-
1.提取要害代码:调用的startActivity中的Intent特点
-
Action
:Intent.ACTION_VIEW -
Flag
:Intent.FLAG_ACTIVITY_NEW_TASK -
Uri
:content格局 -
Type
:application/vnd.android.package-archive
-
-
2.依据以上Intent,调用了startActivity,然后经过PKMS找到对应的Activity。 终究定位到:InstallStart类,这个类便是发动装置时翻开的第一个Activity。
/** Select which activity is the first visible activity of the installation and forward the intent to it. */ public class InstallStart extends Activity { protected void onCreate(@Nullable Bundle savedInstanceState) { ... Uri packageUri = intent.getData(); //假如Scheme是Content格局 if (packageUri != null && packageUri.getScheme().equals( ContentResolver.SCHEME_CONTENT)) { // [IMPORTANT] This path is deprecated, but should still work. Only necessary // features should be added.
// Copy file to prevent it from being changed underneath this process nextActivity.setClass(this, InstallStaging.class); //假如Scheme是package格局 } else if (packageUri != null && packageUri.getScheme().equals( PackageInstallerActivity.SCHEME_PACKAGE)) { nextActivity.setClass(this, PackageInstallerActivity.class); //假如Scheme是其他格局 } else { Intent result = new Intent(); result.putExtra(Intent.EXTRA_INSTALL_RESULT, PackageManager.INSTALL_FAILED_INVALID_URI); setResult(RESULT_FIRST_USER, result); nextActivity = null; } if (nextActivity != null) { startActivity(nextActivity); } finish(); ``` }
}
InstallStart的onCreate办法会对传入的Scheme格局进行判别,然后发动别的一个Activity,并完毕自己。 咱们重点来看Content格局的Activity。终究发动的是InstallStaging.class。看父类名字应该是一个挑选框类型的Activity。
//frameworks/base/packages/PackageInstaller/src/com/android/packageinstaller/InstallStaging.java
public class InstallStaging extends AlertActivity {
protected void onResume() {
...
mStagingTask = new StagingAsyncTask();
//履行StagingAsyncTask
mStagingTask.execute(getIntent().getData());
}
private final class StagingAsyncTask extends AsyncTask<Uri, Void, Boolean> {
@Override
protected Boolean doInBackground(Uri... params) {
if (params == null || params.length <= 0) {
return false;
}
Uri packageUri = params[0];
try (InputStream in = getContentResolver().openInputStream(packageUri)) {
// Despite the comments in ContentResolver#openInputStream the returned stream can
// be null.
if (in == null) {
return false;
}
try (OutputStream out = new FileOutputStream(mStagedFile)) {
byte[] buffer = new byte[1024 * 1024];
int bytesRead;
while ((bytesRead = in.read(buffer)) >= 0) {
// Be nice and respond to a cancellation
if (isCancelled()) {
return false;
}
out.write(buffer, 0, bytesRead);
}
}
} catch (IOException | SecurityException | IllegalStateException e) {
Log.w(LOG_TAG, "Error staging apk from content URI", e);
return false;
}
return true;
}
@Override
protected void onPostExecute(Boolean success) {
if (success) {
// Now start the installation again from a file
Intent installIntent = new Intent(getIntent());
installIntent.setClass(InstallStaging.this, DeleteStagedFileOnResult.class);
installIntent.setData(Uri.fromFile(mStagedFile));
if (installIntent.getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false)) {
installIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
}
installIntent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
startActivity(installIntent);
InstallStaging.this.finish();
} else {
showError();
}
}
}
}
- 1.InstallStaging的onResume时会启用了一个ASyncTask,在后台读取apk文件,并写入到mStagedFile文件中。 mStagedFile文件的作用是临时文件,防止在装置进程中对原文件变更。
- 2.在文件读取完结后,调用AsyncTask的onPostExecute办法,这个办法中会再次发动一个DeleteStagedFileOnResult类Activity。
持续进入DeleteStagedFileOnResult
/**
* Trampoline activity. Calls PackageInstallerActivity and deletes staged install file onResult.
*/
public class DeleteStagedFileOnResult extends Activity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState == null) {
Intent installIntent = new Intent(getIntent());
installIntent.setClass(this, PackageInstallerActivity.class);
installIntent.setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
startActivityForResult(installIntent, 0);
}
}
看谷歌给咱们的注解:这个类是一个过渡Activity:终究是用来发动PackageInstallerActivity并删去mStagedFile临时文件,这在onCreate办法中也能够看出。
那就转到PackageInstallerActivity,在PackageInstallerActivity中会让引导用户点击装置按钮,点击之后会调用startInstall办法进行装置操作。
//frameworks/base/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
private void startInstall() {
// Start subactivity 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 != 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 (mLocalLOGV) Log.i(TAG, "downloaded app uri=" + mPackageURI);
startActivity(newIntent);
finish();
}
startInstall重新发动了一个“InstallInstalling”去装置,并将发动运用需求的参数信息放到Intent中。
//frameworks/base/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java
public class InstallInstalling extends AlertActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
PackageInstaller.SessionParams.MODE_FULL_INSTALL);
...
//注册一个装置成果监听器launchFinishBasedOnResult
mInstallId = InstallEventReceiver
.addObserver(this, EventResultPersister.GENERATE_NEW_ID,
this::launchFinishBasedOnResult);
...
//创立一个createSession
mSessionId = getPackageManager().getPackageInstaller().createSession(params);
}
@Override
protected void onResume() {
//发动一个InstallingAsyncTask
mInstallingTask = new InstallingAsyncTask();
mInstallingTask.execute();
}
private final class InstallingAsyncTask extends AsyncTask<Void, Void,
PackageInstaller.Session> {
@Override
protected PackageInstaller.Session doInBackground(Void... params) {
PackageInstaller.Session session;
try {
//翻开Session
session = getPackageManager().getPackageInstaller().openSession(mSessionId);
} catch (IOException e) {
}
//设置session装置进度
session.setStagingProgress(0);
try {
File file = new File(mPackageURI.getPath());
try (InputStream in = new FileInputStream(file)) {
long sizeBytes = file.length();
try (OutputStream out = session
.openWrite("PackageInstaller", 0, sizeBytes)) {
byte[] buffer = new byte[1024 * 1024];
while (true) {
int numRead = in.read(buffer);
if (numRead == -1) {
session.fsync(out);
break;
}
if (isCancelled()) {
session.close();
break;
}
out.write(buffer, 0, numRead);
if (sizeBytes > 0) {
float fraction = ((float) numRead / (float) sizeBytes);
session.addProgress(fraction);
}
}
}
}
return session;
} catch (IOException | SecurityException e) {
}
}
@Override
protected void onPostExecute(PackageInstaller.Session session) {
if (session != null) {
//注册一个broadcastIntent监听装置成果:BROADCAST_ACTION = "com.android.packageinstaller.ACTION_INSTALL_COMMIT";
Intent broadcastIntent = new Intent(BROADCAST_ACTION);
broadcastIntent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
broadcastIntent.setPackage(getPackageName());
broadcastIntent.putExtra(EventResultPersister.EXTRA_ID, mInstallId);
PendingIntent pendingIntent = PendingIntent.getBroadcast(
InstallInstalling.this,
mInstallId,
broadcastIntent,
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE);
//调用commit进行装置
session.commit(pendingIntent.getIntentSender());
mCancelButton.setEnabled(false);
setFinishOnTouchOutside(false);
}
}
}
}
}
InstallInstalling能够总结为下面几个进程:
- 1.创立session
- 2.翻开session
- 3.copy apk文件到Session中
- 4.调用commit进行装置。
仔细观察你会发现:这儿进程和咱们前面剖析的静默装置办法进程其实是一样的。而咱们的InstallInstalling是运行在体系进程中,所以具有静默装置权限, 而第三方运用是没有这个权限的、
下面咱们深入PackageInstaller看看其是怎么完结装置进程的?
首要来看context.getPackageManager().getPackageInstaller()获取到的是哪个类?
假如你对Activity了解的话,应该知道context的完结类是ContextImpl类。
定位到它的getPackageManager。
//frameworks/base/core/java/android/app/ContextImpl.java
public PackageManager getPackageManager() {
if (mPackageManager != null) {
return mPackageManager;
}
final IPackageManager pm = ActivityThread.getPackageManager();
if (pm != null) {
// Doesn't matter if we make more than one instance.
return (mPackageManager = new ApplicationPackageManager(this, pm));
}
return null;
}
由此可知getPackageManager回来的是一个ApplicationPackageManager,而这儿有个要害参数pm,后期操作实践都是经过pm进行的。 pm是经过ActivityThread.getPackageManager()获取。
//frameworks/base/core/java/android/app/ActivityThread.java
public static IPackageManager getPackageManager() {
if (sPackageManager != null) {
return sPackageManager;
}
final IBinder b = ServiceManager.getService("package");
sPackageManager = IPackageManager.Stub.asInterface(b);
return sPackageManager;
}
哦?原来便是获取ServiceManager中的package服务。假如你还有印象,前面咱们剖析过在PKMS的main办法中有下面这段代码。
//构造IPackageManagerImpl方针并将其add到ServiceManager中:name为package
IPackageManagerImpl iPackageManager = m.new IPackageManagerImpl();
ServiceManager.addService("package", iPackageManager);
所以这儿回来的是一个IPackageManagerImpl方针。
好了,回到前面context.getPackageManager().getPackageInstaller()
context.getPackageManager():对应ApplicationPackageManager(context,IPackageManagerImpl)
进入ApplicationPackageManager的getPackageInstaller:
//frameworks/base/core/java/android/app/ApplicationPackageManager.java
public PackageInstaller getPackageInstaller() {
if (mInstaller == null) {
try {
mInstaller = new PackageInstaller(mPM.getPackageInstaller(),
mContext.getPackageName(), mContext.getAttributionTag(), getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
return mInstaller;
}
由此可知getPackageInstaller回来的是一个PackageInstaller方针,而要害看第一个参数mPM.getPackageInstaller(),这个参数也是实践进行装置的类。
前面剖析过mPM是IPackageManagerImpl方针,进入IPackageManagerImpl.getPackageInstaller()
在IPackageManagerImpl父类IPackageManagerBase完结了getPackageInstaller
//frameworks/base/services/core/java/com/android/server/pm/IPackageManagerBase.java
public final IPackageInstaller getPackageInstaller() {
...
return mInstallerService;
}
回来的是一个mInstallerService,这个mInstallerService是在哪里赋值的呢?经过几轮跳转,定位到。
mInstallerService是在PKMS的构造办法中赋值的:mInstallerService = mInjector.getPackageInstallerService();
mInjector是PackageManagerServiceInjector类(PKMS的运行时环境类)。
终究获取的mInstallerService是在PKMS构造进程中也便是体系开机时初始化的PackageInstallerService方针。
好了这儿画了一张图来表示他们之间的关系:
由以上剖析可知:context.getPackageManager().getPackageInstaller()获取的是PackageInstaller,而实践装置操作是PackageInstaller的内部PackageInstallerService方针
下面咱们依据前面剖分出的装置进程进行详细剖析 1.创立session 2.翻开session 3.copy apk文件到Session中 4.调用commit进行装置。
1.创立session
//frameworks/base/core/java/android/content/pm/PackageInstaller.java
public int createSession(@NonNull SessionParams params) throws IOException {
//mInstaller为PackageInstallerService
return mInstaller.createSession(params, mInstallerPackageName, mAttributionTag,
mUserId);
}
PackageInstallerService的createSession办法会调用内置的createSessionInternal办法
private int createSessionInternal(SessionParams params, String installerPackageName...){
//前面一大堆对Session创立的条件进行判别,不满意创立则抛出反常
//创立随机数的sessionId
sessionId = allocateSessionIdLocked();
//创立SessionDir
stageDir = buildSessionDir(sessionId, params);
//InstallSource持有运用装置的apk源文件信息
InstallSource installSource = InstallSource.create(installerPackageName,
originatingPackageName, requestedInstallerPackageName,
installerAttributionTag, params.packageSource);
//创立session : PackageInstallerSession
session = new PackageInstallerSession(mInternalCallback, mContext, mPm, this,
mSilentUpdatePolicy, mInstallThread.getLooper(), mStagingManager, sessionId,
userId, callingUid, installSource, params, createdMillis, 0L, stageDir, stageCid,
null, null, false, false, false, false, null, SessionInfo.INVALID_ID,
false, false, false, PackageManager.INSTALL_UNKNOWN, "");
//将session放入到mSessions键值对中,key为sessionId
synchronized (mSessions) {
mSessions.put(sessionId, session);
}
//将InstallSource放入到PKMS的Setting调集中
mPm.addInstallerPackageName(session.getInstallSource());
}
createSessionInternal进程首要便是创立了PackageInstallerSession方针,并将方针放入到mSessions调集中。
2.翻开session
翻开Session也是调用PackageInstallerService的createSession办法,内部调用openSessionInternal进行翻开。
private IPackageInstallerSession openSessionInternal(int sessionId) throws IOException {
synchronized (mSessions) {
final PackageInstallerSession session = mSessions.get(sessionId);
if (!checkOpenSessionAccess(session)) {
throw new SecurityException("Caller has no access to session " + sessionId);
}
session.open();
return session;
}
}
内容很简单,经过sessionId去mSessions获取Session方针,然后调用session.open()翻开。
//frameworks/base/services/core/java/com/android/server/pm/PackageInstallerSession.java
public void open() throws IOException {
...
boolean wasPrepared;
synchronized (mLock) {
wasPrepared = mPrepared;
if (!mPrepared) {
if (stageDir != null) {
prepareStageDir(stageDir);
}
mPrepared = true;
}
}
}
static void prepareStageDir(File stageDir) throws IOException {
try {
Os.mkdir(stageDir.getAbsolutePath(), 0775);
Os.chmod(stageDir.getAbsolutePath(), 0775);
} catch (ErrnoException e) {
}
}
open办法也仅仅调用Os的mkdir进行stageDir文件夹创立,并且给stageDir文件夹设置了对应的权限、 stageDir临时文件夹途径:new File(“data/app”, “vmdl” + sessionId + “.tmp”);
3.copy apk文件到Session中
Session文件的写操作openWrite终究调用的是PackageInstallerSession的doWriteInternal,写文件就不介绍了,这个大家都非常清楚了。 只要知道文件是写入到的是stageDir临时文件夹(“data/app/vmdl{$sessionId}.tmp”)下面
4.调用commit进行装置。
public void commit(@NonNull IntentSender statusReceiver, boolean forTransfer) {
...
dispatchSessionSealed();
}
private void dispatchSessionSealed() {
mHandler.obtainMessage(MSG_ON_SESSION_SEALED).sendToTarget();
}
private final Handler.Callback mHandlerCallback = new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
switch (msg.what) {
case MSG_ON_SESSION_SEALED:
//内部发射一个MSG_STREAM_VALIDATE_AND_COMMIT msg
handleSessionSealed();
break;
case MSG_STREAM_VALIDATE_AND_COMMIT:
//内部发射一个MSG_INSTALL msg
handleStreamValidateAndCommit();
break;
case MSG_INSTALL:
//处理运用装置进程
handleInstall();
break;
case MSG_ON_PACKAGE_INSTALLED:
final SomeArgs args = (SomeArgs) msg.obj;
final String packageName = (String) args.arg1;
final String message = (String) args.arg2;
final Bundle extras = (Bundle) args.arg3;
final IntentSender statusReceiver = (IntentSender) args.arg4;
final int returnCode = args.argi1;
args.recycle();
sendOnPackageInstalled(mContext, statusReceiver, sessionId,
isInstallerDeviceOwnerOrAffiliatedProfileOwner(), userId,
packageName, returnCode, message, extras);
break;
case MSG_SESSION_VALIDATION_FAILURE:
final int error = msg.arg1;
final String detailMessage = (String) msg.obj;
onSessionValidationFailure(error, detailMessage);
break;
}
return true;
}
};
发送MSG_ON_SESSION_SEALED的msg调用handleSessionSealed办法。handleSessionSealed办法内部又发送了MSG_STREAM_VALIDATE_AND_COMMIT的msg。 然后在handleStreamValidateAndCommit又发送了MSG_INSTALL。所以终究调用了handleInstall办法进行装置。
handleInstall办法能够大致分为:
- 1.apk文件的校验
- 2.apk文件的装置
@WorkerThread
private void handleInstall() {
...
if (params.isStaged) {
mStagedSession.verifySession();
} else {
verify();
}
}
mStagedSession.verifySession终究也会走到verify,能够直接看verify办法
private void verify() {
try {
//1.创立装置apk需求的文件夹:
prepareInheritedFiles();
//2.解析APK文件并提取so库文件。其实便是解析AndroidManfest中的四大组件信息
parseApkAndExtractNativeLibraries();
//3.查验apk的进程
verifyNonStaged();
} catch (PackageManagerException e) {
final String completeMsg = ExceptionUtils.getCompleteMessage(e);
final String errorMsg = PackageManager.installStatusToString(e.error, completeMsg);
setSessionFailed(e.error, errorMsg);
onSessionVerificationFailure(e.error, errorMsg);
}
}
重点来看3.查验apk的进程,verifyNonStaged在经过一系列session检查之后,终究会调用到PackageSessionVerifier的verifyAPK办法, verifyAPK内部设置了装置成果监听IPackageInstallObserver2:
//frameworks/base/services/core/java/com/android/server/pm/PackageSessionVerifier.java
private void verifyAPK(PackageInstallerSession session, Callback callback)
throws PackageManagerException {
final IPackageInstallObserver2 observer = new IPackageInstallObserver2.Stub() {
@Override
public void onUserActionRequired(Intent intent) {
throw new IllegalStateException();
}
@Override
public void onPackageInstalled(String basePackageName, int returnCode, String msg,
Bundle extras) {
if (session.isStaged() && returnCode == PackageManager.INSTALL_SUCCEEDED) {
// Continue verification for staged sessions
verifyStaged(session.mStagedSession, callback);
return;
}
if (returnCode != PackageManager.INSTALL_SUCCEEDED) {
String errorMessage = PackageManager.installStatusToString(returnCode, msg);
session.setSessionFailed(returnCode, errorMessage);
callback.onResult(returnCode, msg);
} else {
session.setSessionReady();
callback.onResult(PackageManager.INSTALL_SUCCEEDED, null);
}
}
};
final VerificationParams verifyingSession = makeVerificationParams(session, observer);
...
verifyingSession.verifyStage();
}
frameworks/base/services/core/java/com/android/server/pm/VerificationParams.java
public void verifyStage() {
mPm.mHandler.post(this::startCopy);
}
能够看到verifyStage终究调用了mPm.mHandler post了一个startCopy的任务。
final void startCopy() {
handleStartCopy();
handleReturnCode();
}
handleStartCopy和handleReturnCode是两个笼统办法:详细完结是在VerificationParams类中
public void handleStartCopy() {
//获取需求装置的apk包信息
PackageInfoLite pkgLite = PackageManagerServiceUtils.getMinimalPackageInfo(mPm.mContext,
mPackageLite, mOriginInfo.mResolvedPath, mInstallFlags, mPackageAbiOverride);
//校验需求更新的app的VersionCode,这儿面会对VersionCode版别和原始版别进行校验。
Pair<Integer, String> ret = mInstallPackageHelper.verifyReplacingVersionCode(
pkgLite, mRequiredInstalledVersionCode, mInstallFlags);
if (!mOriginInfo.mExisting) {
//假如是PackageManager.INSTALL_APEX不是APEX包,也便是apk包,则调用sendApkVerificationRequest对APK包进行更新
if ((mInstallFlags & PackageManager.INSTALL_APEX) == 0) {
sendApkVerificationRequest(pkgLite);//关注点1
}
//回溯版别走的
if ((mInstallFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) != 0) {
sendEnableRollbackRequest();
}
}
}
//关注点1
private void sendApkVerificationRequest(PackageInfoLite pkgLite) {
...
//发送完好的校验恳求
sendIntegrityVerificationRequest(verificationId, pkgLite, verificationState);
//发送package装置包校验
sendPackageVerificationRequest(
verificationId, pkgLite, verificationState);
...
}
sendPackageVerificationRequest首要校验下面几个:
- 1.四大组件包信息校验
- 2.apk打包公钥校验
- 3.校验打包的sdk版别信息,经过添加广播的办法进行。
好了,持续回到startCopy的handleReturnCode办法
//frameworks/base/services/core/java/com/android/server/pm/VerificationParams.java
void handleReturnCode() {
sendVerificationCompleteNotification();
}
private void sendVerificationCompleteNotification() {
...
try {
mObserver.onPackageInstalled(null, mRet, mErrorMessage,
new Bundle());
} catch (RemoteException e) {
Slog.i(TAG, "Observer no longer exists.");
}
...
}
在校验完毕成功今后,回调mObserver的onPackageInstalled办法。 而mObserver之前说过是在verifyAPK办法时传入的。
final IPackageInstallObserver2 observer = new IPackageInstallObserver2.Stub() {
@Override
public void onPackageInstalled(String basePackageName, int returnCode, String msg,
Bundle extras) {
...
if (returnCode != PackageManager.INSTALL_SUCCEEDED) {
String errorMessage = PackageManager.installStatusToString(returnCode, msg);
session.setSessionFailed(returnCode, errorMessage);
callback.onResult(returnCode, msg);
} else {
session.setSessionReady();
callback.onResult(PackageManager.INSTALL_SUCCEEDED, null);
}
}
};
onPackageInstalled回调成功后会再次调用callback的onResult办法
callBack是在前面剖析的PackageInstallerSession的verifyNonStaged办法中传入的,一层一层向外回调。
private void verifyNonStaged()
throws PackageManagerException {
mSessionProvider.getSessionVerifier().verify(this, (error, msg) -> {
mHandler.post(() -> {
if (dispatchPendingAbandonCallback()) {
// No need to continue if abandoned
return;
}
if (error == INSTALL_SUCCEEDED) {
onVerificationComplete();
} else {
onSessionVerificationFailure(error, msg);
}
});
});
}
终究回调到onVerificationComplete办法,能够看到前面很大一部分是在对运用进行校验的部分。
下面剖析的才是详细装置的进程:
@WorkerThread
private void onVerificationComplete() {
...
install();
}
private CompletableFuture<Void> install() {
List<CompletableFuture<InstallResult>> futures = installNonStaged();
...
}
private List<CompletableFuture<InstallResult>> installNonStaged() {
...
final InstallParams installingSession = makeInstallParams(future);
installingSession.installStage();
...
}
frameworks/base/services/core/java/com/android/server/pm/InstallParams.java
public void installStage() {
final Message msg = mPm.mHandler.obtainMessage(INIT_COPY);
msg.obj = this;
mPm.mHandler.sendMessage(msg);
}
installStage发送了一个INIT_COPY的msg,定位到mPm = PackageManagerImpl.java mPm.mHandler = PackageHandler
final class PackageHandler extends Handler {
@Override
public void handleMessage(Message msg) {
try {
doHandleMessage(msg);
} finally {
Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
}
}
void doHandleMessage(Message msg) {
switch (msg.what) {
case INIT_COPY: {
HandlerParams params = (HandlerParams) msg.obj;
if (params != null) {
...
params.startCopy();
}
break;
}
}
}
}
INIT_COPY的msg调用了HandlerParams的startCopy办法处理,而这个时分的HandlerParams的完结类是InstallParams.java,前面校验进程中的完结类是VerificationParams
final void startCopy() {
handleStartCopy();//关注点1
handleReturnCode();//关注点2
}
先看关注点1
//frameworks/base/services/core/java/com/android/server/pm/InstallParams.java
public void handleStartCopy() {
PackageInfoLite pkgLite = PackageManagerServiceUtils.getMinimalPackageInfo(mPm.mContext,
mPackageLite, mOriginInfo.mResolvedPath, mInstallFlags, mPackageAbiOverride);
if (!mOriginInfo.mStaged && pkgLite.recommendedInstallLocation
== InstallLocationUtils.RECOMMEND_FAILED_INSUFFICIENT_STORAGE) {
//先开释一部分不需求的缓存。
pkgLite.recommendedInstallLocation = mPm.freeCacheForInstallation(
pkgLite.recommendedInstallLocation, mPackageLite,
mOriginInfo.mResolvedPath, mPackageAbiOverride, mInstallFlags);
}
//依据默许的规矩重写装置途径,首要是区分运用外置sdcard途径仍是内置途径
mRet = overrideInstallLocation(pkgLite.packageName, pkgLite.recommendedInstallLocation,
pkgLite.installLocation);
}
再看关注点2:
void handleReturnCode() {
processPendingInstall();
}
private void processPendingInstall() {
//创立装置的参数信息
InstallArgs args = createInstallArgs(this);
if (mRet == PackageManager.INSTALL_SUCCEEDED) {
//关注点3 复制apk
mRet = args.copyApk();
}
if (mRet == PackageManager.INSTALL_SUCCEEDED) {
F2fsUtils.releaseCompressedBlocks(
mPm.mContext.getContentResolver(), new File(args.getCodePath()));
}
if (mParentInstallParams != null) {
mParentInstallParams.tryProcessInstallRequest(args, mRet);
} else {
PackageInstalledInfo res = new PackageInstalledInfo(mRet);
//关注点4
processInstallRequestsAsync(
res.mReturnCode == PackageManager.INSTALL_SUCCEEDED,
Collections.singletonList(new InstallRequest(args, res)));
}
}
processPendingInstall关注两个部分:
- 1.复制apk
- 2.装置apk
1.复制apk:mRet = args.copyApk();
而args 是FileInstallArgs类方针
//frameworks/base/services/core/java/com/android/server/pm/FileInstallArgs.java
int copyApk() {
return doCopyApk();
}
private int doCopyApk() {
//1.给StageDir分配对应的临时文件夹以及权限
final File tempDir = mPm.mInstallerService.allocateStageDirLegacy(mVolumeUuid, isEphemeral);
mCodeFile = tempDir;
//2.复制Package,这儿面首要是四大组件信息的复制
int ret = PackageManagerServiceUtils.copyPackage(
mOriginInfo.mFile.getAbsolutePath(), mCodeFile);
//3.依据abifilter 复制NativeLibrary ,so库到对应的lib目录下
handle = NativeLibraryHelper.Handle.create(mCodeFile);
ret = NativeLibraryHelper.copyNativeBinariesWithOverride(handle, libraryRoot,
mAbiOverride, isIncremental);
return ret;
}
先来剖析1处allocateStageDirLegacy
public File allocateStageDirLegacy(String volumeUuid, boolean isEphemeral) throws IOException {
synchronized (mSessions) {
try {
final int sessionId = allocateSessionIdLocked();
mLegacySessions.put(sessionId, true);
final File sessionStageDir = buildTmpSessionDir(sessionId, volumeUuid);
prepareStageDir(sessionStageDir);
return sessionStageDir;
} catch (IllegalStateException e) {
throw new IOException(e);
}
}
}
看buildTmpSessionDir,这个前面也剖析过,终究回来的File途径为:data/app/vmdl{$sessionId}.tmp
再来剖析2处PackageManagerServiceUtils.copyPackage
public static int copyPackage(String packagePath, File targetDir) {
try {
final File packageFile = new File(packagePath);
//解析APK文件到ParseResult中
final ParseResult<PackageLite> result = ApkLiteParseUtils.parsePackageLite(
input.reset(), packageFile, /* flags */ 0);
//获取apk的文件PackageLite信息
final PackageLite pkg = result.getResult();
//复制file,核心办法
copyFile(pkg.getBaseApkPath(), targetDir, "base.apk");
return PackageManager.INSTALL_SUCCEEDED;
} catch (IOException | ErrnoException e) {
}
}
private static void copyFile(String sourcePath, File targetDir, String targetName)
throws ErrnoException, IOException {
final File targetFile = new File(targetDir, targetName);
final FileDescriptor targetFd = Os.open(targetFile.getAbsolutePath(),
O_RDWR | O_CREAT, 0644);
Os.chmod(targetFile.getAbsolutePath(), 0644);
FileInputStream source = null;
try {
source = new FileInputStream(sourcePath);
FileUtils.copy(source.getFD(), targetFd);
} finally {
IoUtils.closeQuietly(source);
}
}
copyPackage会先去解析apk文件,然后调用copyFile办法,copyFile中调用Os.open去翻开targetFile方针文件, 调用FileUtils.copy办法将原文件复制到方针文件中
2.装置apk:processInstallRequestsAsync
// Queue up an async operation since the package installation may take a little while.
private void processInstallRequestsAsync(boolean success,
List<InstallRequest> installRequests) {
mPm.mHandler.post(() -> {
mInstallPackageHelper.processInstallRequests(success, installRequests);
});
}
frameworks/base/services/core/java/com/android/server/pm/InstallPackageHelper.java
public void processInstallRequests(boolean success, List<InstallRequest> installRequests) {
List<InstallRequest> apkInstallRequests = new ArrayList<>();
for (InstallRequest request : installRequests) {
...
apkInstallRequests.add(request);
}
if (success) {
for (InstallRequest request : apkInstallRequests) {
//预装置,内部首要是做clean操作
request.mArgs.doPreInstall(request.mInstallResult.mReturnCode);
}
synchronized (mPm.mInstallLock) {
//实践装置apk进程
installPackagesTracedLI(apkInstallRequests);
}
}
...
}
private void installPackagesTracedLI(List<InstallRequest> requests) {
...
installPackagesLI(requests);
...
}
下面咱们重点来剖析下installPackagesLI
private void installPackagesLI(List<InstallRequest> requests) {
for (InstallRequest request : requests) {
//阶段1:prepare阶段
repareResult = preparePackageLI(request.mArgs, request.mInstallResult);
//阶段2:scan阶段
final ScanResult result = scanPackageTracedLI(
prepareResult.mPackageToScan, prepareResult.mParseFlags,
prepareResult.mScanFlags, System.currentTimeMillis(),
request.mArgs.mUser, request.mArgs.mAbiOverride);
}
//阶段3:Reconcile阶段
reconciledPackages = ReconcilePackageUtils.reconcilePackages(
reconcileRequest, mSharedLibraries,
mPm.mSettings.getKeySetManagerService(), mPm.mSettings);
}
commitRequest = new CommitRequest(reconciledPackages,
mPm.mUserManager.getUserIds());
//阶段4:Commit阶段
commitPackagesLocked(commitRequest);
//阶段5:完结apk装置
executePostCommitSteps(commitRequest);
}
installPackagesLI是终究装置运用的办法:首要分为4个阶段
- 阶段1:prepare阶段:剖析当前装置包的状况,解析装置包并对其做初始化验证
- 阶段2:scan阶段:依据prepare阶段中搜集的装置包状况信息去扫描解分出来的包
- 阶段3:Reconcile阶段:验证scan阶段扫描到的Package信息以及当前体系状况,确保apk的正确装置。
- 阶段4:Commit阶段:提交一切扫描的包并更新体系状况。这是仅有能够在装置流程和一切可预测错误中修正体系状况的当地.
在 preparePackageLI() 内运用 PackageParser2.parsePackage() 解析AndroidManifest.xml,获取四大组件等信息; 运用ParsingPackageUtils.getSigningDetails() 解析签名信息;重命名包终究途径 等。
完结了解析和校验准备工作后,终究一步便是对apk的装置了。这儿调用了executePostCommitSteps准备app数据,并履行dex优化。
终究经过executePostCommitSteps完结apk的装置,履行dex优化等操作
-
阶段5:完结apk装置
- private void executePostCommitSteps(CommitRequest commitRequest) { for (ReconciledPackage reconciledPkg : commitRequest.mReconciledPackages.values()) { // prepareAppDataPostCommitLIF经过一系列调用会走到Installer的createAppData办法。 mAppDataHelper.prepareAppDataPostCommitLIF(pkg, 0); /* 检测是否需求进行dex优化:一起满意下面三种状况就需求 1.不是一个即时运用app或许假如是的话经过gservices进行dex优化操作 2.debuggable为false 3.不在增量文件体系上。 */ final boolean performDexopt = (!instantApp || android.provider.Settings.Global.getInt( mContext.getContentResolver(), android.provider.Settings.Global.INSTANT_APP_DEXOPT_ENABLED, 0) != 0) && !pkg.isDebuggable() && (!onIncremental) && dexoptOptions.isCompilationEnabled(); if (performDexopt) { //获取so库所在的目录 PackageSetting realPkgSetting = result.mExistingSettingCopied ? result.mRequest.mPkgSetting : result.mPkgSetting; boolean isUpdatedSystemApp = reconciledPkg.mPkgSetting.getPkgState() .isUpdatedSystemApp(); //更新体系app信息。 realPkgSetting.getPkgState().setUpdatedSystemApp(isUpdatedSystemApp); //进行dex优化 mPackageDexOptimizer.performDexOpt(pkg, realPkgSetting, null /* instructionSets */, mPm.getOrCreateCompilerPackageStats(pkg), mDexManager.getPackageUseInfoOrDefault(packageName), dexoptOptions); } //告诉BackgroundDexOptService服务当前packageName的运用进行了更新。 BackgroundDexOptService.getService().notifyPackageChanged(packageName); notifyPackageChangeObserversOnUpdate(reconciledPkg); } }
阶段5:首要做了下面两件事
-
任务1:调用prepareAppDataPostCommitLIF办法,终究履行到createAppData办法进行app的装置.
public @NonNull CreateAppDataResult createAppData(@NonNull CreateAppDataArgs args) throws InstallerException { ... try { return mInstalld.createAppData(args); } catch (Exception e) { throw InstallerException.from(e); } }
mInstalld在前面剖析过了,installd进程 的履行权限为 root,一切实践的运用装置,卸载等操作都是经过这个服务进行的。 PKMS仅仅java层的封装。mInstalld进程和PKMS是经过binder进行通讯的。
-
任务2.调用performDexOpt进行dex优化 一起满意下面三种状况就需求
- 1.不是一个即时运用app或许假如是的话经过gservices进行dex优化操作
- 2.debuggable为false
- 3.不在增量文件体系上。
然后关于dex优化部分,后面会单独出一篇文章来解说。
关于运用装置部分就讲到这儿了。
总结
关于Android中保证理机制,由于源码部分内容较多,小余运用了两篇文章来解说。期望你能从中有所收获。
上一篇:根据Android T:保证理机制详解(上)
假如文字对你协助,帮助给小余点个赞,谢啦。
参阅:
一篇文章看明白 Android PackageManagerService 工作流程
Android T谷歌官方文档
深入了解安卓-了解一下 Android 10 中的 APEX
android overlay机制实践
Android 体系服务 PMS Installd 守护进程(二)
Android FileProvider介绍
ANDROID 保证理(PACKAGEMANAGERSERVICE)
android体系源码目录system/framework下各个jar包的用处
同类文章:
“一文读懂”系列:Android屏幕刷新机制
Android Framework常识收拾:WindowManager体系(上)
“一文读懂”系列:Android中的硬件加速
“framework必会”系列:Android Input体系(一)事件读取机制
“framework必会”系列:Android Input体系(二)事件分发机制
“一文读懂”系列:无处不在的WMS
“一文读懂”系列:AMS是怎么动态办理进程的?
不知道怎么看源码?试试这几种办法~