美文网首页Android开发AndroidAndroid开发经验谈
09 Android包管理机制-PMS的创建过程-Android

09 Android包管理机制-PMS的创建过程-Android

作者: 凤邪摩羯 | 来源:发表于2021-02-08 16:46 被阅读0次

    PackageManagerService启动流程和APK安装流程(Android 10)

    PackageManagerService简称PKMS,Android系统的核心服务之一,是在SystemServer的run方法中的startBootstrapServices启动的,管理者着所有跟Package相关的工作,常见的比如安装,卸载,信息查询等,主要完成以下核心功能:

    1. 解析AndroidNanifest.xml清单文件,解析清单文件中的所有节点信息。
    2. 扫描.apk文件,安装系统应用,安装本地应用等。
    3. 管理本地应用,主要有, 安装,卸载,应用信息查询等。

    PKMS调用方式

    客户端可通过Context.getPackageManager()获得ApplicationPackageManager对象, 而mPM指向的是Proxy代理,当调用到mPM.方法后,将会调用到IPackageManager的Proxy代理方法,然后通过Binder机制中的mRemote与服务端PackageManagerService通信,并调用到PackageManagerService的方法。

    image.png

    Binder服务端:PackageManagerService继承于IPackageManager.Stub;

    Binder客户端:ApplicationPackageManager(简称APM)的成员变量mPM继承于IPackageManager.Stub.Proxy; 本身APM是继承于PackageManager对象。

    简单的调用如下:

    /**
         * 得到应用层序的版本名称
         * 
         * @return
         */
        private String getVersionName() {
            PackageManager packageManager = getPackageManager();
            try {
                // 得到apk的功能清单文件:为了防止出错直接使用getPackageName()方法获得包名
                // packageManager.getPackageInfo("com.xxx.xxx", 0);
                PackageInfo packageInfo = packageManager.getPackageInfo(getPackageName(), 0);
    
                //返回版本名称
                return packageInfo.versionName;
            } catch (NameNotFoundException e) {
                e.printStackTrace();
                return "";
            }
        }
    复制代码
    

    PKMS启动过程分析

    首先描述一下大致的启动流程:SystemServer.startBootstrapServices()函数中启动PKMS服务,再调用startOtherServices()函数中对dex优化,磁盘管理功能,让PKMS进入systemReady状态。

    再来看一下PKMS的启动时序图和流程图,有助于我们了解大概的类调用和启动流程,下面在进行详细分析

    PKMS启动时序图
    image.png
    PKMS启动流程图
    image.png
    具体步骤分析

    第一步到第四步,都在startBootstrapServices()中,首先启动Intaller服务,也就是安装器,随后判断当前设备是否处于加密状态,如果只是解析核心应用,接着调用PKMS的静态方法main来创建pms对象。

    • 第一步:启动Installer服务(阻塞等待),以便有机会创建具有适当权限的关键目录,如/data/user,我们需要在初始化其他服务之前完成此任务
    Installer installer = mSystemServiceManager.startService(Installer.class);
    mActivityManagerService.setInstaller(installer);
    复制代码
    
    • 第二步:获取设备是否加密(手机设置密码),如果设备加密了,则只解析"core"应用
    // Only run "core" apps if we're encrypting the device.
    String cryptState = VoldProperties.decrypt().orElse("");
    复制代码
    
    • 第三步: 调用PKMS的main方法初始化PackageManagerService,其中调用PackageManagerService()构造函数创建了PKMS对象
    mPackageManagerService = PackageManagerService.main(mSystemContext, installer,
            mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore);
    //main方法
    public static PackageManagerService main(Context context, Installer installer,
                boolean factoryTest, boolean onlyCore) {
            // Self-check for initial settings.
                // 检查Package编译相关系统属性
            PackageManagerServiceCompilerMapping.checkProperties();
                    //执行pkms构造方法
            PackageManagerService m = new PackageManagerService(context, installer,
                    factoryTest, onlyCore);
                //启动部分应用服务于多用户场景
            m.enableSystemUserPackages();
                //往ServiceManager中注册"package"和"package_native"
            ServiceManager.addService("package", m);
            final PackageManagerNative pmn = m.new PackageManagerNative();
            ServiceManager.addService("package_native", pmn);
            return m;
        }
    复制代码
    
    • 第四步:如果设备没有加密,操作它。管理 A/B OTA dexopting.
    // Manages A/B OTA dexopting. This is a bootstrap service as we need it to rename
    // A/B artifacts after boot, before anything else might touch/need them.
    // Note: this isn't needed during decryption (we don't have /data anyways).
    if (!mOnlyCore) {
      boolean disableOtaDexopt = SystemProperties.getBoolean("config.disable_otadexopt",
                                                             false);
      if (!disableOtaDexopt) {
        traceBeginAndSlog("StartOtaDexOptService");
        try {
          Watchdog.getInstance().pauseWatchingCurrentThread("moveab");
          OtaDexoptService.main(mSystemContext, mPackageManagerService);
        } catch (Throwable e) {
          reportWtf("starting OtaDexOptService", e);
        } finally {
          Watchdog.getInstance().resumeWatchingCurrentThread("moveab");
          traceEnd();
        }
      }
    }
    复制代码
    

    第五,六,七步都在startOtherServices()方法中。

    • 第五步:如果设备没有加密,执行updatePackagesIfNeeded,完成Dex优化等
    mPackageManagerService.updatePackagesIfNeeded();
    //updatePackagesIfNeeded
    @Override
        public void updatePackagesIfNeeded() {
            enforceSystemOrRoot("Only the system can request package update");
    
            // We need to re-extract after an OTA.
            boolean causeUpgrade = isDeviceUpgrading();
    
            // First boot or factory reset.
            // Note: we also handle devices that are upgrading to N right now as if it is their
            //       first boot, as they do not have profile data.
            boolean causeFirstBoot = isFirstBoot() || mIsPreNUpgrade;
    
            // We need to re-extract after a pruned cache, as AoT-ed files will be out of date.
            boolean causePrunedCache = VMRuntime.didPruneDalvikCache();
    
            if (!causeUpgrade && !causeFirstBoot && !causePrunedCache) {
                return;
            }
    
            List<PackageParser.Package> pkgs;
            synchronized (mPackages) {
                pkgs = PackageManagerServiceUtils.getPackagesForDexopt(mPackages.values(), this);
            }
    
            final long startTime = System.nanoTime();
            final int[] stats = performDexOptUpgrade(pkgs, mIsPreNUpgrade /* showDialog */,
                        causeFirstBoot ? REASON_FIRST_BOOT : REASON_BOOT,
                        false /* bootComplete */);
    
            final int elapsedTimeSeconds =
                    (int) TimeUnit.NANOSECONDS.toSeconds(System.nanoTime() - startTime);
    
            MetricsLogger.histogram(mContext, "opt_dialog_num_dexopted", stats[0]);
            MetricsLogger.histogram(mContext, "opt_dialog_num_skipped", stats[1]);
            MetricsLogger.histogram(mContext, "opt_dialog_num_failed", stats[2]);
            MetricsLogger.histogram(mContext, "opt_dialog_num_total", getOptimizablePackages().size());
            MetricsLogger.histogram(mContext, "opt_dialog_time_s", elapsedTimeSeconds);
        }
    复制代码
    
    • 第六步:执行performFstrimIfNeeded,完成磁盘维护
    mPackageManagerService.performFstrimIfNeeded();
    @Override
        public void performFstrimIfNeeded() {
            enforceSystemOrRoot("Only the system can request fstrim");
    
            // Before everything else, see whether we need to fstrim.
            try {
                IStorageManager sm = PackageHelper.getStorageManager();
                if (sm != null) {
                    boolean doTrim = false;
                    final long interval = Global.getLong(
                            mContext.getContentResolver(),
                            Global.FSTRIM_MANDATORY_INTERVAL,
                            DEFAULT_MANDATORY_FSTRIM_INTERVAL);
                    if (interval > 0) {
                        final long timeSinceLast = System.currentTimeMillis() - sm.lastMaintenance();
                        if (timeSinceLast > interval) {
                            doTrim = true;
                            Slog.w(TAG, "No disk maintenance in " + timeSinceLast
                                    + "; running immediately");
                        }
                    }
                    if (doTrim) {
                        final boolean dexOptDialogShown;
                        synchronized (mPackages) {
                            dexOptDialogShown = mDexOptDialogShown;
                        }
                        if (!isFirstBoot() && dexOptDialogShown) {
                            try {
                                ActivityManager.getService().showBootMessage(
                                        mContext.getResources().getString(
                                                R.string.android_upgrading_fstrim), true);
                            } catch (RemoteException e) {
                            }
                        }
                        sm.runMaintenance();
                    }
                } else {
                    Slog.e(TAG, "storageManager service unavailable!");
                }
            } catch (RemoteException e) {
                // Can't happen; StorageManagerService is local
            }
        }
    
    复制代码
    
    • 第七步:调用systemReady,PKMS准备就绪
    mPackageManagerService.systemReady();
    复制代码
    
    PKMS构造方法
    image.png

    APK的扫描

    PKMS的构造函数中调用了scanDirTracedLI方法来扫描某个目录的APK文件,Android10.0版本和其他低版本的扫描的路径是不一样的,PKMS主要扫描以下路径的APK信息,以下都属于系统app类别

    /vendor/overlay
    /product/overlay
    /product_services/overlay
    /odm/overlay
    /oem/overlay
    /system/framework
    /system/priv-app
    /system/app
    /vendor/priv-app
    /vendor/app
    /odm/priv-app
    /odm/app
    /oem/app
    /oem/priv-app
    /product/priv-app
    /product/app
    /product_services/priv-app
    /product_services/app
    /product_services/priv-app
    复制代码
    
    APK扫描流程图
    image.png

    在该流程中,可以看到AndroidManifest文件的解析后的所有信息存储在Package对象中。

    APK扫描流程总结
    • 第一步:扫描APK,解析AndroidManifest.xml文件,得到清单文件各个标签内容
    • 第二步:解析清单文件到的信息由 Package 保存。从该类的成员变量可看出,和 Android四大组件相关的信息分别由 activites、receivers、providers、services 保存,由于一个 APK 可声明多个组件,因此 activites 和 receivers等均声明为 ArrayList 。

    APK的安装

    APK的安装流程图(三大步骤)
    image.png
    APK安装时序图
    image.png
    具体步骤分析

    点击一个apk后,会弹出安装界面,点击确定按钮后,会进入PackageInstallerActivitybindUi() 中的mAlert点击事件, 弹出的安装界面底部显示的是一个diaglog,主要由bindUi构成,上面有 ”取消“ 和 ”安装“ 两个按钮,点击安装后 调用startInstall()进行安装:

    private void bindUi() {
        mAlert.setIcon(mAppSnippet.icon);
        mAlert.setTitle(mAppSnippet.label);
        mAlert.setView(R.layout.install_content_view);
        mAlert.setButton(DialogInterface.BUTTON_POSITIVE,
    getString(R.string.install),
                (ignored, ignored2) -> {
                    if (mOk.isEnabled()) {
                        if (mSessionId != -1) {
    startInstall 做的事情
        mInstaller.setPermissionsResult(mSessionId, true);
        finish();
    } else {
    startInstall(); // 进行APK安装 下面开始分析 }
    }
    }, null);
        mAlert.setButton(DialogInterface.BUTTON_NEGATIVE,
    getString(R.string.cancel),
                (ignored, ignored2) -> {
                    // Cancel and finish
                    setResult(RESULT_CANCELED);
                    if (mSessionId != -1) {
    //如果mSessionId存在,执行setPermissionsResult()完成取消安装
                        mInstaller.setPermissionsResult(mSessionId, false);
                    }
                    finish();
                }, null);
        setupAlert();
        mOk = mAlert.getButton(DialogInterface.BUTTON_POSITIVE);
        mOk.setEnabled(false);
    }
    复制代码
    

    startInstall方法组装了一个Intent,并跳转到 InstallInstalling 这个Activity,并关闭掉当前的PackageInstallerActivity。InstallInstalling主要用于向包管理器发送包的信息并处理包管理的回调:

    private void startInstall() {
        // Start subactivity to actually install the application
        Intent newIntent = new Intent();
        newIntent.putExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO,
                mPkgInfo.applicationInfo);
        newIntent.setData(mPackageURI);
    // 设置Intent中的class为 InstallInstalling,用来进行Activity跳转
    // class InstallInstalling extends AlertActivity 下面会分析 InstallInstalling Activity
        newIntent.setClass(this, InstallInstalling.class);
        String installerPackageName = getIntent().getStringExtra(
    Intent.EXTRA_INSTALLER_PACKAGE_NAME);
        if (mOriginatingURI != null) {
            newIntent.putExtra(Intent.EXTRA_ORIGINATING_URI, mOriginatingURI);
        }
        if (mReferrerURI != null) {
            newIntent.putExtra(Intent.EXTRA_REFERRER, mReferrerURI);
        }
        if (mOriginatingUid != PackageInstaller.SessionParams.UID_UNKNOWN) {
            newIntent.putExtra(Intent.EXTRA_ORIGINATING_UID, mOriginatingUid);
        }
        if (installerPackageName != null) {
            newIntent.putExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME,
                    installerPackageName);
        }
        if (getIntent().getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false)) {
            newIntent.putExtra(Intent.EXTRA_RETURN_RESULT, true);
        }
        newIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
        if(localLOGV) Log.i(TAG, "downloaded app uri="+mPackageURI);
        startActivity(newIntent);
        finish();
    }
    复制代码
    

    启动 InstallInstalling,进入onCreate, 重点是看onCreate函数中的六步:

    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 {
                getPackageManager().installExistingPackage(appInfo.packageName);
                launchSuccess();
            } catch (PackageManager.NameNotFoundException e) {
                launchFailure(PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null);
            }
    } else {
    //根据mPackageURI创建一个对应的File
    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);
    // 第一步.如果savedInstanceState不为null,获取此前保存的mSessionId和 mInstallId,其中mSessionId是安装包的会话id,mInstallId是等待的安装事件id
            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 {
    // 第二步.根据mInstallId向InstallEventReceiver注册一个观察者,
    launchFinishBasedOnResult会接收到安装事件的回调, //无论安装成功或者失败都会关闭当前的Activity(InstallInstalling)。如果
    savedInstanceState为null,代码的逻辑也是类似的 InstallEventReceiver.addObserver(this, mInstallId,
                            this::launchFinishBasedOnResult);
                } catch (EventResultPersister.OutOfIdsException e) {
                    // Does not happen
    }
    } else {
    // 第三步.创建SessionParams,它用来代表安装会话的参数,组装params
                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);
    // 第四步.根据mPackageUri对包(APK)进行轻量级的解析,并将解析的参数赋值给 SessionParams
                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
        Log.e(LOG_TAG,
                "Cannot calculate installed size " + file + ". Try only
        params.setSize(file.length());
    } catch (IOException e) {
        Log.e(LOG_TAG,
                "Cannot calculate installed size " + file + ". Try only
        params.setSize(file.length());
    apk size.");
    apk size.");
                }
    try {
    // 第五步.向InstallEventReceiver注册一个观察者返回一个新的mInstallId, //其中InstallEventReceiver继承自BroadcastReceiver,用于接收安装事件并
    回调给EventResultPersister。
    mInstallId = InstallEventReceiver
                            .addObserver(this, EventResultPersister.GENERATE_NEW_ID,
                                    this::launchFinishBasedOnResult);
                } catch (EventResultPersister.OutOfIdsException e) {
                    launchFailure(PackageManager.INSTALL_FAILED_INTERNAL_ERROR,
    null);
    }
    try {
    // 第六步.PackageInstaller的createSession方法内部会通过
    IPackageInstaller与PackageInstallerService进行进程间通信, //最终调用的是PackageInstallerService的createSession方法来创建并返回
    mSessionId
                    mSessionId =
    getPackageManager().getPackageInstaller().createSession(params);
                } catch (IOException e) {
                    launchFailure(PackageManager.INSTALL_FAILED_INTERNAL_ERROR,
    null);
    }
    }
            mCancelButton = mAlert.getButton(DialogInterface.BUTTON_NEGATIVE);
            mSessionCallback = new InstallSessionCallback();
        }
    }
    复制代码
    

    以上第六步是重点 PackageInstaller 的 createSession()内部会通过IPackageInstaller与 PackageInstallerService进行进程间通信,最终调用的是PackageInstallerService的createSession方法 来创建并返回mSessionId。

    InstallInstalling.onResume方法中,调用onPostExecute()方法,将APK的信息通过IO流的形式写入到PackageInstaller.Session中

    protected void onResume() {
        super.onResume();
        // This is the first onResume in a single life of the activity
    if (mInstallingTask == null) {
    PackageInstaller installer = getPackageManager().getPackageInstaller(); // 获取sessionInfo
    PackageInstaller.SessionInfo sessionInfo =
    installer.getSessionInfo(mSessionId);
    if (sessionInfo != null && !sessionInfo.isActive()) {
    // 最终执行onPostExecute() 下面来分析
    // 创建内部类InstallingAsyncTask的对象,调用execute(),最终进入
    onPostExecute()
    } }
        mInstallingTask = new InstallingAsyncTask();
        mInstallingTask.execute();
    } else {
        // we will receive a broadcast when the install is finished
        mCancelButton.setEnabled(false);
        setFinishOnTouchOutside(false);
    }    
    复制代码
    

    Installinstalling.InstallingAsyncTask : 关注第一步和第二步

    private final class InstallingAsyncTask extends AsyncTask<Void, Void,
    PackageInstaller.Session> {
        volatile boolean isDone;
    // 第一步: doInBackground()会根据包(APK)的Uri,将APK的信息通过IO流的形式写入到 PackageInstaller.Session中
    @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; }
    //将APK的信息通过IO流的形式写入到PackageInstaller.Session中 out.write(buffer, 0, numRead);
    if (sizeBytes > 0) {
                        float fraction = ((float) numRead / (float)
                        session.addProgress(fraction);
    } }
        return session;
    } catch (IOException | SecurityException e) {
        Log.e(LOG_TAG, "Could not write package", e);
        session.close();
        return null;
    } finally {
    sizeBytes);
    } }
    synchronized (this) {
        isDone = true;
        notifyAll();
    }
    // 第二步:最后在onPostExecute()中 调用PackageInstaller.Session的commit方法,进行 安装
    @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);
    // commit 下面会分析
    // 调用PackageInstaller.Session的commit方法,进行安装 session.commit(pendingIntent.getIntentSender()); mCancelButton.setEnabled(false); setFinishOnTouchOutside(false);
            } else {
     getPackageManager().getPackageInstaller().abandonSession(mSessionId);
                if (!isCancelled()) {
                    launchFailure(PackageManager.INSTALL_FAILED_INVALID_APK, null);
                    }
                }
            }
      }
    复制代码
    

    PackageInstaller的commit()

    [PackageInstaller.java] commit
    public void commit(@NonNull IntentSender statusReceiver) {
    try {
    // mSession的类型为IPackageInstallerSession,这说明要通过
    IPackageInstallerSession来进行进程间的通信,最终会调用PackageInstallerSession的commit 方法,这样代码逻辑就到了Java框架层的。
    // 调用IPackageInstallerSession的commit方法, 跨进程调用到 PackageInstallerSession.commit()
            mSession.commit(statusReceiver, false);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }
    
    public void commit(@NonNull IntentSender statusReceiver, boolean forTransfer) {
        if (mIsPerfLockAcquired && mPerfBoostInstall != null) {
            mPerfBoostInstall.perfLockRelease();
            mIsPerfLockAcquired = false;
    }
    ...
    // 调用markAsCommitted()
    if (!markAsCommitted(statusReceiver, forTransfer)) {
    return; }
    ...
    // 向Handler发送一个类型为MSG_COMMIT的消息 ,下面会分析
        mHandler.obtainMessage(MSG_COMMIT).sendToTarget();
    }
    复制代码
    

    MSG_COMMIT在handler中进行处理,进入handleCommit()

    public boolean handleMessage(Message msg) {
        switch (msg.what) {
    } }
    case MSG_COMMIT:
        handleCommit();
    break;
    private void handleCommit() {
    List<PackageInstallerSession> childSessions = getChildSessions();
        try {
            synchronized (mLock) {
    //最终调用installStage(),进入PKMS
                commitNonStagedLocked(childSessions);
            }
        } catch (PackageManagerException e) {
            final String completeMsg = ExceptionUtils.getCompleteMessage(e);
            Slog.e(TAG, "Commit of session " + sessionId + " failed: " +
    completeMsg);
            destroyInternal();
            dispatchSessionFinished(e.error, completeMsg, null);
        }
    }
    复制代码
    

    最终调用 mPm.installStage(),进入PKMS 【经过千辛万苦,终于要进入PKMS了】

    private void commitNonStagedLocked(...)throws PackageManagerException {
        if (isMultiPackage()) {
    ...
    mPm.installStage(activeChildSessions); // 【同学们注意】跨越进程 进入 PKMS.installStage了
        } else {
            mPm.installStage(committingSession);
    } }
    复制代码
    

    PKMS.installStage

    [PackageManagerService.java]
    void installStage(ActiveInstallSession activeInstallSession) {
        if (DEBUG_INSTANT) {
            if ((activeInstallSession.getSessionParams().installFlags
                    & PackageManager.INSTALL_INSTANT_APP) != 0) {
                Slog.d(TAG, "Ephemeral install of " +
    activeInstallSession.getPackageName());
            }
    }
    // 第一步.创建了类型为INIT_COPY的消息
    final Message msg = mHandler.obtainMessage(INIT_COPY);
    // 第二步.创建InstallParams,它对应于包的安装数据
    final InstallParams params = new InstallParams(activeInstallSession);
     params.setTraceMethod("installStage").setTraceCookie(System.identityHashCode(pa
    rams));
        msg.obj = params;
        Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "installStage",
                System.identityHashCode(msg.obj));
        Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "queueInstall",
                System.identityHashCode(msg.obj));
    // 第三步.将InstallParams通过消息发送出去。
        mHandler.sendMessage(msg);
    }
    对INIT_COPY的消息的处理 [PackageManagerService.java]
    void doHandleMessage(Message msg) {
        switch (msg.what) {
            case INIT_COPY: {
    } }
        HandlerParams params = (HandlerParams) msg.obj;
        if (params != null) {
            if (DEBUG_INSTALL) Slog.i(TAG, "init_copy: " + params);
            Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "queueInstall",
    System.identityHashCode(params)); Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "startCopy");
    // 执行APK拷贝动作,这里会执行到 final void startCopy() params.startCopy();
    Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
    }
    break; }
    [PKMS.HandlerParams]
    final void startCopy() {
    if (DEBUG_INSTALL) Slog.i(TAG, "startCopy " + mUser + ": " + this); handleStartCopy();
    handleReturnCode(); // 调用到下面 handleReturnCode
    }
    [PKMS.MultiPackageInstallParams]
    void handleReturnCode() {
        if (mVerificationCompleted && mEnableRollbackCompleted) {
            .....
    if (mRet == PackageManager.INSTALL_SUCCEEDED) {
    mRet = mArgs.copyApk(); // 下面会说到 copyApk
    }
    ..... }
    }            
    复制代码
    

    APK 拷贝方法调用步骤如下:

    PKMS
        copyApk()
        doCopyApk()
    PackageManagerServiceUtils
        copyPacakge()
        copyFile()
    复制代码
    
    安装的原理

    安装其实就是把apk文件copy到对应的目录

    1. data/app/包名/,安装是吧apk文件复制到此目录,可以将文件取出并安装,和我们本身的apk是一样的,用该apk安装可能会遇到问题:用该命令 adb install -t<apk>来安装

    2. data/data/包名/,开辟存放应用程序的文件数据的文件夹,包括我们的so库,缓存文件,oat文件等。

    image.png

    注意:开机的时候,如果配置了odex优化,会生成上面的oat文件内容,后面手机重启后不会重复进行优化,除非系统升级或者恢复出厂设置等操作。

    1. 将apk中的dex文件安装到data/dalvik-cach目录下(dex文件时dalvik虚拟机的可执行文件)

    PKMS之权限扫描

    PackageManagerService中执行systemReady()以后,需要对/system/etc/permissions中各种xml进行扫描,进行相应的权限存储,供之后权限管理使用。

    PackageManagerService执行systemReady()时,通过SystemConfig的readPermissionsFromXml()来扫描读取 /system/etc/permissions中的xml文件,包括platform.xml和系统支持的各种硬件模块的 feature主要工作:

    PKMS权限扫描流程图
    image.png

    静默安装

    // 如果想实现,静默安装,就需要设置好UID,只有设置这个UID后,才有安装的权限
    // 但是这个UID必须要求有系统的[签名], 而这个系统的[签名]是属于各大手机厂商的机密,也 意味着非常的坑爹
    // 如果是系统厂商要做这种静默安装, 那就是非常容易的事情, 因为系统厂商可以轻而易举的拿 到 系统的[签名]
    mSettings.addSharedUserLPw("android.uid.system", Process.SYSTEM_UID,
            ApplicationInfo.FLAG_SYSTEM,ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
    mSettings.addSharedUserLPw("android.uid.phone", RADIO_UID,
            ApplicationInfo.FLAG_SYSTEM,ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
    mSettings.addSharedUserLPw("android.uid.log", LOG_UID,
            ApplicationInfo.FLAG_SYSTEM,ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
    mSettings.addSharedUserLPw("android.uid.nfc", NFC_UID,
            ApplicationInfo.FLAG_SYSTEM,ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
    mSettings.addSharedUserLPw("android.uid.bluetooth", BLUETOOTH_UID,
            ApplicationInfo.FLAG_SYSTEM,ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
    mSettings.addSharedUserLPw("android.uid.shell", SHELL_UID,
            ApplicationInfo.FLAG_SYSTEM,ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
    mSettings.addSharedUserLPw("android.uid.se", SE_UID,
            ApplicationInfo.FLAG_SYSTEM,ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
    mSettings.addSharedUserLPw("android.uid.networkstack", NETWORKSTACK_UID,
            ApplicationInfo.FLAG_SYSTEM,ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
    
    复制代码
    

    requestPermissions源码流程解析

    Google在 Android 6.0 开始引入了权限申请机制,将所有权限分成了正常权限危险权限(需要动态的申请并得到用户的授权才能使用)。

    核心方法简介:

    //检查权限
    checkSelfPermission(@NonNull String permission) 
    //申请权限
    requestPermissions(@NonNull String[] permissions, int requestCode)
    //处理结果回调
    onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
    @NonNull int[] grantResults)
    //是否需要显示UI界面提示用户为什么需要这个权限 
    shouldShowRequestPermissionRationale(@NonNull String permission)
    复制代码
    
    权限源码申请流程总结:
    • 第一步:MainActivity 调用 requestPermissions 进行动态权限申请;

    • 第二步:requestPermissions函数通过隐式意图,激活PackageInstaller的GrantPermissionsActivity界 面,让用户选择是否授权;

    • 第三步:经过PKMS把相关信息传递给PermissionManagerService处理;

    • 第四步:PermissionManagerService处理结束后回调给---->PKMS中的onPermissionGranted方法把处理结果返回;

    • 第五步:PKMS通知过程中权限变化,并调用writeRuntimePermissionsForUserLPr函数让PackageManager的settings记录下相关授权信息。

    面试题

    1. 为什么手机启动那么耗时

      点击开机键,固定的地方启动开机动画,启动linux驱动,然后启动init进程(用户态第一个进程),init进程启动zogyte进程,zogyte启动SystemServer,SystemServer的main函数启动各种服务,PMS中的构造函数有五个阶段耗时,五个阶段后还会进行dex优化。

    2. startActivity MainActivity跳转到LoginActivity设置android:launcMode="singleTask",这个singleTask是什么时候被解析的? PKMS提前就进行加载了,存放在Package中了,Package里面有所有apk的配置信息,不需要再解析。

    3. 静态广播是什么时候注册的?

      手机开机的时候,PKMS构造函数, PKMS扫描进来Package,再进行注册

    4. 开机的时候,不会把apk重新安装一遍,那样太恐怖了,在ota(系统升级)的时候会进行重新安装一遍。

    5. SystemServer.run()方法简要介绍

      1. startBootstrapServices() 引导服务,AMS,PKMS,电源管理等等
      2. startCoreServices() 核心服务
      3. startOtherServices() 其他服务
    6. 安装新的apk过程,何时进行配置文件的获取(扫描),PackageInstallerActivity的onCreate方法中processPackageUri,该方法只是进行权限等内容的扫描,不会全部扫描,执行安装方法之前,真正进行扫描的方法是在PKMS.installPackageLI方法

      //PackageInstallerActivity.java  界面弹出框
      protected void onCreate(Bundle icicle) {
          getWindow().addSystemFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
          super.onCreate(null);
          // 初始化安装需要用到的对象
          mPm = getPackageManager();
          mIpm = AppGlobals.getPackageManager();
          mAppOpsManager = (AppOpsManager) getSystemService(Context.APP_OPS_SERVICE);
          mInstaller = mPm.getPackageInstaller();
          mUserManager = (UserManager) getSystemService(Context.USER_SERVICE);
      
          // 根据Uri的Scheme做不同的处理,这里面进行扫描。
          boolean wasSetUp = processPackageUri(packageUri);
          if (!wasSetUp) {
              return;
          }
          // 显示安装界面
          bindUi();
          // 检查是否允许安装包,如果允许则启动安装。如果不允许显示适当的对话框
          checkIfAllowedAndInitiateInstall();
      }
      
      复制代码
      

      主要做了对象的初始化,解析 Uri 的 Scheme,初始化界面,安装包检查等等工作,接着查看一下 processPackageUri 方法,这里只是获取一些权限,名字,icon等等,没有进行全部解析AndroidManifest.xml文件,各个厂商可以自己定制。

      真正进行全部扫描的方法是在PKMS.installPackageLI。

    相关文章

      网友评论

        本文标题:09 Android包管理机制-PMS的创建过程-Android

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