PMS启动 APK 安装流程详解

Posted xyTianZhao

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了PMS启动 APK 安装流程详解相关的知识,希望对你有一定的参考价值。

文章目录

概述

PackageManagerService(以下简称 PMS)是一个常用的系统服务,主要负责系统中的 Package 的管理,应用程序的安装、卸载、查询等相关功能。其相关类图如下

相关类说明

  • IPackageManager
    定义了服务端和客户端通信的业务函数,还定义了内部类 Stub ,该类从 Binder 派生并实现了 IPackageManager 接口
  • PackageManagerService
    继承自 IPackageManager.Stub 类,由于 Stub 类从 Binder 派生,因此 PackageManagerService 将作为服务端参与 Binder 通信
  • Stub
    定义了一个内部类 Proxy ,该类有一个 IBinder 类型(实际类型为 BinderProxy )的成员变量 mRemote ,根据 Binder 详解 中介绍的 Binder 系统的知识,mRemote 用于和服务端 PackageManagerService 通信
  • ApplicationPackageManager
    承自 PackageManager 类。它并没有直接参与 Binder 通信,而是通过 mPM 成员变量指向一个 IPackageManager.Stub.Proxy 类型的对象。

注:IPackageManager 在 android Studio 中可能找不到,因为他是一个 AIDL 处理后的文件,这里附上相关的 IPackageManager.aidl 文件

PMS 服务启动

分析 PMS 之前我们先来看下这玩意是在哪里启动的。

我们都知道系统启动的时候会调用 SystemServer 的 main 函数。

注释已经说明了,这个 main 函数是通过 zygote 调用的,这块的调用链涉及到了进程启动相关,此处先不多说。看到 main 创建了一个自己然后调用 run 方法。

可以看到在 run 方法中系统将服务类型按照级别分为了三大类,引导服务内核服务其他服务,而 PMS 的启动就在引导服务中启动。

/**
 * The main entry point from zygote.
 */
public static void main(String[] args) 
    new SystemServer().run();

private void run() 
    // Start services.
    try 
        //系统引导服务
        startBootstrapServices();
        //内核服务
        startCoreServices();
        //其他服务
        startOtherServices();
     catch (Throwable ex) 
        Slog.e("System", "******************************************");
        Slog.e("System", "************ Failure starting system services", ex);
        throw ex;
     finally 
        traceEnd();
    

private void startBootstrapServices() 
    mPackageManagerService = PackageManagerService.main(mSystemContext, installer,
                mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore);
    mFirstBoot = mPackageManagerService.isFirstBoot();
    //标注①
    mPackageManager = mSystemContext.getPackageManager();

我们进入 PackageManagerService 的 main 方法继续追踪。

可以看到在 main 方法中也是 new 了一把自己。然后将服务添加的 ServiceManager 中进行管理。到这,PMS 服务就启动完成了。

public class PackageManagerService extends IPackageManager.Stub
        implements PackageSender 
    public static PackageManagerService main(Context context, Installer installer,
            boolean factoryTest, boolean onlyCore) 
        // Self-check for initial settings.
        PackageManagerServiceCompilerMapping.checkProperties();

        PackageManagerService m = new PackageManagerService(context, installer,
                factoryTest, onlyCore);
        m.enableSystemUserPackages();
        ServiceManager.addService("package", m);
        final PackageManagerNative pmn = m.new PackageManagerNative();
        ServiceManager.addService("package_native", pmn);
        return m;
    

这里在额外看一下 PackageManager 的创建。上面 标注① 出获取了 PackageManager ,我们就以此为入口看下。mSystemContext 为 Content 的一个实现类,可以直接搜 ContextImpl

可以看到和上面的类图完美的对应上了,这里拿到系统提供的 PM,然后创建一个 ApplicationPackageManager 持有这个 pm 去和系统服务进行交互。

class ContextImpl extends Context 
    @Override
    public PackageManager getPackageManager() 
        if (mPackageManager != null) 
            return mPackageManager;
        

        IPackageManager pm = ActivityThread.getPackageManager();
        if (pm != null) 
            // Doesn't matter if we make more than one instance.
            return (mPackageManager = new ApplicationPackageManager(this, pm));
        

        return null;
    

注:Android 系统启动慢的原因就是在启动 PMS 的时候,需要执行扫描文件夹、处理权限、安装系统应用(文件的解压与copy)等比较耗时的操作。

应用程序(APK)安装

有界面安装

我们下载的安装包都是一个 .apk 文件,当我们点击该文件的时候,会启动系统的一个安装 apk 的应用,该应用是系统应用,在系统启动时就已经安装成功了。这里附上 PackageInstaller.apk 源码

首先这个 Intent 会启动 InstallStart ,在这里做一些校验后就跳转到 PackageInstallerActivity 进行确认安装后,进入 InstallInstalling 界面进行安装。

可以看到在 AndroidManifest.xml 中对外暴露了 InstallStart。

<activity android:name=".InstallStart"
        android:exported="true"
        android:excludeFromRecents="true">
    <intent-filter android:priority="1">
        <action android:name="android.intent.action.VIEW" />
        <action android:name="android.intent.action.INSTALL_PACKAGE" />
        <category android:name="android.intent.category.DEFAULT" />
        <data android:scheme="file" />
        <data android:scheme="content" />
        <data android:mimeType="application/vnd.android.package-archive" />
    </intent-filter>
    <intent-filter android:priority="1">
        <action android:name="android.intent.action.INSTALL_PACKAGE" />
        <category android:name="android.intent.category.DEFAULT" />
        <data android:scheme="file" />
        <data android:scheme="package" />
        <data android:scheme="content" />
    </intent-filter>
    <intent-filter android:priority="1">
        <action android:name="android.content.pm.action.CONFIRM_PERMISSIONS" />
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
</activity>

安装界面调用流程

public class InstallStart extends Activity 
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        ......
        Intent nextActivity = new Intent(intent);
        ......
        nextActivity.setClass(this, PackageInstallerActivity.class);
        ......
        if (nextActivity != null) 
            startActivity(nextActivity);
        
        finish();
    


public class PackageInstallerActivity extends OverlayTouchActivity implements OnClickListener 
    public void onClick(View v) 
        if (v == mOk) 
            if (mOk.isEnabled()) 
                if (mOkCanInstall || mScrollView == null) 
                    if (mSessionId != -1) 
                        mInstaller.setPermissionsResult(mSessionId, true);
                        finish();
                     else 
                        //确认安装
                        startInstall();
                    
                 else 
                    mScrollView.pageScroll(View.FOCUS_DOWN);
                
            
         else if (v == mCancel) 
            // Cancel and finish
            setResult(RESULT_CANCELED);
            if (mSessionId != -1) 
                mInstaller.setPermissionsResult(mSessionId, false);
            
            finish();
        
    
    private void startInstall() 
        // Start subactivity to actually install the application
        Intent newIntent = new Intent();
        ......
        newIntent.setClass(this, InstallInstalling.class);
        ......
        startActivity(newIntent);
        finish();
    

public class InstallInstalling extends Activity 
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) 
        PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
                        PackageInstaller.SessionParams.MODE_FULL_INSTALL);
        params.installFlags = PackageManager.INSTALL_FULL_APP;
        ......
        try 
            //标注②
            mSessionId = getPackageManager().getPackageInstaller().createSession(params);
         catch (IOException e) 
            launchFailure(PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null);
        
    

上面标注②中获取了 PackageInstaller 执行真正的安装

public class ApplicationPackageManager extends PackageManager 
    @Override
    public PackageInstaller getPackageInstaller() 
        synchronized (mLock) 
            if (mInstaller == null) 
                try 
                    mInstaller = new PackageInstaller(mPM.getPackageInstaller(),
                            mContext.getPackageName(), mContext.getUserId());
                 catch (RemoteException e) 
                    throw e.rethrowFromSystemServer();
                
            
            return mInstaller;
        
    

public class PackageInstaller 

    //标注③
    private final IPackageInstaller mInstaller;

    public int createSession(@NonNull SessionParams params) throws IOException 
        try 
            final String installerPackage;
            if (params.installerPackageName == null) 
                installerPackage = mInstallerPackageName;
             else 
                installerPackage = params.installerPackageName;
            

            return mInstaller.createSession(params, installerPackage, mUserId);
         catch (RuntimeException e) 
            ExceptionUtils.maybeUnwrapIOException(e);
            throw e;
         catch (RemoteException e) 
            throw e.rethrowFromSystemServer();
        
    

上面标注③ 中的 IPackageInstaller 为 PackageInstallerService 的 Binder 对象,看到这里是不发现,系统服务将 Binder 通信用的是淋漓尽致啊。

这里安装完成后通过 Binder 和 Handler 进行回调。

public class PackageInstallerService extends IPackageInstaller.Stub 
    @Override
    public int createSession(SessionParams params, String installerPackageName, int userId) 
        try 
            return createSessionInternal(params, installerPackageName, userId);
         catch (IOException e) 
            throw ExceptionUtils.wrap(e);
        
    
    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;
            
        

        // 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");
        

        if ((params.installFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0
                || (params.installFlags & PackageManager.INSTALL_EXTERNAL) != 0) 
            throw new IllegalArgumentException(
                    "New installs into ASEC containers no longer supported");
        

        // Defensively resize giant app icons
        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.
        if ((params.installFlags & PackageManager.INSTALL_INTERNAL) != 0) 
            if (!PackageHelper.fitsOnInternal(mContext, params)) 
                throw new IOException("No suitable internal storage available");
            

         else if ((params.installFlags & PackageManager.INSTALL_EXTERNAL) != 0) 
            if (!PackageHelper.fitsOnExternal(mContext, params)) 
                throw new IOException("No suitable external 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.
            params.setInstallFlagsInternal();

         else 
            // For now, installs to adopted media are treated as internal from
            // an install flag point-of-view.
            params.setInstallFlagsInternal();

            // Resolve best location for install, based on combination of
            // requested install flags, delta size, and manifest settings.
            final long ident = Binder.clearCallingIdentity();
            try以上是关于PMS启动 APK 安装流程详解的主要内容,如果未能解决你的问题,请参考以下文章

Launcher启动过程详解

Launcher启动过程详解

从APK安装过程来认识PMS

从APK安装过程来认识PMS

从APK安装过程来认识PMS

Android APK安装流程(4)--APK加载