勿忘初心

个人签名

530篇博客

Android应用程序安装过程浅析(1)

勿忘初心2018-09-25 14:55

我们知道在android中,安装应用是由PackageManager来管理的,但是我们发现PackageManager是一个抽象类,他的installPackage方法也没有具体的实现。那在安装过程中是怎么执行的呐?

调用方

查看代码可以知道ApplicationPackageManager是直接继承自PackageManager的,所以最终代码会调用ApplicationPackageManager下的installPackage(Uri packageURI, IPackageInstallObserver observer, int flags,String installerPackageName),而在installPackage里面又调用了installCommon。

installCommon的实现如下:

 private void installCommon(Uri packageURI,
            PackageInstallObserver observer, int flags, String installerPackageName,
            VerificationParams verificationParams, ContainerEncryptionParams encryptionParams) {
        if (!"file".equals(packageURI.getScheme())) {
            throw new UnsupportedOperationException("Only file:// URIs are supported");
        }
        if (encryptionParams != null) {
            throw new UnsupportedOperationException("ContainerEncryptionParams not supported");
        }

        final String originPath = packageURI.getPath();
        try {
            mPM.installPackage(originPath, observer.getBinder(), flags, installerPackageName,
                    verificationParams, null);
        } catch (RemoteException ignored) {
        }
    }

       可以看到在installCommon最终调用了mPm.installPackage那mPm又是什么?可以发现mPM是一个IPackageManager,他在是ApplicationPackageManager的构造函数中传入的,那是什么时候调用构造函数的呐?在ContextImpl中调用getPackageManager时会进行调用,传入的pm在是ActivityThread中创建的。

    @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;
    }

ActivityThread.getPackageManager()的代码如下:

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;
    }

       从上面可以看到IPackageManager是进程间通信的客户端, 首先是IPackageManager是通过IPackageManager.aidl文件生成,同时生成了存根类IPackageManager.Stub,代理类:IPackageManager.Stub.Proxy,他是IBinder类型,那远端又是谁呐?远端就是PackageManagerService,PackageManagerService继承自IPackageManager.Stub,因此最终的调用都是通过aidl由PackageManagerService执行。因此我们主要来看看PackageManagerService中的执行过程。

安装方式

       主要有两种方式:        1:系统启动后扫描安装,会调用PackageManagerService的scanPackageLI函数,        2:应用市场安装,应用市场下载后会默认调用PackageManagerService的intallPackage函数,改函数最终也会调用到scanPackageLI,因此只需要分享第二种

流程图

       我们可以大致看看代码调用的过程,流程图如下:

执行过程

       由于PackageManager最终是由PackageManagerService来执行的

 @Override
public void installPackage(String originPath, IPackageInstallObserver2 observer,
        int installFlags, String installerPackageName, VerificationParams verificationParams,
        String packageAbiOverride) {
    installPackageAsUser(originPath, observer, installFlags, installerPackageName,
            verificationParams, packageAbiOverride, UserHandle.getCallingUserId());
}

       主要传递了6个参数:

1,originPath,安装包的位置,他必须是file类型活在content的URI类型。传递这里的是一个string类型。

2,observer,是一个IPackageInstallObserver类型,回调通知调用者安装完成

3,installFlags,他的值是INSTALL_FORWARD_LOCK,INSTALL_REPLACE_EXISTING,INSTALL_ALLOW_TEST三个中的一个,INSTALL_FORWARD_LOCK表示安装过程中是否锁定,INSTALL_REPLACE_EXISTING表示是否替换安装包,INSTALL_ALLOW_TEST是否测试安装包,如果有改标志,manifest必须配置android:testOnly

4,installerPackageName,安装包包名

5,verificationParams,代表验证参数用于验证包安装。

6,packageAbiOverride,一般传null

     该函数调用了installPackageAsUser函数,installPackageAsUser函数如下:

@Override
public void installPackageAsUser(String originPath, IPackageInstallObserver2 observer,
        int installFlags, String installerPackageName, VerificationParams verificationParams,
        String packageAbiOverride, int userId) {
    mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES, null);

    final int callingUid = Binder.getCallingUid();
    enforceCrossUserPermission(callingUid, userId, true, true, "installPackageAsUser");

    if (isUserRestricted(userId, UserManager.DISALLOW_INSTALL_APPS)) {
        try {
            if (observer != null) {
                observer.onPackageInstalled("", INSTALL_FAILED_USER_RESTRICTED, null, null);
            }
        } catch (RemoteException re) {
        }
        return;
    }

    if ((callingUid == Process.SHELL_UID) || (callingUid == Process.ROOT_UID)) {
        installFlags |= PackageManager.INSTALL_FROM_ADB;

    } else {
        // Caller holds INSTALL_PACKAGES permission, so we're less strict
        // about installerPackageName.

        installFlags &= ~PackageManager.INSTALL_FROM_ADB;
        installFlags &= ~PackageManager.INSTALL_ALL_USERS;
    }

    UserHandle user;
    if ((installFlags & PackageManager.INSTALL_ALL_USERS) != 0) {
        user = UserHandle.ALL;
    } else {
        user = new UserHandle(userId);
    }

    // Only system components can circumvent runtime permissions when installing.
    if ((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");
    }

    verificationParams.setInstallerUid(callingUid);

    final File originFile = new File(originPath);
    final OriginInfo origin = OriginInfo.fromUntrustedFile(originFile);

    final Message msg = mHandler.obtainMessage(INIT_COPY);
    msg.obj = new InstallParams(origin, null, observer, installFlags, installerPackageName,
            null, verificationParams, user, packageAbiOverride, null);
    mHandler.sendMessage(msg);
}

       该函数主要做了以下操作,第一强制获取权限,如果被拒绝则退出执行,接着设置installFlags参数,最后一步发送了一个what为INIT_COPY的message,参数为InstallParams,记住该参数,后面还会多次用到,看what的名称INIT_COPY,看起来是表达初始化并且拷贝,那是不是真是这样呐?我们去看看这个操作,这个操做是PackageHandler来执行的,PackageHandler继续字Handler,那我们来具体看看执行代码:

case INIT_COPY: {
    HandlerParams params = (HandlerParams) msg.obj;
    int idx = mPendingInstalls.size();
    if (DEBUG_INSTALL) Slog.i(TAG, "init_copy idx=" + idx + ": " + params);
    // If a bind was already initiated we dont really
    // need to do anything. The pending install
    // will be processed later on.
    if (!mBound) {
        // If this is the only one pending we might
        // have to bind to the service again.
        if (!connectToService()) {
            Slog.e(TAG, "Failed to bind to media container service");
            params.serviceError();
            return;
        } else {
            // Once we bind to the service, the first
            // pending request will be processed.
            mPendingInstalls.add(idx, params);
        }
    } else {
        mPendingInstalls.add(idx, params);
        // Already bound to the service. Just make
        // sure we trigger off processing the first request.
        if (idx == 0) {
            mHandler.sendEmptyMessage(MCS_BOUND);
        }
    }
    break;
}

       可以看到首先取出参数params,这个params就是之前传入的InstallParams,接着获取等待安装队列的内容个数,由于初始mBound为false,因此会进入该判断,之后执行了connectToService函数,如个返回false表示连接失败,直接行使params的serviceError函数来结束当前执行,如果为这将paramsa添加到mPendingInstalls的最后一个位置,connectToService连接又是什么呐?当前代码也没有执行任何与copy有段的操作啊?那我们去看看connectToService究竟干了什么?

private boolean connectToService() {
    if (DEBUG_SD_INSTALL) Log.i(TAG, "Trying to bind to" +
            " DefaultContainerService");
    Intent service = new Intent().setComponent(DEFAULT_CONTAINER_COMPONENT);
    Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
    if (mContext.bindServiceAsUser(service, mDefContainerConn,
            Context.BIND_AUTO_CREATE, UserHandle.OWNER)) {
        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
        mBound = true;
        return true;
    }
    Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
    return false;
}

       可以看到这里bind到了一个service,service的名称是DefaultContainerService,这又是个什么service呐?并且在绑定之前先设置该进程的优先级为THREAD_PRIORITY_DEFAULT,执行完成后再次设置为THREAD_PRIORITY_BACKGROUND,这里我们也没有看到有任何copy的操作,那copy操作究竟在什么地方,绑定的这个服务又是什么?我们来看看绑定的connection参数:

class DefaultContainerConnection implements ServiceConnection {
    public void onServiceConnected(ComponentName name, IBinder service) {
        if (DEBUG_SD_INSTALL) Log.i(TAG, "onServiceConnected");
        IMediaContainerService imcs =
            IMediaContainerService.Stub.asInterface(service);
        mHandler.sendMessage(mHandler.obtainMessage(MCS_BOUND, imcs));
    }

    public void onServiceDisconnected(ComponentName name) {
        if (DEBUG_SD_INSTALL) Log.i(TAG, "onServiceDisconnected");
    }
}

       可以看到当绑定成功后将一个service转换成了一个IMediaContainerService,这个又是什么呐?这个就是在onServiceConnected回调函数中根据参数传进来的IMediaContainerService.Stub的对象引用创建一个远程代理对象。以后PackageManagerService服务通过该代理对象访问DefaultContainerService服务。DefaultContainerService是一个应用服务,具体负责实现APK等相关资源文件在内部或外部存储器上的存储工作,DefaultContainerService服务中提供了一个IMediaContainerService.Stub桩对象。

       接下来我们看到这里又发送了一个what为MCS_BOUND的message,参数为之前获得的IMediaContainerService,这里也没有任何copy操作,那我们继续跟进看看该what执行了什么?

case MCS_BOUND: {
    if (DEBUG_INSTALL) Slog.i(TAG, "mcs_bound");
    if (msg.obj != null) {
        mContainerService = (IMediaContainerService) msg.obj;
    }
    if (mContainerService == null) {
        if (!mBound) {
            // Something seriously wrong since we are not bound and we are not
            // waiting for connection. Bail out.
            Slog.e(TAG, "Cannot bind to media container service");
            for (HandlerParams params : mPendingInstalls) {
                // Indicate service bind error
                params.serviceError();
            }
            mPendingInstalls.clear();
        } else {
            Slog.w(TAG, "Waiting to connect to media container service");
        }
    } else if (mPendingInstalls.size() > 0) {
        HandlerParams params = mPendingInstalls.get(0);
        if (params != null) {
            if (params.startCopy()) {
                // We are done...  look for more work or to
                // go idle.
                if (DEBUG_SD_INSTALL) Log.i(TAG,
                        "Checking for more work or unbind...");
                // Delete pending install
                if (mPendingInstalls.size() > 0) {
                    mPendingInstalls.remove(0);
                }
                if (mPendingInstalls.size() == 0) {
                    if (mBound) {
                        if (DEBUG_SD_INSTALL) Log.i(TAG,
                                "Posting delayed MCS_UNBIND");
                        removeMessages(MCS_UNBIND);
                        Message ubmsg = obtainMessage(MCS_UNBIND);
                        // Unbind after a little delay, to avoid
                        // continual thrashing.
                        sendMessageDelayed(ubmsg, 10000);
                    }
                } else {
                    // There are more pending requests in queue.
                    // Just post MCS_BOUND message to trigger processing
                    // of next pending install.
                    if (DEBUG_SD_INSTALL) Log.i(TAG,
                            "Posting MCS_BOUND for next work");
                    mHandler.sendEmptyMessage(MCS_BOUND);
                }
            }
        }
    } else {
        // Should never happen ideally.
        Slog.w(TAG, "Empty queue");
    }
    break;
}

       可以看到这里首先获取了传入的参数,如果参数为空,则调用HandlerParams的serviceError,并且清空mPendingInstalls列表,否则获取到等待安装列表中的第一个对象,就是我们最初始添加进的InstallParams,这里我们看到调用了InstallParams的startCopy函数,执行完成后移除该参数,如果等待安装列表为空且当前绑定状态为true,则发一个what为MCS_UNBIND的解绑操作,否则就继续执行该操作,将等待列表中的一个一个执行,MCS_UNBIND与MCS_RECONNECT,这就不详细说了,MCS_UNBIND主要是解绑之前的链接,MCS_RECONNECT是重新绑定链接,那我们继续看看startCopy函数:

final boolean startCopy() {
boolean res;
try {
if (DEBUG_INSTALL) Slog.i(TAG, "startCopy " + mUser + ": " + this);

if (++mRetries > MAX_RETRIES) {
Slog.w(TAG, "Failed to invoke remote methods on default container service. Giving up");
mHandler.sendEmptyMessage(MCS_GIVE_UP);
handleServiceError();
return false;
} else {
handleStartCopy();
res = true;
}
} catch (RemoteException e) {
if (DEBUG_INSTALL) Slog.i(TAG, "Posting install MCS_RECONNECT");
mHandler.sendEmptyMessage(MCS_RECONNECT);
res = false;
}
handleReturnCode();
return res;
}



网易云免费体验馆,0成本体验20+款云产品! 

更多网易研发、产品、运营经验分享请访问网易云社区