Android APK安装流程(2)--APK的拷贝
-
第6步 启动InstallInstalling
该Activity是安装等待界面,真正的安装过程就是发生在这个界面的,所谓的真正的安装过程就是指开始调用PMS等S端Service、开始文件复制安装等工作。我们先来看看其onCreate()方法
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ApplicationInfo appInfo = getIntent()
.getParcelableExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO);
mPackageURI = getIntent().getData();
if ("package".equals(mPackageURI.getScheme())) {
try {
//***1***
getPackageManager().installExistingPackage(appInfo.packageName);
//***2***
launchSuccess();
} catch (PackageManager.NameNotFoundException e) {
launchFailure(PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null);
}
} else {
final File sourceFile = new File(mPackageURI.getPath());
PackageUtil.AppSnippet as = PackageUtil.getAppSnippet(this, appInfo, sourceFile);
mAlert.setIcon(as.icon);
mAlert.setTitle(as.label);
mAlert.setView(R.layout.install_content_view);
mAlert.setButton(DialogInterface.BUTTON_NEGATIVE, getString(R.string.cancel),
(ignored, ignored2) -> {
if (mInstallingTask != null) {
mInstallingTask.cancel(true);
}
if (mSessionId > 0) {
getPackageManager().getPackageInstaller().abandonSession(mSessionId);
mSessionId = 0;
}
setResult(RESULT_CANCELED);
finish();
}, null);
setupAlert();
requireViewById(R.id.installing).setVisibility(View.VISIBLE);
if (savedInstanceState != null) {
mSessionId = savedInstanceState.getInt(SESSION_ID);
mInstallId = savedInstanceState.getInt(INSTALL_ID);
// Reregister for result; might instantly call back if result was delivered while
// activity was destroyed
try {
//***3***
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.setInstallAsInstantApp(false);
params.setReferrerUri(getIntent().getParcelableExtra(Intent.EXTRA_REFERRER));
params.setOriginatingUri(getIntent()
.getParcelableExtra(Intent.EXTRA_ORIGINATING_URI));
params.setOriginatingUid(getIntent().getIntExtra(Intent.EXTRA_ORIGINATING_UID,
UID_UNKNOWN));
params.setInstallerPackageName(getIntent().getStringExtra(
Intent.EXTRA_INSTALLER_PACKAGE_NAME));
params.setInstallReason(PackageManager.INSTALL_REASON_USER);
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 {
//***4***
mInstallId = InstallEventReceiver
.addObserver(this, EventResultPersister.GENERATE_NEW_ID,
this::launchFinishBasedOnResult);
} catch (EventResultPersister.OutOfIdsException e) {
launchFailure(PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null);
}
try {
//***5***
mSessionId = getPackageManager().getPackageInstaller().createSession(params);
} catch (IOException e) {
launchFailure(PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null);
}
}
mCancelButton = mAlert.getButton(DialogInterface.BUTTON_NEGATIVE);
mSessionCallback = new InstallSessionCallback();
}
}
从以上方法我们可以看到onCreate()大体干了以下几件事:
1.如果从上个Activity里带过来的Scheme是"package",那就调用方法getPackageManager().installExistingPackage()
进行安装,并且直接跳转到InstallSuccess界面
2.从上一步我们也分析出Scheme到了这里只有package
和file
2种了,如果Scheme不是package
那就只能是file
了。此时获取PackageUri对应的临时文件,先通过PackageParser.parsePackageLite 解析出Package包的中apk的AndroidManifest的packageName、versionCode、installLocation等包含一个apk包中基础数据的PackageLite。
3.注册一个广播InstallEventReceiver实现监听。
4.创建PackageInstaller.SessionParams对象并初始化一些数据,然后把SessionParams传入getPackageManager().getPackageInstaller().createSession(params)
方法,从而获取mSessionId。
5.这里用到getPackageManager()、getPackageManager().getPackageInstaller()、PackageParser.parsePackageLite、PackageInstaller.SessionParams、InstallEventReceiver等,要多留意。
我们先来看看InstallEventReceiver是个什么东东(从android源码上看android9.0会在Manifest里静态注册,但是android10时已经去掉了,我们可以认为是动态注册了)
/**
* Receives install events and perists them using a {@link EventResultPersister}.
*/
public class InstallEventReceiver extends BroadcastReceiver {
private static final Object sLock = new Object();
private static EventResultPersister sReceiver;
/**
* Get the event receiver persisting the results
*
* @return The event receiver.
*/
@NonNull private static EventResultPersister getReceiver(@NonNull Context context) {
synchronized (sLock) {
if (sReceiver == null) {
//***6***
sReceiver = new EventResultPersister(
TemporaryFileManager.getInstallStateFile(context));
}
}
return sReceiver;
}
@Override
public void onReceive(Context context, Intent intent) {
//***7***
getReceiver(context).onEventReceived(context, intent);
}
/**
* Add an observer. If there is already an event for this id, call back inside of this call.
*
* @param context A context of the current app
* @param id The id the observer is for or {@code GENERATE_NEW_ID} to generate a new one.
* @param observer The observer to call back.
*
* @return The id for this event
*/
static int addObserver(@NonNull Context context, int id,
@NonNull EventResultPersister.EventResultObserver observer)
throws EventResultPersister.OutOfIdsException {
return getReceiver(context).addObserver(id, observer);
}
/**
* Remove a observer.
*
* @param context A context of the current app
* @param id The id the observer was added for
*/
static void removeObserver(@NonNull Context context, int id) {
getReceiver(context).removeObserver(id);
}
}
其实这个广播是个中间过渡的类,其核心的逻辑处理都转交给EventResultPersister处理了,但是我们要注意2个细节点:
1.sReceiver = new EventResultPersister( TemporaryFileManager.getInstallStateFile(context));
这一行代码,核心关注的是TemporaryFileManager.getInstallStateFile()
方法。初始化EventResultPersister时传入的这个File,我们看其源码
@NonNull
public static File getInstallStateFile(@NonNull Context context) {
return new File(context.getNoBackupFilesDir(), "install_results.xml");
}
再看context.getNoBackupFilesDir()
方法在ContextImpl的实现:
@Override
public File getNoBackupFilesDir() {
synchronized (mSync) {
if (mNoBackupFilesDir == null) {
mNoBackupFilesDir = new File(getDataDir(), "no_backup");
}
return ensurePrivateDirExists(mNoBackupFilesDir);
}
}
看到了no_backup
文件夹,这个文件夹上文也有提到,是临时保存文件夹,这个步骤里是保存install_results.xml
,这个install_results.xml
是干什么用呢?后续我们会分析到,暂时先等等。
2.这个广播接受的是com.android.packageinstaller.ACTION_INSTALL_COMMIT。
/platform/packages/apps/PackageInstaller/+/refs/tags/android-9.0.0_r48/AndroidManifest.xml里注册的源码:
···
<activity android:name=".DeleteStagedFileOnResult"
android:exported="false" />
<activity android:name=".PackageInstallerActivity"
android:exported="false" />
<activity android:name=".InstallInstalling"
android:theme="@style/DialogWhenLargeNoAnimation"
android:exported="false" />
<receiver android:name=".InstallEventReceiver"
android:permission="android.permission.INSTALL_PACKAGES"
android:exported="true">
<intent-filter android:priority="1">
<action android:name="com.android.packageinstaller.ACTION_INSTALL_COMMIT" />
</intent-filter>
</receiver>
<activity android:name=".InstallSuccess"
android:theme="@style/DialogWhenLargeNoAnimation"
android:exported="false" />
<activity android:name=".InstallFailed"
android:theme="@style/DialogWhenLargeNoAnimation"
android:exported="false" />
···
我们继续看EventResultPersister这个类的源码,先来看上文提到的其初始化的构造方法:
/**
* Read persisted state.
*
* @param resultFile The file the results are persisted in
*/
EventResultPersister(@NonNull File resultFile) {
mResultsFile = new AtomicFile(resultFile);
mCounter = GENERATE_NEW_ID + 1;
try (FileInputStream stream = mResultsFile.openRead()) {
XmlPullParser parser = Xml.newPullParser();
parser.setInput(stream, StandardCharsets.UTF_8.name());
nextElement(parser);
while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
String tagName = parser.getName();
if ("results".equals(tagName)) {
mCounter = readIntAttribute(parser, "counter");
} else if ("result".equals(tagName)) {
int id = readIntAttribute(parser, "id");
int status = readIntAttribute(parser, "status");
int legacyStatus = readIntAttribute(parser, "legacyStatus");
String statusMessage = readStringAttribute(parser, "statusMessage");
if (mResults.get(id) != null) {
throw new Exception("id " + id + " has two results");
}
mResults.put(id, new EventResult(status, legacyStatus, statusMessage));
} else {
throw new Exception("unexpected tag");
}
nextElement(parser);
}
} catch (Exception e) {
mResults.clear();
writeState();
}
}
这段代码其实就是读取上文中提到的no_backup/install_results.xml
,而install_results.xml
实际上可以获取到Android每一次安装后记录,每新增一个apk的安装都会递增id。并且该id就对应上不同安装的结果,如status
,legacyStatus
,statusMessage
都会记录在这个文件中。当安装完后,分发的消息就会同步到这个install_results.xml
文件中,这样就能保证需要分发的安装完成的事件可以保证分发出去。
再来看addObserver()方法:
/**
* Add an observer. If there is already an event for this id, call back inside of this call.
*
* @param id The id the observer is for or {@code GENERATE_NEW_ID} to generate a new one.
* @param observer The observer to call back.
*
* @return The id for this event
*/
int addObserver(int id, @NonNull EventResultObserver observer)
throws OutOfIdsException {
synchronized (mLock) {
int resultIndex = -1;
if (id == GENERATE_NEW_ID) {
id = getNewId();
} else {
resultIndex = mResults.indexOfKey(id);
}
// Check if we can instantly call back
if (resultIndex >= 0) {
EventResult result = mResults.valueAt(resultIndex);
observer.onResult(result.status, result.legacyStatus, result.message);
mResults.removeAt(resultIndex);
writeState();
} else {
mObservers.put(id, observer);
}
}
return id;
}
回顾上文中添加监听的代码:
···
try {
mInstallId = InstallEventReceiver
.addObserver(this, EventResultPersister.GENERATE_NEW_ID,
this::launchFinishBasedOnResult);
} catch (EventResultPersister.OutOfIdsException e) {
launchFailure(PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null);
}
···
- 此时传入的id是
EventResultPersister.GENERATE_NEW_ID
(其值是Integer.MIN_VALUE
),当id=GENERATE_NEW_ID
时会通过getNewId对应的id会从0依次递增,并且把id和observer记录到EventResultObserver的mObservers里(一个SparseArray),等到后面安装完成后,再通过id获取observer回调到外面的InstallInstalling的launchFinishBasedOnResult()
方法。 - 如果id不为GENERATE_NEW_ID,则尝试从mResults(mResults是EventResultPersister初始化时解析
install_results.xml
时put的数据)中获取,如果能找到,说明需要告诉监听者这个apk已经安装好了,则回调EventResultObserver的onResult方法。而onResult最后又回调到InstallInstalling的launchFinishBasedOnResult()
方法:
/**
* Launch the appropriate finish activity (success or failed) for the installation result.
*
* @param statusCode The installation result.
* @param legacyStatus The installation as used internally in the package manager.
* @param statusMessage The detailed installation result.
*/
private void launchFinishBasedOnResult(int statusCode, int legacyStatus, String statusMessage) {
if (statusCode == PackageInstaller.STATUS_SUCCESS) {
//***8***
launchSuccess();
} else {
//***9***
launchFailure(legacyStatus, statusMessage);
}
}
launchFinishBasedOnResult()
方法要表达的是要么安装成功,要么安装失败,成功和失败会分别跳转到2个Activity里,具体的后面再说。
下面我们要聊的是上面反复提到的getPackageManager()
方法,getPackageManager()
是一个Activity的方法,对应的是ContextImpl的getPackageManager()
方法:
@Override
public PackageManager getPackageManager() {
if (mPackageManager != null) {
return mPackageManager;
}
//***10***
IPackageManager pm = ActivityThread.getPackageManager();
if (pm != null) {
// Doesn't matter if we make more than one instance.
//***11***
return (mPackageManager = new ApplicationPackageManager(this, pm));
}
return null;
}
ActivityThread的getPackageManager()
方法:
@UnsupportedAppUsage
public static IPackageManager getPackageManager() {
if (sPackageManager != null) {
//Slog.v("PackageManager", "returning cur default = " + sPackageManager);
return sPackageManager;
}
IBinder b = ServiceManager.getService("package");
//Slog.v("PackageManager", "default service binder = " + b);
sPackageManager = IPackageManager.Stub.asInterface(b);
//Slog.v("PackageManager", "default service = " + sPackageManager);
return sPackageManager;
}
ActivityThread getPackageManager()
拿的是PMS在C侧的代理,如果ActivityThread的getPackageManager()
不为null,则执行mPackageManager = new ApplicationPackageManager(this, pm)
/** @hide */
public class ApplicationPackageManager extends PackageManager {
private static final String TAG = "ApplicationPackageManager";
private final static boolean DEBUG_ICONS = false;
private static final int DEFAULT_EPHEMERAL_COOKIE_MAX_SIZE_BYTES = 16384; // 16KB
// Default flags to use with PackageManager when no flags are given.
private final static int sDefaultFlags = PackageManager.GET_SHARED_LIBRARY_FILES;
private final Object mLock = new Object();
···
private final ContextImpl mContext;
@UnsupportedAppUsage
private final IPackageManager mPM;
···
@UnsupportedAppUsage
protected ApplicationPackageManager(ContextImpl context,
IPackageManager pm) {
mContext = context;
mPM = pm;
}
从上面代码我们可以看出ApplicationPackageManager会持有ActivityThread获取到PMS在C侧的代理,也即ApplicationPackageManager的mPM
就是PMS的IPackageManager接口在整个APP侧的代理。ApplicationPackageManager是PackageManager的实现类,PackageManager里充满了各种abstract方法。
看一下ApplicationPackageManager的getPackageInstaller()
方法
@Override
public PackageInstaller getPackageInstaller() {
synchronized (mLock) {
if (mInstaller == null) {
try {
//***12***
mInstaller = new PackageInstaller(mPM.getPackageInstaller(),
mContext.getPackageName(), getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
return mInstaller;
}
}
public class PackageInstaller {
private static final String TAG = "PackageInstaller";
···
/** {@hide} */
public PackageInstaller(IPackageInstaller installer,
String installerPackageName, int userId) {
mInstaller = installer;
mInstallerPackageName = installerPackageName;
mUserId = userId;
}
···
而mPM.getPackageInstaller()
对应的就是PMS的getPackageInstaller()
public class PackageManagerService extends IPackageManager.Stub
implements PackageSender {
static final String TAG = "PackageManager";
final PackageInstallerService mInstallerService;
···
@Override
public IPackageInstaller getPackageInstaller() {
if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
return null;
}
return mInstallerService;
}
···
返回类型是IPackageInstaller类型的PackageInstallerService,相当PackageInstaller持有PMS拿到的PackageInstallerService对象,如此以来PackageInstaller就可以直接调用PackageInstallerService操作S端的关于package管理的方法了。
简单总结,IPackageManager是个Binder接口,其在服务端的具体实现就是PMS,而在客户端的代理是上文中ActivityThread通过getPackageManager()
方法实现的sPackageManager
,然后这个sPackageManager
会被传入ApplicationPackageManager,也就是最终IPackageManager在客户端的实现都会交由ApplicationPackageManager完成。IPackageInstaller同样也是个Binder接口,其在客户端没有具体的实现,在服务端的具体实现是PackageInstallerService,PMS会持有PackageInstallerService对象,客户端会把PMS持有的PackageInstallerService对象包装在PackageInstaller 里,所以如果客户端想调用PackageInstallerService的方法,只需要操作PackageInstaller就可以拿到PMS持有的PackageInstallerService对象,从而可以操作服务端的PackageInstallerService。
最后来看getPackageManager().getPackageInstaller().createSession(params)
方法,从上面的分析我们知道getPackageManager().getPackageInstaller()
得到的PackageInstaller对象,看createSession()
方法:
#PackageInstaller
public int createSession(@NonNull SessionParams params) throws IOException {
try {
final String installerPackage;
if (params.installerPackageName == null) {
installerPackage = mInstallerPackageName;
} else {
installerPackage = params.installerPackageName;
}
//***13***
return mInstaller.createSession(params, installerPackage, mUserId);
} catch (RuntimeException e) {
ExceptionUtils.maybeUnwrapIOException(e);
throw e;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
mInstaller.createSession(params, installerPackage, mUserId)
的mInstaller就是PMS持有的PackageInstallerService对象,所以该方法其实是PackageInstallerService的方法:
#PackageInstallerService
@Override
public int createSession(SessionParams params, String installerPackageName, int userId) {
try {
//***14***
return createSessionInternal(params, installerPackageName, userId);
} catch (IOException e) {
throw ExceptionUtils.wrap(e);
}
}
#PackageInstallerService
private int createSessionInternal(SessionParams params, String installerPackageName, int userId)
throws IOException {
final int callingUid = Binder.getCallingUid();
mPermissionManager.enforceCrossUserPermission(
callingUid, userId, true, true, "createSession");
if (mPm.isUserRestricted(userId, UserManager.DISALLOW_INSTALL_APPS)) {
throw new SecurityException("User restriction prevents installing");
}
if ((callingUid == Process.SHELL_UID) || (callingUid == Process.ROOT_UID)) {
params.installFlags |= PackageManager.INSTALL_FROM_ADB;
} else {
// Only apps with INSTALL_PACKAGES are allowed to set an installer that is not the
// caller.
if (mContext.checkCallingOrSelfPermission(Manifest.permission.INSTALL_PACKAGES) !=
PackageManager.PERMISSION_GRANTED) {
mAppOps.checkPackage(callingUid, installerPackageName);
}
params.installFlags &= ~PackageManager.INSTALL_FROM_ADB;
params.installFlags &= ~PackageManager.INSTALL_ALL_USERS;
params.installFlags |= PackageManager.INSTALL_REPLACE_EXISTING;
if ((params.installFlags & PackageManager.INSTALL_VIRTUAL_PRELOAD) != 0
&& !mPm.isCallerVerifier(callingUid)) {
params.installFlags &= ~PackageManager.INSTALL_VIRTUAL_PRELOAD;
}
}
if (Build.IS_DEBUGGABLE || isDowngradeAllowedForCaller(callingUid)) {
params.installFlags |= PackageManager.INSTALL_ALLOW_DOWNGRADE;
} else {
params.installFlags &= ~PackageManager.INSTALL_ALLOW_DOWNGRADE;
params.installFlags &= ~PackageManager.INSTALL_REQUEST_DOWNGRADE;
}
boolean isApex = (params.installFlags & PackageManager.INSTALL_APEX) != 0;
if (params.isStaged || isApex) {
mContext.enforceCallingOrSelfPermission(Manifest.permission.INSTALL_PACKAGES, TAG);
}
if (isApex) {
if (!mApexManager.isApexSupported()) {
throw new IllegalArgumentException(
"This device doesn't support the installation of APEX files");
}
if (!params.isStaged) {
throw new IllegalArgumentException(
"APEX files can only be installed as part of a staged session.");
}
}
if (!params.isMultiPackage) {
// Only system components can circumvent runtime permissions when installing.
if ((params.installFlags & PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS) != 0
&& mContext.checkCallingOrSelfPermission(Manifest.permission
.INSTALL_GRANT_RUNTIME_PERMISSIONS) == PackageManager.PERMISSION_DENIED) {
throw new SecurityException("You need the "
+ "android.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS permission "
+ "to use the PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS flag");
}
// Defensively resize giant app icons
//***15***
if (params.appIcon != null) {
final ActivityManager am = (ActivityManager) mContext.getSystemService(
Context.ACTIVITY_SERVICE);
final int iconSize = am.getLauncherLargeIconSize();
if ((params.appIcon.getWidth() > iconSize * 2)
|| (params.appIcon.getHeight() > iconSize * 2)) {
params.appIcon = Bitmap.createScaledBitmap(params.appIcon, iconSize, iconSize,
true);
}
}
switch (params.mode) {
case SessionParams.MODE_FULL_INSTALL:
case SessionParams.MODE_INHERIT_EXISTING:
break;
default:
throw new IllegalArgumentException("Invalid install mode: " + params.mode);
}
// If caller requested explicit location, sanity check it, otherwise
// resolve the best internal or adopted location.
// PackageManager.INSTALL_INTERNAL标志表示将应用安装到/data分区
// 想方设法把params.installFlags设置成了PackageManager.INSTALL_INTERNAL,也就是不在允许apk安装到主存储
if ((params.installFlags & PackageManager.INSTALL_INTERNAL) != 0) {
if (!PackageHelper.fitsOnInternal(mContext, params)) {
// 如果/data分区空间不满足,只能抛出异常给调用方处理
throw new IOException("No suitable internal storage available");
}
} else if ((params.installFlags & PackageManager.INSTALL_FORCE_VOLUME_UUID) != 0) {
// For now, installs to adopted media are treated as internal from
// an install flag point-of-view.
// PackageManager.INSTALL_FORCE_VOLUME_UUID 标志和指定的分区id也会被忽略, 使用/data分区来安装。
params.installFlags |= PackageManager.INSTALL_INTERNAL;
} else {
params.installFlags |= PackageManager.INSTALL_INTERNAL;
// Resolve best location for install, based on combination of
// requested install flags, delta size, and manifest settings.
final long ident = Binder.clearCallingIdentity();
// 没有使用PackageManager.INSTALL_INTERNAL和PackageManager.INSTALL_FORCE_VOLUME_UUID标志来安装,
// 允许使用其他扩展的内置存储安装,所以调用PackageHelper.resolveInstallVolume(mContext, params)来进行解析,所以即使安装程序指定了volumeId也会被忽略。
try {
//***16***
params.volumeUuid = PackageHelper.resolveInstallVolume(mContext, params);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
}
final int sessionId;
final PackageInstallerSession session;
synchronized (mSessions) {
// Sanity check that installer isn't going crazy
final int activeCount = getSessionCount(mSessions, callingUid);
if (activeCount >= MAX_ACTIVE_SESSIONS) {
throw new IllegalStateException(
"Too many active sessions for UID " + callingUid);
}
final int historicalCount = mHistoricalSessionsByInstaller.get(callingUid);
if (historicalCount >= MAX_HISTORICAL_SESSIONS) {
throw new IllegalStateException(
"Too many historical sessions for UID " + callingUid);
}
//***17***
sessionId = allocateSessionIdLocked();
}
final long createdMillis = System.currentTimeMillis();
// We're staging to exactly one location
File stageDir = null;
String stageCid = null;
if (!params.isMultiPackage) {
if ((params.installFlags & PackageManager.INSTALL_INTERNAL) != 0) {
//***18***
stageDir = buildSessionDir(sessionId, params);
} else {
//***19***
stageCid = buildExternalStageCid(sessionId);
}
}
//***20***
session = new PackageInstallerSession(mInternalCallback, mContext, mPm, this,
mInstallThread.getLooper(), mStagingManager, sessionId, userId,
installerPackageName, callingUid, params, createdMillis, stageDir, stageCid, false,
false, false, null, SessionInfo.INVALID_ID, false, false, false,
SessionInfo.STAGED_SESSION_NO_ERROR, "");
synchronized (mSessions) {
mSessions.put(sessionId, session);
}
if (params.isStaged) {
mStagingManager.createSession(session);
}
if ((session.params.installFlags & PackageManager.INSTALL_DRY_RUN) == 0) {
mCallbacks.notifySessionCreated(session.sessionId, session.userId);
}
//***21***
writeSessionsAsync();
return sessionId;
}
该方法主要做了以下几件事:
1.从ActivityManager 获取全局配置下,当前配置下的dp数值以及图标大小,从而计算出应用图标对应的bitmap大小,并提前申请出内存出来.
2.PackageHelper.resolveInstallVolume
拿到磁盘中最合适安装的扇区的uuid,保存到SessionParams的volumeUuid中。设置installFlags带上INSTALL_INTERNAL,INSTALL_INTERNAL分区代表/data 目录所对应的分区。它对应的volume.id为ID_PRIVATE_INTERNAL,类型是VolumeInfo.TYPE_PRIVATE, 其他的类型为VolumeInfo.TYPE_PRIVATE的分区作为内置分区的扩展,被全盘加密后挂载到/mnt/expand/${volumeId}
(这个地址后续会分析到)下面, 所以应用只能安装到VolumeInfo.TYPE_PRIVATE分区下面(早期Android应用可以安装到PUBLIC类型分区)。
public static String resolveInstallVolume(Context context, SessionParams params)
throws IOException {
TestableInterface testableInterface = getDefaultTestableInterface();
//***22***
return resolveInstallVolume(context, params.appPackageName, params.installLocation,
params.sizeBytes, testableInterface);
}
@VisibleForTesting
@Deprecated
public static String resolveInstallVolume(Context context, String packageName,
int installLocation, long sizeBytes, TestableInterface testInterface)
throws IOException {
final SessionParams params = new SessionParams(SessionParams.MODE_INVALID);
params.appPackageName = packageName;
params.installLocation = installLocation;
params.sizeBytes = sizeBytes;
//***23***
return resolveInstallVolume(context, params, testInterface);
}
@VisibleForTesting
public static String resolveInstallVolume(Context context, SessionParams params,
TestableInterface testInterface) throws IOException {
final StorageManager storageManager = testInterface.getStorageManager(context);
final boolean forceAllowOnExternal = testInterface.getForceAllowOnExternalSetting(context);
final boolean allow3rdPartyOnInternal =
testInterface.getAllow3rdPartyOnInternalConfig(context);
// TODO: handle existing apps installed in ASEC; currently assumes
// they'll end up back on internal storage
ApplicationInfo existingInfo = testInterface.getExistingAppInfo(context,
params.appPackageName);
// Figure out best candidate volume, and also if we fit on internal
final ArraySet<String> allCandidates = new ArraySet<>();
boolean fitsOnInternal = false;
VolumeInfo bestCandidate = null;
long bestCandidateAvailBytes = Long.MIN_VALUE;
for (VolumeInfo vol : storageManager.getVolumes()) {
if (vol.type == VolumeInfo.TYPE_PRIVATE && vol.isMountedWritable()) {
final boolean isInternalStorage = ID_PRIVATE_INTERNAL.equals(vol.id);
final UUID target = storageManager.getUuidForPath(new File(vol.path));
final long availBytes = storageManager.getAllocatableBytes(target,
translateAllocateFlags(params.installFlags));
if (isInternalStorage) {
fitsOnInternal = (params.sizeBytes <= availBytes);
}
if (!isInternalStorage || allow3rdPartyOnInternal) {
if (availBytes >= params.sizeBytes) {
allCandidates.add(vol.fsUuid);
}
if (availBytes >= bestCandidateAvailBytes) {
bestCandidate = vol;
bestCandidateAvailBytes = availBytes;
}
}
}
}
// 1 上面allCandidates表示空间充足的PRIVATE分区, allCandidates表示空间充足的候选分区,
// bestCandidate表示空间最充足的候选分区,fitsOnInternal表示/data分区是否空间充足。
// 注意这里如果第三方应用不允许安装到/data分区,allCandidates和bestCandidate不会包含/data分区
// System apps always forced to internal storage
// 2 系统应用只允许安装在/data分区,/data分区如果空间不足,抛出异常
if (existingInfo != null && existingInfo.isSystemApp()) {
if (fitsOnInternal) {
return StorageManager.UUID_PRIVATE_INTERNAL;
} else {
throw new IOException("Not enough space on existing volume "
+ existingInfo.volumeUuid + " for system app " + params.appPackageName
+ " upgrade");
}
}
// If app expresses strong desire for internal storage, honor it
// 3 没有强制允许安装到扩展分区的情况,并且有INSTALL_LOCATION_INTERNAL_ONLY标志表示有强烈愿望安装到/data分区
if (!forceAllowOnExternal
&& params.installLocation == PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY) {
// 3.1 已经安装到了扩展分区,不允许move
if (existingInfo != null && !Objects.equals(existingInfo.volumeUuid,
StorageManager.UUID_PRIVATE_INTERNAL)) {
throw new IOException("Cannot automatically move " + params.appPackageName
+ " from " + existingInfo.volumeUuid + " to internal storage");
}
//3.2 不允许第三方应用安装到/data分区, 直接抛出异常。
if (!allow3rdPartyOnInternal) {
throw new IOException("Not allowed to install non-system apps on internal storage");
}
//3.3 /data分区存储空间满足,安装到/data分区。空间不满足抛出异常。
if (fitsOnInternal) {
return StorageManager.UUID_PRIVATE_INTERNAL;
} else {
throw new IOException("Requested internal only, but not enough space");
}
}
// If app already exists somewhere, we must stay on that volume
// 4 覆盖安装
if (existingInfo != null) {
// 4.1 原来也安装在/data分区,并且空间足够则允许覆盖安装到/data分区。
// 非allow3rdPartyOnInternal的情况/data分区不在allCandidates中,所以有这个分支的存在。
if (Objects.equals(existingInfo.volumeUuid, StorageManager.UUID_PRIVATE_INTERNAL)
&& fitsOnInternal) {
return StorageManager.UUID_PRIVATE_INTERNAL;
// 4.2 如果原有安装的扩展分区空间满足,则使用扩展分区。
} else if (allCandidates.contains(existingInfo.volumeUuid)) {
return existingInfo.volumeUuid;
// 4.3 没有合适的分区,抛出异常。
} else {
throw new IOException("Not enough space on existing volume "
+ existingInfo.volumeUuid + " for " + params.appPackageName + " upgrade");
}
}
// We're left with new installations with either preferring external or auto, so just pick
// volume with most space
// 5 没有强烈要求安装到内部分区,也没有被安装过,也没有强制要求安装到扩展分区,则使用最合适的分区(也可能是/data分区)
if (bestCandidate != null) {
return bestCandidate.fsUuid;
} else {
throw new IOException("No special requests, but no room on allowed volumes. "
+ " allow3rdPartyOnInternal? " + allow3rdPartyOnInternal);
}
}
确定安装卷这里看起来比较复杂,其实有几个参考指标:
a、磁盘的空间要满足。
b、是否允许三方应用安装在/data分区(allow3rdPartyOnInternal)。
c、是否强制允许安装到扩展分区(forceAllowOnExternal)。
d、应用是否要求安装到/data分区。(PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY)
e、不支持move应用到其他盘。
f、系统应用只允许安装在/data分区。
g、是否强制安装到外置存储。
h、哪个磁盘剩余空间最大。
3.从Int的最大数值内挑选一个没有使用过的随机数,成为新的PackageInstallerSession的id
@GuardedBy("mSessions")
private int allocateSessionIdLocked() {
int n = 0;
int sessionId;
do {
sessionId = mRandom.nextInt(Integer.MAX_VALUE - 1) + 1;
if (!mAllocatedSessions.get(sessionId, false)) {
mAllocatedSessions.put(sessionId, true);
return sessionId;
}
} while (n++ < 32);
throw new IllegalStateException("Failed to allocate session ID");
}
4.如果installFlag包括PackageManager.INSTALL_INTERNAL(if ((params.installFlags & PackageManager.INSTALL_INTERNAL) != 0)
),则通过buildSessionDir(sessionId, params)
方法创建一个特殊文件stageDir
private File buildSessionDir(int sessionId, SessionParams params) {
// 当params.isStaged为真的时候表示下次启动的时候才真正安装这个应用,一般用于apex的安装。
if (params.isStaged) {
// 所以我们不用关心这个
final File sessionStagingDir = Environment.getDataStagingDirectory(params.volumeUuid);
return new File(sessionStagingDir, "session_" + sessionId);
}
// 核心是buildTmpSessionDir方法
//***24***
return buildTmpSessionDir(sessionId, params.volumeUuid);
}
private File buildTmpSessionDir(int sessionId, String volumeUuid) {
final File sessionStagingDir = getTmpSessionDir(volumeUuid);
return new File(sessionStagingDir, "vmdl" + sessionId + ".tmp");
}
private File getTmpSessionDir(String volumeUuid) {
return Environment.getDataAppDirectory(volumeUuid);
}
/** {@hide} */
public static File getDataStagingDirectory(String volumeUuid) {
return new File(getDataDirectory(volumeUuid), "app-staging");
}
/** {@hide} */
public static File getDataAppDirectory(String volumeUuid) {
return new File(getDataDirectory(volumeUuid), "app");
}
/** {@hide} */
public static File getDataDirectory(String volumeUuid) {
if (TextUtils.isEmpty(volumeUuid)) {
return DIR_ANDROID_DATA;
} else {
return new File("/mnt/expand/" + volumeUuid);
}
}
如果isStaged=true
且volumeUuid.isEmpty()
,stageDir
对应的文件就是/data/app/app-staging/session_sessionId
如果isStaged=true
且!volumeUuid.isEmpty()
,stageDir
对应的文件就是/mnt/expand/volumeUuid/app-staging/session_sessionId
如果isStaged=false
且volumeUuid.isEmpty()
,stageDir
对应的文件就是
/data/app/app/vmdl${serssionId}.tmp
如果isStaged=false
且!volumeUuid.isEmpty()
,stageDir
对应的文件就是/mnt/expand/${volumeId}/app/vmdl${serssionId}.tmp
鉴于我们上文中说isStaged=true的情况不考虑,所以其实我们要分析的stageDir就是/data/app/app/vmdl${serssionId}.tmp
或者/mnt/expand/${volumeId}/app/vmdl${serssionId}.tmp
。
5.如果installFlag不包括PackageManager.INSTALL_INTERNAL,则执行stageCid = buildExternalStageCid(sessionId)
,生成一个新的名字smdlsessionid.tmp名字保存到PackageInstallerSession中。不包含PackageManager.INSTALL_INTERNAL也就意味着不安装在/data下,我们暂不做分析。
private String buildExternalStageCid(int sessionId) {
return "smdl" + sessionId + ".tmp";
}
6.生成一个PackageInstallerSession对象,并保存到mSessions这个Map中,唤醒监听者notifySessionCreated的Session创建成功的监听。
session = new PackageInstallerSession(mInternalCallback, mContext, mPm, this,
mInstallThread.getLooper(), mStagingManager, sessionId, userId,
installerPackageName, callingUid, params, createdMillis, stageDir, stageCid, false,
false, false, null, SessionInfo.INVALID_ID, false, false, false,
SessionInfo.STAGED_SESSION_NO_ERROR, "");
synchronized (mSessions) {
mSessions.put(sessionId, session);
}
if (params.isStaged) {
mStagingManager.createSession(session);
}
if ((session.params.installFlags & PackageManager.INSTALL_DRY_RUN) == 0) {
mCallbacks.notifySessionCreated(session.sessionId, session.userId);
}
7.writeSessionsAsync()
通过IO线程,调用PackageInstallerSession的write方法,把数据全部写入到PackageInstallerSession的
private void writeSessionsAsync() {
IoThread.getHandler().post(new Runnable() {
@Override
public void run() {
synchronized (mSessions) {
//***25***
writeSessionsLocked();
}
}
});
}
@GuardedBy("mSessions")
private void writeSessionsLocked() {
if (LOGD) Slog.v(TAG, "writeSessionsLocked()");
FileOutputStream fos = null;
try {
fos = mSessionsFile.startWrite();
XmlSerializer out = new FastXmlSerializer();
out.setOutput(fos, StandardCharsets.UTF_8.name());
out.startDocument(null, true);
out.startTag(null, TAG_SESSIONS);
final int size = mSessions.size();
for (int i = 0; i < size; i++) {
final PackageInstallerSession session = mSessions.valueAt(i);
session.write(out, mSessionsDir);
}
out.endTag(null, TAG_SESSIONS);
out.endDocument();
mSessionsFile.finishWrite(fos);
} catch (IOException e) {
if (fos != null) {
mSessionsFile.failWrite(fos);
}
}
}
其实这个过程就很简单了,就是往/data/system/install_sessions.xml
中写入当前PackageInstallerSession的配置数据。至于这个/data/system/install_sessions.xml
,它是在PMS初始化的时候生成的,而PMS初始化是在Android系统刚启动的时候完成的,我们后续会分析其详细作用。
上面onCreate()方法,这个过程主要还是初始化一些参数变量,并没有看到安装的逻辑。下面我们就来看看其onResume()方法:
@Override
protected void onResume() {
super.onResume();
// This is the first 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()) {
//***26***
mInstallingTask = new InstallingAsyncTask();
mInstallingTask.execute();
} else {
// we will receive a broadcast when the install is finished
mCancelButton.setEnabled(false);
setFinishOnTouchOutside(false);
}
}
}
在onCreate的过程通过PackageInstallerService的CreateSession方法实例化获取到每一个Session对应的id,这里通过sessionId调用PackageInstallerService的getSessionInfo()方法获取到PackageInstaller.SessionInfo,然后执行InstallingAsyncTask。
/**
* Send the package to the package installer and then register a event result observer that
* will call {@link #launchFinishBasedOnResult(int, int, String)}
*/
private final class InstallingAsyncTask extends AsyncTask<Void, Void,
PackageInstaller.Session> {
volatile boolean isDone;
@Override
protected PackageInstaller.Session doInBackground(Void... params) {
PackageInstaller.Session session;
try {
session = getPackageManager().getPackageInstaller().openSession(mSessionId);
} catch (IOException e) {
return null;
}
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) {
Log.e(LOG_TAG, "Could not write package", e);
session.close();
return null;
} finally {
synchronized (this) {
isDone = true;
notifyAll();
}
}
}
@Override
protected void onPostExecute(PackageInstaller.Session session) {
if (session != null) {
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);
//***27***
session.commit(pendingIntent.getIntentSender());
mCancelButton.setEnabled(false);
setFinishOnTouchOutside(false);
} else {
//***28***
getPackageManager().getPackageInstaller().abandonSession(mSessionId);
if (!isCancelled()) {
launchFailure(PackageManager.INSTALL_FAILED_INVALID_APK, null);
}
}
}
}
这个InstallingAsyncTask 其实就是在通过PackageInstallerService.openSession方法打开一个session,并通过FileInputStream读取临时apk文件的数据流,并通过session.openWrite ,把临时apk数据往session的OutputSream中写入。下面我们分别看看openSession()
和openWrite()
#PackageInstaller
public @NonNull Session openSession(int sessionId) throws IOException {
try {
try {
//***29***
return new Session(mInstaller.openSession(sessionId));
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
} catch (RuntimeException e) {
ExceptionUtils.maybeUnwrapIOException(e);
throw e;
}
}
#PackageInstallerService
@Override
public IPackageInstallerSession openSession(int sessionId) {
try {
//***30***
return openSessionInternal(sessionId);
} catch (IOException e) {
throw ExceptionUtils.wrap(e);
}
}
private IPackageInstallerSession openSessionInternal(int sessionId) throws IOException {
synchronized (mSessions) {
final PackageInstallerSession session = mSessions.get(sessionId);
if (session == null || !isCallingUidOwner(session)) {
throw new SecurityException("Caller has no access to session " + sessionId);
}
session.open();
return session;
}
}
#PackageInstallerSession
public void open() throws IOException {
if (mActiveCount.getAndIncrement() == 0) {
mCallback.onSessionActiveChanged(this, true);
}
boolean wasPrepared;
synchronized (mLock) {
wasPrepared = mPrepared;
if (!mPrepared) {
if (stageDir != null) {
//***31***
prepareStageDir(stageDir);
} else if (params.isMultiPackage) {
// it's all ok
} else {
throw new IllegalArgumentException("stageDir must be set");
}
mPrepared = true;
}
}
if (!wasPrepared) {
mCallback.onSessionPrepared(this);
}
}
#PackageInstallerService
static void prepareStageDir(File stageDir) throws IOException {
if (stageDir.exists()) {
throw new IOException("Session dir already exists: " + stageDir);
}
try {
//***32***
Os.mkdir(stageDir.getAbsolutePath(), 0775);
Os.chmod(stageDir.getAbsolutePath(), 0775);
} catch (ErrnoException e) {
// This purposefully throws if directory already exists
throw new IOException("Failed to prepare session dir: " + stageDir, e);
}
if (!SELinux.restorecon(stageDir)) {
throw new IOException("Failed to restorecon session dir: " + stageDir);
}
}
opneSession()
最后只是把stageDir
这个文件设置权限为0755,也就是当前用户允许操作所有权限,同用户组或者其他用户的权限只能读。
#PackageInstaller
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 {
//主要分析这个分支 ***33***
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();
}
}
#PackagerInstallerSession
@Override
public ParcelFileDescriptor openWrite(String name, long offsetBytes, long lengthBytes) {
try {
//***34***
return doWriteInternal(name, offsetBytes, lengthBytes, null);
} catch (IOException e) {
throw ExceptionUtils.wrap(e);
}
}
private ParcelFileDescriptor doWriteInternal(String name, long offsetBytes, long lengthBytes,
ParcelFileDescriptor incomingFd) throws IOException {
// Quick sanity check of state, and allocate a pipe for ourselves. We
// then do heavy disk allocation outside the lock, but this open pipe
// will block any attempted install transitions.
final RevocableFileDescriptor fd;
final FileBridge bridge;
final File stageDir;
synchronized (mLock) {
assertCallerIsOwnerOrRootLocked();
assertPreparedAndNotSealedLocked("openWrite");
if (PackageInstaller.ENABLE_REVOCABLE_FD) {
fd = new RevocableFileDescriptor();
bridge = null;
mFds.add(fd);
} else {
fd = null;
bridge = new FileBridge();
mBridges.add(bridge);
}
//***35***
stageDir = resolveStageDirLocked();
}
try {
// Use installer provided name for now; we always rename later
if (!FileUtils.isValidExtFilename(name)) {
throw new IllegalArgumentException("Invalid name: " + name);
}
final File target;
final long identity = Binder.clearCallingIdentity();
try {
target = new File(stageDir, name);
} finally {
Binder.restoreCallingIdentity(identity);
}
// TODO: this should delegate to DCS so the system process avoids
// holding open FDs into containers.
//***36***
final FileDescriptor targetFd = Os.open(target.getAbsolutePath(),
O_CREAT | O_WRONLY, 0644);
Os.chmod(target.getAbsolutePath(), 0644);
// If caller specified a total length, allocate it for them. Free up
// cache space to grow, if needed.
if (stageDir != null && lengthBytes > 0) {//***37***
mContext.getSystemService(StorageManager.class).allocateBytes(targetFd, lengthBytes,
PackageHelper.translateAllocateFlags(params.installFlags));
}
if (offsetBytes > 0) {
Os.lseek(targetFd, offsetBytes, OsConstants.SEEK_SET);
}
if (incomingFd != null) {//暂时没搞懂,应该也不是核心流程,暂不分析
switch (Binder.getCallingUid()) {
case android.os.Process.SHELL_UID:
case android.os.Process.ROOT_UID:
case android.os.Process.SYSTEM_UID:
break;
default:
throw new SecurityException(
"Reverse mode only supported from shell or system");
}
// In "reverse" mode, we're streaming data ourselves from the
// incoming FD, which means we never have to hand out our
// sensitive internal FD. We still rely on a "bridge" being
// inserted above to hold the session active.
try {
final Int64Ref last = new Int64Ref(0);
//***38***
FileUtils.copy(incomingFd.getFileDescriptor(), targetFd, lengthBytes, null,
Runnable::run, (long progress) -> {
if (params.sizeBytes > 0) {
final long delta = progress - last.value;
last.value = progress;
addClientProgress((float) delta / (float) params.sizeBytes);
}
});
} finally {
IoUtils.closeQuietly(targetFd);
IoUtils.closeQuietly(incomingFd);
// We're done here, so remove the "bridge" that was holding
// the session active.
synchronized (mLock) {
if (PackageInstaller.ENABLE_REVOCABLE_FD) {
mFds.remove(fd);
} else {
bridge.forceClose();
mBridges.remove(bridge);
}
}
}
return null;
} else if (PackageInstaller.ENABLE_REVOCABLE_FD) {
fd.init(mContext, targetFd);
return fd.getRevocableFileDescriptor();
} else {
bridge.setTargetFile(targetFd);
bridge.start();
return new ParcelFileDescriptor(bridge.getClientSocket());
}
} catch (ErrnoException e) {
throw e.rethrowAsIOException();
}
}
session.openWrite("PackageInstaller", 0, sizeBytes)
则是调用PackageInstallerSession的openWrite()
,然后调用doWriteInternal()
,doWriteInternal()
方法主要根据stageDir目录下生成一个特殊的文件(其实就是/data/app/vmdlsessionId.tmp/PackageInstaller
)并调用Os.open创建这个文件,设置这个文件的权限是0644(也就是本用户能读写,其他和同一个用户组只能读),然后根据文件大小分配内存长度(如果内存不够需要释放内存),最后拿到ParcelFileDescriptor,再将ParcelFileDescriptor作为FileBridge.FileBridgeOutputStream构造方法的参数传入,最终封装获取到一个OutputStream。
public static class FileBridgeOutputStream extends OutputStream {
private final ParcelFileDescriptor mClientPfd;
private final FileDescriptor mClient;
private final byte[] mTemp = new byte[MSG_LENGTH];
public FileBridgeOutputStream(ParcelFileDescriptor clientPfd) {
mClientPfd = clientPfd;
mClient = clientPfd.getFileDescriptor();
}
public FileBridgeOutputStream(FileDescriptor client) {
mClientPfd = null;
mClient = client;
}
@Override
public void close() throws IOException {
try {
//***39***
writeCommandAndBlock(CMD_CLOSE, "close()");
} finally {
IoBridge.closeAndSignalBlockedThreads(mClient);
IoUtils.closeQuietly(mClientPfd);
}
}
public void fsync() throws IOException {
//***40***
writeCommandAndBlock(CMD_FSYNC, "fsync()");
}
private void writeCommandAndBlock(int cmd, String cmdString) throws IOException {
Memory.pokeInt(mTemp, 0, cmd, ByteOrder.BIG_ENDIAN);
IoBridge.write(mClient, mTemp, 0, MSG_LENGTH);
// Wait for server to ack
if (IoBridge.read(mClient, mTemp, 0, MSG_LENGTH) == MSG_LENGTH) {
if (Memory.peekInt(mTemp, 0, ByteOrder.BIG_ENDIAN) == cmd) {
return;
}
}
throw new IOException("Failed to execute " + cmdString + " across bridge");
}
@Override
public void write(byte[] buffer, int byteOffset, int byteCount) throws IOException {
ArrayUtils.throwsIfOutOfBounds(buffer.length, byteOffset, byteCount);
Memory.pokeInt(mTemp, 0, CMD_WRITE, ByteOrder.BIG_ENDIAN);
Memory.pokeInt(mTemp, 4, byteCount, ByteOrder.BIG_ENDIAN);
IoBridge.write(mClient, mTemp, 0, MSG_LENGTH);
IoBridge.write(mClient, buffer, byteOffset, byteCount);
}
@Override
public void write(int oneByte) throws IOException {
Streams.writeSingleByte(this, oneByte);
}
}
@Deprecated
public class FileBridge extends Thread {
private static final String TAG = "FileBridge";
// TODO: consider extending to support bidirectional IO
private static final int MSG_LENGTH = 8;
/** CMD_WRITE [len] [data] */
private static final int CMD_WRITE = 1;
/** CMD_FSYNC */
private static final int CMD_FSYNC = 2;
/** CMD_CLOSE */
private static final int CMD_CLOSE = 3;
private FileDescriptor mTarget;
private final FileDescriptor mServer = new FileDescriptor();
private final FileDescriptor mClient = new FileDescriptor();
private volatile boolean mClosed;
public FileBridge() {
try {
//***41***
Os.socketpair(AF_UNIX, SOCK_STREAM, 0, mServer, mClient);
} catch (ErrnoException e) {
throw new RuntimeException("Failed to create bridge");
}
}
public boolean isClosed() {
return mClosed;
}
public void forceClose() {
IoUtils.closeQuietly(mTarget);
IoUtils.closeQuietly(mServer);
IoUtils.closeQuietly(mClient);
mClosed = true;
}
public void setTargetFile(FileDescriptor target) {
mTarget = target;
}
public FileDescriptor getClientSocket() {
return mClient;
}
@Override
public void run() {
final byte[] temp = new byte[8192];
try {
while (IoBridge.read(mServer, temp, 0, MSG_LENGTH) == MSG_LENGTH) {
final int cmd = Memory.peekInt(temp, 0, ByteOrder.BIG_ENDIAN);
if (cmd == CMD_WRITE) {
// Shuttle data into local file
int len = Memory.peekInt(temp, 4, ByteOrder.BIG_ENDIAN);
while (len > 0) {
int n = IoBridge.read(mServer, temp, 0, Math.min(temp.length, len));
if (n == -1) {
throw new IOException(
"Unexpected EOF; still expected " + len + " bytes");
}
IoBridge.write(mTarget, temp, 0, n);
len -= n;
}
} else if (cmd == CMD_FSYNC) {
// Sync and echo back to confirm
Os.fsync(mTarget);
IoBridge.write(mServer, temp, 0, MSG_LENGTH);
} else if (cmd == CMD_CLOSE) {
// Close and echo back to confirm
Os.fsync(mTarget);
Os.close(mTarget);
mClosed = true;
IoBridge.write(mServer, temp, 0, MSG_LENGTH);
break;
}
}
} catch (ErrnoException | IOException e) {
Log.wtf(TAG, "Failed during bridge", e);
} finally {
forceClose();
}
}
}
1.FileBridge是过时的,后续会被RevocableFileDescriptor替代
2.FileBridge的构造函数中通过 Os.socketpair
生成一对socket
3.FileBridge是一个线程,一旦开启,就会监听mServer 服务端的socket是否有数据到来,有数据到来,则开始分析当前的命令是什么并执行相应的操作
4.命令结构如下:
socket数据 | 头8位高4位 | 头8位低4位 | 头8位的决定的长度内容 |
---|---|---|---|
意义 | 命令 | 长度 | 传递过来的数据内容 |
- 先从mServer的服务端socket读取当前数据的命令和命令中对应数据的长度
- CMD_WRITE 说明需要开始写入数据,从socket中读取长度对应的内容,并把当前的数据写入到设置到FileBridge中的mTarget中
- CMD_FSYNC 则把mTarget对应缓存数据刷到mTarget这个文件中
- CMD_CLOSE 则关闭服务端socket的监听和需要写入的文件fd
5.FileBridgeOutputStream的write()
过程就是不断的往mClient 的socket端口写入数据,先写入命令CMD_WRITE,然后byteCount但是这个过程是以BIG_ENDIAN写入,因此两个顺序是颠倒的。最后是apk包数据内容。
6.写完数据后,执行FileBridgeOutputStream的fsync()
方法--->writeCommandAndBlock()
方法,就是写入了一个CMD_FSYNC命令交给正在监听的FileBridge处理,调用Os.fsync从缓存刷入磁盘。
7.完成以上步骤,就把临时文件/data/no_backup/packagexxx.apk
拷贝到/data/app/vmdlsessionId.tmp/PackageInstaller
中了。
再来看看InstallingAsyncTask的onPostExecute()
方法:
@Override
protected void onPostExecute(PackageInstaller.Session session) {
if (session != null) {
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);
//***42***
session.commit(pendingIntent.getIntentSender());
mCancelButton.setEnabled(false);
setFinishOnTouchOutside(false);
} else {
getPackageManager().getPackageInstaller().abandonSession(mSessionId);
if (!isCancelled()) {
launchFailure(PackageManager.INSTALL_FAILED_INVALID_APK, null);
}
}
}
构造一个BROADCAST_ACTION(com.android.packageinstaller.ACTION_INSTALL_COMMIT
)的PendingIntent广播,调用session的commit方法,进行发送。
#PackageInstaller.Session
public void commit(@NonNull IntentSender statusReceiver) {
try {
//***43***
mSession.commit(statusReceiver, false);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
#PackageInstallerSession
@Override
public void commit(@NonNull IntentSender statusReceiver, boolean forTransfer) {
if (hasParentSessionId()) {
throw new IllegalStateException(
"Session " + sessionId + " is a child of multi-package session "
+ mParentSessionId + " and may not be committed directly.");
}
//***44***
if (!markAsCommitted(statusReceiver, forTransfer)) {
return;
}
if (isMultiPackage()) {
final SparseIntArray remainingSessions = mChildSessionIds.clone();
final IntentSender childIntentSender =
new ChildStatusIntentReceiver(remainingSessions, statusReceiver)
.getIntentSender();
RuntimeException commitException = null;
boolean commitFailed = false;
for (int i = mChildSessionIds.size() - 1; i >= 0; --i) {
final int childSessionId = mChildSessionIds.keyAt(i);
try {
// commit all children, regardless if any of them fail; we'll throw/return
// as appropriate once all children have been processed
//***45***
if (!mSessionProvider.getSession(childSessionId)
.markAsCommitted(childIntentSender, forTransfer)) {
commitFailed = true;
}
} catch (RuntimeException e) {
commitException = e;
}
}
if (commitException != null) {
throw commitException;
}
if (commitFailed) {
return;
}
}
//***46***
mHandler.obtainMessage(MSG_COMMIT).sendToTarget();
}
/**
* Do everything but actually commit the session. If this was not already called, the session
* will be sealed and marked as committed. The caller of this method is responsible for
* subsequently submitting this session for processing.
*
* This method may be called multiple times to update the status receiver validate caller
* permissions.
*/
public boolean markAsCommitted(
@NonNull IntentSender statusReceiver, boolean forTransfer) {
Preconditions.checkNotNull(statusReceiver);
List<PackageInstallerSession> childSessions = getChildSessions();
final boolean wasSealed;
synchronized (mLock) {
assertCallerIsOwnerOrRootLocked();
assertPreparedAndNotDestroyedLocked("commit");
//***47***
final PackageInstallObserverAdapter adapter = new PackageInstallObserverAdapter(
mContext, statusReceiver, sessionId,
isInstallerDeviceOwnerOrAffiliatedProfileOwnerLocked(), userId);
//***48***
//这个mRemoteObserver是为什么了当前类的Handler接收MSG_ON_PACKAGE_INSTALLED事件,
//回调到PackageInstallObserverAdapter.onPackageInstalled()方法展示Notification,也即安装最终完成时了。
//注意这个mRemoteObserver和后文Android APK安装流程(3)里的makeSessionActiveLocked()的localObserver的区别
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");
}
}
// After validations and updating the observer, we can skip re-sealing, etc. because we
// have already marked ourselves as committed.
if (mCommitted) {
return true;
}
wasSealed = mSealed;
if (!mSealed) {
try {
//***49***
sealAndValidateLocked(childSessions);
} catch (IOException e) {
throw new IllegalArgumentException(e);
} catch (PackageManagerException e) {
// Do now throw an exception here to stay compatible with O and older
destroyInternal();
dispatchSessionFinished(e.error, ExceptionUtils.getCompleteMessage(e), null);
return false;
}
}
// Client staging is fully done at this point
mClientProgress = 1f;
computeProgressLocked(true);
// This ongoing commit should keep session active, even though client
// will probably close their end.
mActiveCount.incrementAndGet();
mCommitted = true;
}
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);
}
return true;
}
public class PackageInstallObserver {
private final IPackageInstallObserver2.Stub mBinder = new IPackageInstallObserver2.Stub() {
@Override
public void onUserActionRequired(Intent intent) {
//***50***
PackageInstallObserver.this.onUserActionRequired(intent);
}
@Override
public void onPackageInstalled(String basePackageName, int returnCode,
String msg, Bundle extras) {
//***51***
PackageInstallObserver.this.onPackageInstalled(basePackageName, returnCode, msg,
extras);
}
};
/** {@hide} */
public IPackageInstallObserver2 getBinder() {
return mBinder;
}
···
从以上代码我们可以看出commit()
方法里主要做了以下几个事:
1.在执行commit()
子方法markAsCommitted()
中完成了PackageInstallObserverAdapter对象的构建,并获取到adapter的Binder对象mRemoteObserver
,也就是IPackageInstallObserver2。这个Binder将会监听从远程端发送过来onPackageInstalled安装完毕的消息(真正安装完毕后会回调到这里)。
2.在执行markAsCommitted()
子方法sealAndValidateLocked()
中完成了把当前拷贝过来的文件进行一次重命名,并且清除该文件夹下可能安装过的临时文件。
3.在执行markAsCommitted()
子方法computeProgressLocked()
中完成了进度计算并且回调。
4.如果是多package安装,就会循环执行markAsCommitted()
5.最后mHandler.obtainMessage(MSG_COMMIT).sendToTarget();
发送MSG_COMMIT 的Handler消息
6.理论上markAsCommitted()
只会执行单package的,多package的都会递归进来。
我们来简单梳理一下markAsCommitted()
的子方法sealAndValidateLocked()
:
/**
* Seal the session to prevent further modification and validate the contents of it.
*
* <p>The session will be sealed after calling this method even if it failed.
*
* @param childSessions the child sessions of a multipackage that will be checked for
* consistency. Can be null if session is not multipackage.
* @throws PackageManagerException if the session was sealed but something went wrong. If the
* session was sealed this is the only possible exception.
*/
private void sealAndValidateLocked(List<PackageInstallerSession> childSessions)
throws PackageManagerException, IOException {
assertNoWriteFileTransfersOpenLocked();
assertPreparedAndNotDestroyedLocked("sealing of session");
mSealed = true;
if (childSessions != null) {
assertMultiPackageConsistencyLocked(childSessions);
}
if (params.isStaged) {
final PackageInstallerSession activeSession = mStagingManager.getActiveSession();
final boolean anotherSessionAlreadyInProgress =
activeSession != null && sessionId != activeSession.sessionId
&& mParentSessionId != activeSession.sessionId;
if (anotherSessionAlreadyInProgress) {
throw new PackageManagerException(
PackageManager.INSTALL_FAILED_OTHER_STAGED_SESSION_IN_PROGRESS,
"There is already in-progress committed staged session "
+ activeSession.sessionId, null);
}
}
// Read transfers from the original owner stay open, but as the session's data
// cannot be modified anymore, there is no leak of information. For staged sessions,
// further validation is performed by the staging manager.
if (!params.isMultiPackage) {
final PackageInfo pkgInfo = mPm.getPackageInfo(
params.appPackageName, PackageManager.GET_SIGNATURES
| PackageManager.MATCH_STATIC_SHARED_LIBRARIES /*flags*/, userId);
//***52***
resolveStageDirLocked();
try {
// 我们不做apex文件的分析。什么是APEX文件?
if ((params.installFlags & PackageManager.INSTALL_APEX) != 0) {
//***53***
validateApexInstallLocked();
} else {
//***54***
validateApkInstallLocked(pkgInfo);
}
} catch (PackageManagerException e) {
throw e;
} catch (Throwable e) {
// Convert all exceptions into package manager exceptions as only those are handled
// in the code above
throw new PackageManagerException(e);
}
}
}
/**
* Resolve the actual location where staged data should be written. This
* might point at an ASEC mount point, which is why we delay path resolution
* until someone actively works with the session.
*/
@GuardedBy("mLock")
private File resolveStageDirLocked() throws IOException {
if (mResolvedStageDir == null) {
if (stageDir != null) {
mResolvedStageDir = stageDir;
} else {
throw new IOException("Missing stageDir");
}
}
return mResolvedStageDir;
}
/**
* Validate apex install.
* <p>
* Sets {@link #mResolvedBaseFile} for RollbackManager to use.
*/
@GuardedBy("mLock")
private void validateApexInstallLocked()
throws PackageManagerException {
final File[] addedFiles = mResolvedStageDir.listFiles(sAddedFilter);
if (ArrayUtils.isEmpty(addedFiles)) {
throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, "No packages staged");
}
if (ArrayUtils.size(addedFiles) > 1) {
throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
"Too many files for apex install");
}
mResolvedBaseFile = addedFiles[0];
}
/**
* Validate install by confirming that all application packages are have
* consistent package name, version code, and signing certificates.
* <p>
* Clears and populates {@link #mResolvedBaseFile},
* {@link #mResolvedStagedFiles}, and {@link #mResolvedInheritedFiles}.
* <p>
* Renames package files in stage to match split names defined inside.
* <p>
* Note that upgrade compatibility is still performed by
* {@link PackageManagerService}.
*/
@GuardedBy("mLock")
private void validateApkInstallLocked(@Nullable PackageInfo pkgInfo)
throws PackageManagerException {
ApkLite baseApk = null;
mPackageName = null;
mVersionCode = -1;
mSigningDetails = PackageParser.SigningDetails.UNKNOWN;
mResolvedBaseFile = null;
mResolvedStagedFiles.clear();
mResolvedInheritedFiles.clear();
// Partial installs must be consistent with existing install
if (params.mode == SessionParams.MODE_INHERIT_EXISTING
&& (pkgInfo == null || pkgInfo.applicationInfo == null)) {
throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
"Missing existing base package");
}
// Default to require only if existing base has fs-verity.
mVerityFound = PackageManagerServiceUtils.isApkVerityEnabled()
&& params.mode == SessionParams.MODE_INHERIT_EXISTING
&& VerityUtils.hasFsverity(pkgInfo.applicationInfo.getBaseCodePath());
try {
resolveStageDirLocked();
} catch (IOException e) {
throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR,
"Failed to resolve stage location", e);
}
//移除/data/app/vmd+sessionId+tmp下面.removed结尾的文件
final File[] removedFiles = mResolvedStageDir.listFiles(sRemovedFilter);
final List<String> removeSplitList = new ArrayList<>();
if (!ArrayUtils.isEmpty(removedFiles)) {
for (File removedFile : removedFiles) {
final String fileName = removedFile.getName();
final String splitName = fileName.substring(
0, fileName.length() - REMOVE_SPLIT_MARKER_EXTENSION.length());
removeSplitList.add(splitName);
}
}
//添加/data/app/vmd+sessionId+tmp下面不是.removed,.dm和.fsv_sig结尾的文件
final File[] addedFiles = mResolvedStageDir.listFiles(sAddedFilter);
if (ArrayUtils.isEmpty(addedFiles) && removeSplitList.size() == 0) {
throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, "No packages staged");
}
// Verify that all staged packages are internally consistent
final ArraySet<String> stagedSplits = new ArraySet<>();
for (File addedFile : addedFiles) {
final ApkLite apk;
try {
//解析apk,主要解析mainfest,获取包名及其他属性信息coreApp ,multiArch,use32bitAbi, extractNativeLibs ,versionCode
apk = PackageParser.parseApkLite(
addedFile, PackageParser.PARSE_COLLECT_CERTIFICATES);
} catch (PackageParserException e) {
throw PackageManagerException.from(e);
}
if (!stagedSplits.add(apk.splitName)) {
throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
"Split " + apk.splitName + " was defined multiple times");
}
// Use first package to define unknown values
if (mPackageName == null) {
mPackageName = apk.packageName;
mVersionCode = apk.getLongVersionCode();
}
if (mSigningDetails == PackageParser.SigningDetails.UNKNOWN) {
mSigningDetails = apk.signingDetails;
}
assertApkConsistentLocked(String.valueOf(addedFile), apk);
// Take this opportunity to enforce uniform naming
final String targetName;
if (apk.splitName == null) {
targetName = "base" + APK_FILE_EXTENSION;
} else {
targetName = "split_" + apk.splitName + APK_FILE_EXTENSION;
}
if (!FileUtils.isValidExtFilename(targetName)) {
throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
"Invalid filename: " + targetName);
}
final File targetFile = new File(mResolvedStageDir, targetName);
//***55***
resolveAndStageFile(addedFile, targetFile);
// Base is coming from session
if (apk.splitName == null) {
mResolvedBaseFile = targetFile;
baseApk = apk;
}
//***56***
final File dexMetadataFile = DexMetadataHelper.findDexMetadataForFile(addedFile);
if (dexMetadataFile != null) {
if (!FileUtils.isValidExtFilename(dexMetadataFile.getName())) {
throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
"Invalid filename: " + dexMetadataFile);
}
final File targetDexMetadataFile = new File(mResolvedStageDir,
DexMetadataHelper.buildDexMetadataPathForApk(targetName));
resolveAndStageFile(dexMetadataFile, targetDexMetadataFile);
}
}
if (removeSplitList.size() > 0) {
if (pkgInfo == null) {
throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
"Missing existing base package for " + mPackageName);
}
// validate split names marked for removal
for (String splitName : removeSplitList) {
if (!ArrayUtils.contains(pkgInfo.splitNames, splitName)) {
throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
"Split not found: " + splitName);
}
}
// ensure we've got appropriate package name, version code and signatures
if (mPackageName == null) {
mPackageName = pkgInfo.packageName;
mVersionCode = pkgInfo.getLongVersionCode();
}
if (mSigningDetails == PackageParser.SigningDetails.UNKNOWN) {
try {
mSigningDetails = ApkSignatureVerifier.unsafeGetCertsWithoutVerification(
pkgInfo.applicationInfo.sourceDir,
PackageParser.SigningDetails.SignatureSchemeVersion.JAR);
} catch (PackageParserException e) {
throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
"Couldn't obtain signatures from base APK");
}
}
}
if (params.mode == SessionParams.MODE_FULL_INSTALL) {
// Full installs must include a base package
if (!stagedSplits.contains(null)) {
throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
"Full install must include a base package");
}
} else {
final PackageLite existing;
final ApkLite existingBase;
ApplicationInfo appInfo = pkgInfo.applicationInfo;
try {
existing = PackageParser.parsePackageLite(new File(appInfo.getCodePath()), 0);
existingBase = PackageParser.parseApkLite(new File(appInfo.getBaseCodePath()),
PackageParser.PARSE_COLLECT_CERTIFICATES);
} catch (PackageParserException e) {
throw PackageManagerException.from(e);
}
assertApkConsistentLocked("Existing base", existingBase);
// Inherit base if not overridden
if (mResolvedBaseFile == null) {
mResolvedBaseFile = new File(appInfo.getBaseCodePath());
resolveInheritedFile(mResolvedBaseFile);
// Inherit the dex metadata if present.
final File baseDexMetadataFile =
DexMetadataHelper.findDexMetadataForFile(mResolvedBaseFile);
if (baseDexMetadataFile != null) {
resolveInheritedFile(baseDexMetadataFile);
}
baseApk = existingBase;
}
// Inherit splits if not overridden
if (!ArrayUtils.isEmpty(existing.splitNames)) {
for (int i = 0; i < existing.splitNames.length; i++) {
final String splitName = existing.splitNames[i];
final File splitFile = new File(existing.splitCodePaths[i]);
final boolean splitRemoved = removeSplitList.contains(splitName);
if (!stagedSplits.contains(splitName) && !splitRemoved) {
resolveInheritedFile(splitFile);
// Inherit the dex metadata if present.
final File splitDexMetadataFile =
DexMetadataHelper.findDexMetadataForFile(splitFile);
if (splitDexMetadataFile != null) {
resolveInheritedFile(splitDexMetadataFile);
}
}
}
}
// Inherit compiled oat directory.
final File packageInstallDir = (new File(appInfo.getBaseCodePath())).getParentFile();
mInheritedFilesBase = packageInstallDir;
final File oatDir = new File(packageInstallDir, "oat");
if (oatDir.exists()) {
final File[] archSubdirs = oatDir.listFiles();
// Keep track of all instruction sets we've seen compiled output for.
// If we're linking (and not copying) inherited files, we can recreate the
// instruction set hierarchy and link compiled output.
if (archSubdirs != null && archSubdirs.length > 0) {
final String[] instructionSets = InstructionSets.getAllDexCodeInstructionSets();
for (File archSubDir : archSubdirs) {
// Skip any directory that isn't an ISA subdir.
if (!ArrayUtils.contains(instructionSets, archSubDir.getName())) {
continue;
}
mResolvedInstructionSets.add(archSubDir.getName());
List<File> oatFiles = Arrays.asList(archSubDir.listFiles());
if (!oatFiles.isEmpty()) {
mResolvedInheritedFiles.addAll(oatFiles);
}
}
}
}
// Inherit native libraries for DONT_KILL sessions.
if (mayInheritNativeLibs() && removeSplitList.isEmpty()) {
File[] libDirs = new File[]{
new File(packageInstallDir, NativeLibraryHelper.LIB_DIR_NAME),
new File(packageInstallDir, NativeLibraryHelper.LIB64_DIR_NAME)};
for (File libDir : libDirs) {
if (!libDir.exists() || !libDir.isDirectory()) {
continue;
}
final List<File> libDirsToInherit = new LinkedList<>();
for (File archSubDir : libDir.listFiles()) {
if (!archSubDir.isDirectory()) {
continue;
}
String relLibPath;
try {
relLibPath = getRelativePath(archSubDir, packageInstallDir);
} catch (IOException e) {
Slog.e(TAG, "Skipping linking of native library directory!", e);
// shouldn't be possible, but let's avoid inheriting these to be safe
libDirsToInherit.clear();
break;
}
if (!mResolvedNativeLibPaths.contains(relLibPath)) {
mResolvedNativeLibPaths.add(relLibPath);
}
libDirsToInherit.addAll(Arrays.asList(archSubDir.listFiles()));
}
mResolvedInheritedFiles.addAll(libDirsToInherit);
}
}
}
if (baseApk.useEmbeddedDex) {
for (File file : mResolvedStagedFiles) {
if (file.getName().endsWith(".apk")
&& !DexManager.auditUncompressedDexInApk(file.getPath())) {
throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
"Some dex are not uncompressed and aligned correctly for "
+ mPackageName);
}
}
}
if (baseApk.isSplitRequired && stagedSplits.size() <= 1) {
throw new PackageManagerException(INSTALL_FAILED_MISSING_SPLIT,
"Missing split for " + mPackageName);
}
}
a.sealAndValidateLocked()
的resolveStageDirLocked()
方法主要是把stageDir赋值给mResolvedStageDir中,也就是/data/app/vmdlsessionId.tmp/
b.sealAndValidateLocked()
的validateApkInstallLocked()
方法主要是遍历mResolvedStageDir文件夹中/data/app/vmdlsessionId.tmp/
所有的文件,能通过sRemovedFilter和sAddedFilter识别出哪些文件需要删除,哪些文件是本次需要添加到安装流程中。所有.remove
后缀的文件都需要删除,所有.remove
、.dm
和.fsv_sig
后缀的文件都不会添加到要安装里。
c.拿到这些需要添加到安装的流程的文件后,则对每一个apk包通过PackageParser.parseApkLite
解析出AndroidManifest最顶层的xml内容出来。获得其版本号,包名,签名等数据,并以包名为基础转化为两种形式的名字:
- 1.如果apk没有进行分割,则名字为
base.apk
- 2.如果分割,则为
split_splitname.apk
,并在/data/app/vmdlsessionId.tmp/
中生成一个该文件名的实例File(也就是/data/app/vmdlsessionId.tmp/base.apk
),最后添加到mResolvedStagedFiles集合中。接着校验这个apk文件在有没有对应的包名.dm
文件,存在则生成一个/data/app/vmdlsessionId.tmp/base.dm
文件.
截至到这里,Android10的APK拷贝过程已经完成。
我们再来简单梳理一下markAsCommitted()
的子方法computeProgressLocked()
:
@GuardedBy("mLock")
private void computeProgressLocked(boolean forcePublish) {
mProgress = MathUtils.constrain(mClientProgress * 0.8f, 0f, 0.8f)
+ MathUtils.constrain(mInternalProgress * 0.2f, 0f, 0.2f);
// Only publish when meaningful change
if (forcePublish || Math.abs(mProgress - mReportedProgress) >= 0.01) {
mReportedProgress = mProgress;
mCallback.onSessionProgressChanged(this, mProgress);
}
}
决定当前安装进度的有两个,是mClientProgress和mInternalProgress。mClientProgress是指来自PackageInstallerSession之外的PMS的安装进度,另一方面就是mInternalProgress也就是PackageInstallSession本身的进度。mClientProgress的进度要乘以0.8,并且把这个结果约束到0~0.8之间。mInternalProgress的进度需要乘以0.5,并且把结果约束到0~0.2之间。
最后执行mHandler.obtainMessage(MSG_COMMIT).sendToTarget()
方法我们会在后续流程Android APK安装流程(3)讲解。