美文网首页
Android APK安装流程(2)--APK的拷贝

Android APK安装流程(2)--APK的拷贝

作者: MY1112 | 来源:发表于2021-06-28 11:06 被阅读0次
    • 第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到了这里只有packagefile2种了,如果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=truevolumeUuid.isEmpty()stageDir对应的文件就是/data/app/app-staging/session_sessionId
    如果isStaged=true!volumeUuid.isEmpty() ,stageDir对应的文件就是/mnt/expand/volumeUuid/app-staging/session_sessionId
    如果isStaged=falsevolumeUuid.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)讲解。

    相关文章

      网友评论

          本文标题:Android APK安装流程(2)--APK的拷贝

          本文链接:https://www.haomeiwen.com/subject/cxheyltx.html