android系统apk安装流程

从源码角度解析Android中APK的安装过程
1. Android中APK简介
Android应用Apk的安装有如下四种方式:

1.1 系统应用安装

没有安装界面,在开机时自动完成
1
1.2 网络下载应用安装

没有安装界面,在应用市场完成
1
1.3 ADB命令安装

没有安装界面,通过命令直接安装
1
1.4 外部设备安装

有安装界面,通过SD卡等外部设备安装,由packageInstaller处理安装逻辑
1
2. APK安装涉及到的几个常用目录
system/app : 系统自带的应用程序,获得root权限才能删除

data/app : 用户程序安装目录,安装时会把apk文件复制到此目录下

data/data : 存放应用程序的数据

data/dalvik-cache : 将apk中的dex文件安装到该目录下(dex文件是dalvik虚拟机的可执行文件,大小约为原始apk的四分之一)

3. APK安装的四大步骤
(1)拷贝apk到指定的目录

默认情况下,用户安装的apk首先会拷贝到/data/app下,用户有访问/data/app目录的权限,但系统出厂的apk文件
会被放到/system分区下,包括/system/app,/system/vendor/app,以及/system/priv-app等。该分区需要
root权限的用户才能访问。
1
2
3
(2)加压apk、拷贝文件、创建应用的数据目录

为了加快APP的启动速度,apk在安装的时候,会首先将APP的可执行文件(dex)拷贝到/data/dalvik-cache目录
下,缓存起来。再在/data/data/目录下创建应用程序的数据目录(以应用包名命令),用来存放应用的数据库、xml
文件、cache、二进制的so动态库等。
1
2
3
(3)解析apk的AndroidManifest.xml文件

在安装apk的过程中,会解析apk的AndroidManifest.xml文件,将apk的权限、应用包名、apk的安装位置、版本、
userID等重要信息保存在/data/system/packages.xml文件中。这些操作都是在PackageManagerService中完成
的。
1
2
3
(4)显示icon图标

应用程序经过PMS中的逻辑处理后,相当于已经注册好了,如果想要在Android桌面上看到icon图标,则需要
Launcher将系统中已经安装的程序展现在桌面上。
1
2
4. APK安装的预备知识点
(1)PackageManagerService是由SystemServer启动,PMS负责应用的安装、卸载、权限检查等工作

(2)在/system/app和/data/app目录下的apk文件,PMS在启动过程中,都会扫描安装

(3)每次开机时,PMS都会在构造函数中对指定目录下的apk进行扫描,没有安装的apk就会触发安装。

(4) 本文的源码是基于Android6.0

5. 系统应用安装
在创建PackageManagerService实例时,会在PMS的构造函数中开始执行安装应用程序的逻辑。在PMS的构造函数中做了如下几点重要操作。

(1)创建Settings对象,添加shareUserId

    mSettings = new Settings(mPackages);
    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);

(2)通过PackageManagerService构造函数参数获取应用安装器Installer

mInstaller = installer;
1
(3)获取SystemConfig实例,读取“/system/etc/permissions/*.xml”资源文件

从资源文件中获取mGlobalsGids(Group-ids)、mSystemPermissions(系统权限)、mAvailableFeatures(系统支持的features)属性。

    SystemConfig systemConfig = SystemConfig.getInstance();
    mGlobalGids = systemConfig.getGlobalGids();
    mSystemPermissions = systemConfig.getSystemPermissions();
    mAvailableFeatures = systemConfig.getAvailableFeatures();

(4)创建系统消息处理线程

    mHandlerThread = new ServiceThread(TAG,Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/);
    mHandlerThread.start();
    mHandler = new PackageHandler(mHandlerThread.getLooper());
    Watchdog.getInstance().addThread(mHandler, WATCHDOG_TIMEOUT);

(5)执行com.android.server.pm.Settings中的readLPw方法,读取安装包中的信息,并解析成对应的数据结构。

    File dataDir = Environment.getDataDirectory();
    mAppDataDir = new File(dataDir, "data");
    mAppInstallDir = new File(dataDir, "app");
    mAppLib32InstallDir = new File(dataDir, "app-lib");
    mAsecInternalPath = new File(dataDir, "app-asec").getPath();
    mUserAppDataDir = new File(dataDir, "user");
    mDrmAppPrivateInstallDir = new File(dataDir, "app-private");

    sUserManager = new UserManagerService(context, this,
            mInstallLock, mPackages);

    // Propagate permission configuration in to package manager.
    ArrayMap permConfig
            = systemConfig.getPermissions();
    for (int i=0; i         SystemConfig.PermissionEntry perm = permConfig.valueAt(i);
        BasePermission bp = mSettings.mPermissions.get(perm.name);
        if (bp == null) {
            bp = new BasePermission(perm.name, "android", BasePermission.TYPE_BUILTIN);
            mSettings.mPermissions.put(perm.name, bp);
        }
        if (perm.gids != null) {
            bp.setGids(perm.gids, perm.perUser);
        }
    }

    ArrayMap libConfig = systemConfig.getSharedLibraries();
    for (int i=0; i         mSharedLibraries.put(libConfig.keyAt(i),
                new SharedLibraryEntry(libConfig.valueAt(i), null));
    }

    mFoundPolicyFile = SELinuxMMAC.readInstallPolicy();

    mRestoredSettings = mSettings.readLPw(this, sUserManager.getUsers(false),
            mSdkVersion, mOnlyCore);

其中,读取的重要文件有如下几个:

packages.xml:记录了系统中所有安装应用的信息,包括基本信息、签名和权限。

packages-backup.xml:packages.xml的备份文件

packages.list:保存普通应用的数据目录和uid等信息

packages-stopped.xml:记录系统中被强制停止运行应用的信息。系统在强制停止某个应用时,会将应用的信息记录到该文件中

packages-stopped-backup.xml:packages-stopped.xml的备份文件

这几个目录在创建Settings对象时就已经被封装成了对应的File文件。

(6)执行PMS中的scanDirLI方法扫描系统安装目录和非系统apk信息

    File vendorOverlayDir = new File(VENDOR_OVERLAY_DIR);
    scanDirLI(vendorOverlayDir, PackageParser.PARSE_IS_SYSTEM
            | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags | SCAN_TRUSTED_OVERLAY, 0);

    // Find base frameworks (resource packages without code).
    scanDirLI(frameworkDir, PackageParser.PARSE_IS_SYSTEM
            | PackageParser.PARSE_IS_SYSTEM_DIR
            | PackageParser.PARSE_IS_PRIVILEGED,
            scanFlags | SCAN_NO_DEX, 0);

    // Collected privileged system packages.
    final File privilegedAppDir = new File(Environment.getRootDirectory(), "priv-app");
    scanDirLI(privilegedAppDir, PackageParser.PARSE_IS_SYSTEM
            | PackageParser.PARSE_IS_SYSTEM_DIR
            | PackageParser.PARSE_IS_PRIVILEGED, scanFlags, 0);

    // Collect ordinary system packages.
    final File systemAppDir = new File(Environment.getRootDirectory(), "app");
    scanDirLI(systemAppDir, PackageParser.PARSE_IS_SYSTEM
            | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);

    // Collect all vendor packages.
    File vendorAppDir = new File("/vendor/app");
    try {
        vendorAppDir = vendorAppDir.getCanonicalFile();
    } catch (IOException e) {
        // failed to look up canonical path, continue with original one
    }
    scanDirLI(vendorAppDir, PackageParser.PARSE_IS_SYSTEM
            | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);

    // Collect all OEM packages.
    final File oemAppDir = new File(Environment.getOemDirectory(), "app");
    scanDirLI(oemAppDir, PackageParser.PARSE_IS_SYSTEM
            | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);

其中,系统安装目录有:

/system/framework 系统库

/system/app 默认的系统应用

/vendor/app 厂商定制的应用

非系统apk信息目录:

/data/app/

/system/priv-app/

/data/app-private/

到此为止,PMS构造函数中主要的逻辑操作就介绍完了。

继续探究扫描安装过程:

(1)深入到PackageManagerService—>scanDirLI方法中

private void scanDirLI(File dir, int parseFlags, int scanFlags, long currentTime) {
    final File[] files = dir.listFiles();
    if (ArrayUtils.isEmpty(files)) {
        Log.d(TAG, "No files in app dir " + dir);
        return;
    }

    if (DEBUG_PACKAGE_SCANNING) {
        Log.d(TAG, "Scanning app dir " + dir + " scanFlags=" + scanFlags
                + " flags=0x" + Integer.toHexString(parseFlags));
    }

    for (File file : files) {
        final boolean isPackage = (isApkFile(file) || file.isDirectory())
                && !PackageInstallerService.isStageName(file.getName());
        if (!isPackage) {
            // Ignore entries which are not packages
            continue;
        }
        try {
            scanPackageLI(file, parseFlags | PackageParser.PARSE_MUST_BE_APK,
                    scanFlags, currentTime, null);//注释1
        } catch (PackageManagerException e) {
            Slog.w(TAG, "Failed to parse " + file + ": " + e.getMessage());

            // Delete invalid userdata apps
            if ((parseFlags & PackageParser.PARSE_IS_SYSTEM) == 0 &&
                    e.error == PackageManager.INSTALL_FAILED_INVALID_APK) {
                logCriticalInfo(Log.WARN, "Deleting invalid package at " + file);
                if (file.isDirectory()) {
                    mInstaller.rmPackageDir(file.getAbsolutePath());
                } else {
                    file.delete();
                }
            }
        }
    }
}

注释1处,对于目录中的每一个文件,如果是已apk作为后缀名的,就会调用PackageManagerService—>scanPackageLI方法进行扫描解析

(2)深入到PackageManagerService—>scanPackageLI方法中

private PackageParser.Package scanPackageLI(File scanFile, int parseFlags, int scanFlags,
        long currentTime, UserHandle user) throws PackageManagerException {
    if (DEBUG_INSTALL) Slog.d(TAG, "Parsing: " + scanFile);
    parseFlags |= mDefParseFlags;
    PackageParser pp = new PackageParser();//注释1
    pp.setSeparateProcesses(mSeparateProcesses);
    pp.setOnlyCoreApps(mOnlyCore);
    pp.setDisplayMetrics(mMetrics);

    if ((scanFlags & SCAN_TRUSTED_OVERLAY) != 0) {
        parseFlags |= PackageParser.PARSE_TRUSTED_OVERLAY;
    }

    final PackageParser.Package pkg;
    try {
        pkg = pp.parsePackage(scanFile, parseFlags);//注释2
    } catch (PackageParserException e) {
        throw PackageManagerException.from(e);
    }

    ......省略......        

    // reader
    synchronized (mPackages) {//注释3
        // Look to see if we already know about this package.
        String oldName = mSettings.mRenamedPackages.get(pkg.packageName);
        if (pkg.mOriginalPackages != null && pkg.mOriginalPackages.contains(oldName)) {
            // This package has been renamed to its original name.  Let's
            // use that.
            ps = mSettings.peekPackageLPr(oldName);
        }
        // If there was no original package, see one for the real package name.
        if (ps == null) {
            ps = mSettings.peekPackageLPr(pkg.packageName);
        }
        // Check to see if this package could be hiding/updating a system
        // package.  Must look for it either under the original or real
        // package name depending on our state.
        updatedPkg = mSettings.getDisabledSystemPkgLPr(ps != null ? ps.name : pkg.packageName);
        if (DEBUG_INSTALL && updatedPkg != null) Slog.d(TAG, "updatedPkg = " + updatedPkg);
    }

    ......省略......       

    PackageParser.Package scannedPkg = scanPackageLI(pkg, parseFlags, scanFlags
            | SCAN_UPDATE_SIGNATURE, currentTime, user);//注释4

    /*
     * If the system app should be overridden by a previously installed
     * data, hide the system app now and let the /data/app scan pick it up
     * again.
     */
    if (shouldHideSystemApp) {
        synchronized (mPackages) {
            mSettings.disableSystemPackageLPw(pkg.packageName);
        }
    }

    return scannedPkg;
}

注释1处:创建一个PackageParser实例。

注释2处调用PackageParser的parsePackage函数来对apk安装包的AndroidManifest.xml文件扫描和提取证书信息,然后构建一个PackageParser.Package对象,并将其返回。

注释3将解析返回的PackageParser对象中的信息保存到PMS中。

注释4调用另一个重载的scanPackageLI方法来构建一个PackageSetting对象,这个对象保存的信息最后会通过writeLPr写入到/data/system/packages.xml文件中去。

(3)根据(2)中注释2深入到PackageParser—>parsePackage方法中

public Package parsePackage(File packageFile, int flags) throws PackageParserException {
    if (packageFile.isDirectory()) {
        return parseClusterPackage(packageFile, flags);
    } else {
        return parseMonolithicPackage(packageFile, flags);
    }
}

这里根据packageFile是否是目录分别调用parseClusterPackage和parseMonolithicPackage去解析。

其中parseClusterPackage方法如下:

private Package parseClusterPackage(File packageDir, int flags) throws PackageParserException {
    final PackageLite lite = parseClusterPackageLite(packageDir, 0);

    if (mOnlyCoreApps && !lite.coreApp) {
        throw new PackageParserException(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
                "Not a coreApp: " + packageDir);
    }

    final AssetManager assets = new AssetManager();
    try {

        ...省略...

        final File baseApk = new File(lite.baseCodePath);
        final Package pkg = parseBaseApk(baseApk, assets, flags);//注释1
        if (pkg == null) {
            throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK,
                    "Failed to parse base APK: " + baseApk);
        }

        ...省略...

        pkg.codePath = packageDir.getAbsolutePath();
        return pkg;
    } finally {
        IoUtils.closeQuietly(assets);
    }
}

parseMonolithicPackage方法如下:

public Package parseMonolithicPackage(File apkFile, int flags) throws PackageParserException {
    
    ...省略...

    final AssetManager assets = new AssetManager();
    try {
        final Package pkg = parseBaseApk(apkFile, assets, flags);//注释2
        pkg.codePath = apkFile.getAbsolutePath();
        return pkg;
    } finally {
        IoUtils.closeQuietly(assets);
    }
}

从parseClusterPackage的注释1和parseMonolithicPackage的注释2可以看出都是调用parseBaseApk去解析。

继续探究parseBaseApk方法:

 private Package parseBaseApk(File apkFile, AssetManager assets, int flags)
        throws PackageParserException {
    
    ...省略...

    try {
        res = new Resources(assets, mMetrics, null);
        assets.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                Build.VERSION.RESOURCES_SDK_INT);
        parser = assets.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);

        final String[] outError = new String[1];
        final Package pkg = parseBaseApk(res, parser, flags, outError);//注释1
        if (pkg == null) {
            throw new PackageParserException(mParseError,
                    apkPath + " (at " + parser.getPositionDescription() + "): " + outError[0]);
        }

        ...省略...

    } catch (PackageParserException e) {
        throw e;
    } catch (Exception e) {
        throw new PackageParserException(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
                "Failed to read manifest from " + apkPath, e);
    } finally {
        IoUtils.closeQuietly(parser);
    }
}

注释1处调用另一个重载的parseBaseApk方法对apk进行解析。

(4)根据(2)中注释4深入到PackageManagerService—>另一个scanPackageLI方法中

private PackageParser.Package scanPackageLI(PackageParser.Package pkg, int parseFlags,
        int scanFlags, long currentTime, UserHandle user) throws PackageManagerException {
    boolean success = false;
    try {
        final PackageParser.Package res = scanPackageDirtyLI(pkg, parseFlags, scanFlags,
                currentTime, user);//注释1
        success = true;
        return res;
    } finally {
        if (!success && (scanFlags & SCAN_DELETE_DATA_ON_FAILURES) != 0) {
            removeDataDirsLI(pkg.volumeUuid, pkg.packageName);
        }
    }
}

由注释1继续深入PackageManagerService—>scanPackageDirtyLI方法中:

private PackageParser.Package scanPackageDirtyLI(PackageParser.Package pkg, int parseFlags,
        int scanFlags, long currentTime, UserHandle user) throws PackageManagerException {
    final File scanFile = new File(pkg.codePath);

    ......省略......

    // And now re-install the app.
    ret = createDataDirsLI(pkg.volumeUuid, pkgName, pkg.applicationInfo.uid,
    HasSystemUidErrors = true;//注释1
                
    ......省略......

    //invoke installer to do the actual installation
    int ret = createDataDirsLI(pkg.volumeUuid, pkgName, pkg.applicationInfo.uid,
            pkg.applicationInfo.seinfo);//注释2
    if (ret < 0) {
        // Error from installer
        throw new PackageManagerException(INSTALL_FAILED_INSUFFICIENT_STORAGE,
                "Unable to create data dirs [errorCode=" + ret + "]");
    }

    ......省略......       
}

从注释1和注释2处看出调用了PMS中的createDataDirsLI方法,给installed发送消息,为应用程序创建对应的数据目录,如果目录已经存在,也会重新创建一遍。

深入到PMS的createDataDirsLI方法中:

private int createDataDirsLI(String volumeUuid, String packageName, int uid, String seinfo) {
    int[] users = sUserManager.getUserIds();
    int res = mInstaller.install(volumeUuid, packageName, uid, uid, seinfo);//注释1
    if (res < 0) {
        return res;
    }
    for (int user : users) {
        if (user != 0) {
            res = mInstaller.createUserData(volumeUuid, packageName,//注释2
                    UserHandle.getUid(user, uid), user, seinfo);
            if (res < 0) {
                return res;
            }
        }
    }
    return res;
}

从上面可知调用mInstaller.install()函数完成APK安装。

到此为止,系统应用的安装流程差不多就完成了。

6. 网络下载应用安装
网络应用下载完成后,会自动调用PackageManagerService中的installPackage方法:

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

可以看到主要是调用了PMS—>installPackageAsUser方法:

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 Message msg = mHandler.obtainMessage(INIT_COPY);
    msg.obj = new InstallParams(origin, null, observer, params.installFlags,
            installerPackageName, params.volumeUuid, verifParams, user, params.abiOverride,
            params.grantedRuntimePermissions);
    mHandler.sendMessage(msg);
}

主要是获取用户安装位置,将InstallParams对象封装在Message里,然后发一个Handler消息。

深入到处理INIT_COPY的地方:PMS—>doHandleMessage方法中:

void doHandleMessage(Message msg) {
        switch (msg.what) {
            case INIT_COPY: {
                
                ......省略......                    

                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;
            }
            case MCS_BOUND: {
                if (DEBUG_INSTALL) Slog.i(TAG, "mcs_bound");
                if (msg.obj != null) {
                    mContainerService = (IMediaContainerService) msg.obj;
                }
                if (mContainerService == null) {
                    
                    ......省略......
                
                } else if (mPendingInstalls.size() > 0) {
                    HandlerParams params = mPendingInstalls.get(0);
                    if (params != null) {
                        if (params.startCopy()) {//注释1
                            // 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;
            }

如果msg.what是INIT_COPY:

则连接DefaultContainerService服务,将我们要安装的信息方法HandlerParams的mPendingInstalls中,然后再发送MCS_BOUND消息。

如果msg.what是MCS_BOUND:

则通过HandlerParams params = mPendingInstalls.get(0)获取要安装包的信息,然后清除包信息,如果还有其他包,则继续发MCS_BOUND消息,一直循环,直到安装包都安装完。

然后再调用PackageManagerService.HandlerParams—>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();//注释1
                res = true;
            }
        } catch (RemoteException e) {
            if (DEBUG_INSTALL) Slog.i(TAG, "Posting install MCS_RECONNECT");
            mHandler.sendEmptyMessage(MCS_RECONNECT);
            res = false;
        }
        handleReturnCode();//注释2
        return res;
    }

其中startCopy中有两个重要的方法handleStartCopy和handleReturnCode。

handleStartCopy中会检查应用是否能安装成功,如果不能安装成功,则会返回failed的CODE;返回res标识,判断是否安装成功。

handleReturnCode方法有两处重载了,一个是删除时调用的,一个是安装时调用的,下面列出安装的地方:

@Override
    void handleReturnCode() {
        // If mArgs is null, then MCS couldn't be reached. When it
        // reconnects, it will try again to install. At that point, this
        // will succeed.
        if (mArgs != null) {
            processPendingInstall(mArgs, mRet);
        }
    }

可以看到调用了processPendingInstall方法:

    private void processPendingInstall(final InstallArgs args, final int currentStatus) {
    // Queue up an async operation since the package installation may take a little while.
    mHandler.post(new Runnable() {
        public void run() {
            mHandler.removeCallbacks(this);
             // Result object to be returned
            PackageInstalledInfo res = new PackageInstalledInfo();
            res.returnCode = currentStatus;
            res.uid = -1;
            res.pkg = null;
            res.removedInfo = new PackageRemovedInfo();
            if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
                args.doPreInstall(res.returnCode);
                synchronized (mInstallLock) {
                    installPackageLI(args, res);//注释1
                }
                args.doPostInstall(res.returnCode, res.uid);
            }

           ......省略......
}

从注释1处可以看到,调用的是installPackageLI方法:

private void installPackageLI(InstallArgs args, PackageInstalledInfo res) {
    
    ......省略......

    if (replace) {
        replacePackageLI(pkg, parseFlags, scanFlags | SCAN_REPLACING, args.user,
                installerPackageName, volumeUuid, res);
    } else {
        installNewPackageLI(pkg, parseFlags, scanFlags | SCAN_DELETE_DATA_ON_FAILURES,
                args.user, installerPackageName, volumeUuid, res);
    }
    
    ......省略......
}

如果是替换安装,则走replacePackageLI的逻辑。追踪replacePackageLI的逻辑流程:

replacePackageLI—>如果是系统应用则调用replaceSystemPackageLI—>再调用PackageParser.Package的scanPackageLI方法。

replacePackageLI—>如果是非系统应用则调用replaceNonSystemPackageLI—>再调用PackageParser.Package的scanPackageLI方法。

如果是第一次安装,则走installNewPackageLI。追踪installNewPackageLI的逻辑流程:

installNewPackageLI—>再调用PackageParser.Package的scanPackageLI方法。
到了调用PackageParser.Package的scanPackageLI方法就到了系统应用安装过程中的“继续探究扫描安装过程”逻辑,后面的安装逻辑就与系统应用安装的逻辑一样了。

7. 通过命令安装应用
命令安装应用的入口是:

framework/base/cmds/pm/src/com/android/commands/pm/Pm.java
1
应用安装的时候就会调用runInstall()方法:

private int runInstall() {
    
    ......省略......

    try {
        VerificationParams verificationParams = new VerificationParams(verificationURI,
                originatingURI, referrerURI, VerificationParams.NO_UID, null);

        mPm.installPackageAsUser(apkFilePath, obs.getBinder(), installFlags,
                installerPackageName, verificationParams, abi, userId);//注释1

    ......省略......            

    } catch (RemoteException e) {
        System.err.println(e.toString());
        System.err.println(PM_NOT_RUNNING_ERR);
        return 1;
    }
}

从注释1处可以看到安装逻辑调用的是PackageManagerService—>installPackageAsUser方法。后面的调用逻辑就与“网络下载应用安装”的逻辑一样了。

8. 通过外部设备安装应用
通过外部设备安装应用,调用的是Android内部的PackageInstaller去完成的,其本身也是一个apk。代码位置在

/packages/apps/PackageInstaller/
1
用于显示安装apk,但其最终的本质还是调用PackageManagerService去完成安装的。

当点击文件管理器中的apk时,文件管理器就会启动PackageInstaller中的PackageInstallerActivity,并将apk的信息通过intent传递给PackageInstallerActivity。

(1)深入到PackageInstallerActivity—>onCreate方法:

@Override
protected void onCreate(Bundle icicle) {
    super.onCreate(icicle);

    mPm = getPackageManager();
    mInstaller = mPm.getPackageInstaller();//注释1
    mUserManager = (UserManager) getSystemService(Context.USER_SERVICE);

    final Intent intent = getIntent();
    //注释2
    if (PackageInstaller.ACTION_CONFIRM_PERMISSIONS.equals(intent.getAction())) {
        final int sessionId = intent.getIntExtra(PackageInstaller.EXTRA_SESSION_ID, -1);
        final PackageInstaller.SessionInfo info = mInstaller.getSessionInfo(sessionId);
        if (info == null || !info.sealed || info.resolvedBaseCodePath == null) {
            Log.w(TAG, "Session " + mSessionId + " in funky state; ignoring");
            finish();
            return;
        }

        mSessionId = sessionId;
        mPackageURI = Uri.fromFile(new File(info.resolvedBaseCodePath));
        mOriginatingURI = null;
        mReferrerURI = null;
    } else {
        mSessionId = -1;
        mPackageURI = intent.getData();
        mOriginatingURI = intent.getParcelableExtra(Intent.EXTRA_ORIGINATING_URI);
        mReferrerURI = intent.getParcelableExtra(Intent.EXTRA_REFERRER);
    }

    final boolean unknownSourcesAllowedByAdmin = isUnknownSourcesAllowedByAdmin();
    final boolean unknownSourcesAllowedByUser = isUnknownSourcesEnabled();

    //注释3
    boolean requestFromUnknownSource = isInstallRequestFromUnknownSource(intent);
    
    ......省略.....

    // Block the install attempt on the Unknown Sources setting if necessary.
    if (!requestFromUnknownSource) {
        initiateInstall();//注释4
        return;
    }

    ......省略.....

    if (!unknownSourcesAllowedByAdmin
            || (!unknownSourcesAllowedByUser && isManagedProfile)) {
        showDialogInner(DLG_ADMIN_RESTRICTS_UNKNOWN_SOURCES);
        mInstallFlowAnalytics.setFlowFinished(
                InstallFlowAnalytics.RESULT_BLOCKED_BY_UNKNOWN_SOURCES_SETTING);
    } else if (!unknownSourcesAllowedByUser) {
        // Ask user to enable setting first
        showDialogInner(DLG_UNKNOWN_SOURCES);
        mInstallFlowAnalytics.setFlowFinished(
                InstallFlowAnalytics.RESULT_BLOCKED_BY_UNKNOWN_SOURCES_SETTING);
    } else {
        initiateInstall();//注释5
    }
}

注释1:获取PackageInstaller对象

注释2:PackageInstaller检查权限

注释3:PackageInstaller检查是否开启未知来源

注释4、注释5:调用initiateInstall方法

(2)深入到PackageInstallerActivity—>initiateInstall方法

private void initiateInstall() {
    String pkgName = mPkgInfo.packageName;
    // Check if there is already a package on the device with this name
    // but it has been renamed to something else.
    String[] oldName = mPm.canonicalToCurrentPackageNames(new String[] { pkgName });
    if (oldName != null && oldName.length > 0 && oldName[0] != null) {
        pkgName = oldName[0];
        mPkgInfo.packageName = pkgName;
        mPkgInfo.applicationInfo.packageName = pkgName;
    }
    // Check if package is already installed. display confirmation dialog if replacing pkg
    try {
        // This is a little convoluted because we want to get all uninstalled
        // apps, but this may include apps with just data, and if it is just
        // data we still want to count it as "installed".
        mAppInfo = mPm.getApplicationInfo(pkgName,
                PackageManager.GET_UNINSTALLED_PACKAGES);
        if ((mAppInfo.flags&ApplicationInfo.FLAG_INSTALLED) == 0) {
            mAppInfo = null;
        }
    } catch (NameNotFoundException e) {
        mAppInfo = null;
    }

    mInstallFlowAnalytics.setReplace(mAppInfo != null);
    mInstallFlowAnalytics.setSystemApp(
            (mAppInfo != null) && ((mAppInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0));

    startInstallConfirm();//调用
}

initiateInstall方法主要负责检查是否已经安装过,是否是系统应用等。然后继续调用了startInstallConfirm方法。

(3) 深入到PackageInstallerActivity—>startInstallConfirm方法

 private void startInstallConfirm() {
    TabHost tabHost = (TabHost)findViewById(android.R.id.tabhost);
    tabHost.setup();
    ViewPager viewPager = (ViewPager)findViewById(R.id.pager);
    TabsAdapter adapter = new TabsAdapter(this, tabHost, viewPager);
    adapter.setOnTabChangedListener(new TabHost.OnTabChangeListener() {
        @Override
        public void onTabChanged(String tabId) {
            if (TAB_ID_ALL.equals(tabId)) {
                mInstallFlowAnalytics.setAllPermissionsDisplayed(true);
            } else if (TAB_ID_NEW.equals(tabId)) {
                mInstallFlowAnalytics.setNewPermissionsDisplayed(true);
            }
        }
    });
    // If the app supports runtime permissions the new permissions will
    // be requested at runtime, hence we do not show them at install.
    boolean supportsRuntimePermissions = mPkgInfo.applicationInfo.targetSdkVersion
            >= Build.VERSION_CODES.M;
    boolean permVisible = false;
    mScrollView = null;
    mOkCanInstall = false;
    int msg = 0;

    AppSecurityPermissions perms = new AppSecurityPermissions(this, mPkgInfo);
    final int N = perms.getPermissionCount(AppSecurityPermissions.WHICH_ALL);
    if (mAppInfo != null) {
        msg = (mAppInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0
                ? R.string.install_confirm_question_update_system
                : R.string.install_confirm_question_update;
        mScrollView = new CaffeinatedScrollView(this);
        mScrollView.setFillViewport(true);
        boolean newPermissionsFound = false;
        if (!supportsRuntimePermissions) {
            newPermissionsFound =
                    (perms.getPermissionCount(AppSecurityPermissions.WHICH_NEW) > 0);
            mInstallFlowAnalytics.setNewPermissionsFound(newPermissionsFound);
            if (newPermissionsFound) {
                permVisible = true;
                mScrollView.addView(perms.getPermissionsView(
                        AppSecurityPermissions.WHICH_NEW));
            }
        }
        if (!supportsRuntimePermissions && !newPermissionsFound) {
            LayoutInflater inflater = (LayoutInflater)getSystemService(
                    Context.LAYOUT_INFLATER_SERVICE);
            TextView label = (TextView)inflater.inflate(R.layout.label, null);
            label.setText(R.string.no_new_perms);
            mScrollView.addView(label);
        }
        adapter.addTab(tabHost.newTabSpec(TAB_ID_NEW).setIndicator(
                getText(R.string.newPerms)), mScrollView);
    } else  {
        findViewById(R.id.tabscontainer).setVisibility(View.GONE);
        findViewById(R.id.divider).setVisibility(View.VISIBLE);
    }
    if (!supportsRuntimePermissions && N > 0) {
        permVisible = true;
        LayoutInflater inflater = (LayoutInflater)getSystemService(
                Context.LAYOUT_INFLATER_SERVICE);
        View root = inflater.inflate(R.layout.permissions_list, null);
        if (mScrollView == null) {
            mScrollView = (CaffeinatedScrollView)root.findViewById(R.id.scrollview);
        }
        ((ViewGroup)root.findViewById(R.id.permission_list)).addView(
                    perms.getPermissionsView(AppSecurityPermissions.WHICH_ALL));
        adapter.addTab(tabHost.newTabSpec(TAB_ID_ALL).setIndicator(
                getText(R.string.allPerms)), root);
    }
    mInstallFlowAnalytics.setPermissionsDisplayed(permVisible);
    if (!permVisible) {
        if (mAppInfo != null) {
            // This is an update to an application, but there are no
            // permissions at all.
            msg = (mAppInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0
                    ? R.string.install_confirm_question_update_system_no_perms
                    : R.string.install_confirm_question_update_no_perms;
        } else {
            // This is a new application with no permissions.
            msg = R.string.install_confirm_question_no_perms;
        }
        tabHost.setVisibility(View.GONE);
        mInstallFlowAnalytics.setAllPermissionsDisplayed(false);
        mInstallFlowAnalytics.setNewPermissionsDisplayed(false);
        findViewById(R.id.filler).setVisibility(View.VISIBLE);
        findViewById(R.id.divider).setVisibility(View.GONE);
        mScrollView = null;
    }
    if (msg != 0) {
        ((TextView)findViewById(R.id.install_confirm_question)).setText(msg);
    }
    mInstallConfirm.setVisibility(View.VISIBLE);
    mOk = (Button)findViewById(R.id.ok_button);
    mOk.requestFocus();
    mCancel = (Button)findViewById(R.id.cancel_button);
    mOk.setOnClickListener(this);
    mCancel.setOnClickListener(this);
//add by wangqi begin{@
    TabWidget tabWidget = tabHost.getTabWidget();
    int childCount = tabWidget.getChildCount();
    if (childCount == 2) {
        final View left = tabWidget.getChildAt(0);
        final View right = tabWidget.getChildAt(1);
        left.setId(tabWidget.getId() + 1);
        right.setId(tabWidget.getId() + 2);
        right.setNextFocusLeftId(left.getId());

        right.setOnKeyListener(new View.OnKeyListener() {
            @Override
            public boolean onKey(View v, int keyCode, KeyEvent event) {
                if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT && event.getAction() == KeyEvent.ACTION_DOWN) {
                    left.requestFocus();
                }
                return true;
            }
        });
    }
// end @}

    if (mScrollView == null) {
        // There is nothing to scroll view, so the ok button is immediately
        // set to install.
        //mOk.setText(R.string.install);
        mOkCanInstall = true;
    } else {
        mScrollView.setFullScrollAction(new Runnable() {
            @Override
            public void run() {
                //mOk.setText(R.string.install);
                mOkCanInstall = true;
            }
        });
    }
}

startInstallConfirm主要负责界面初始化,显示权限信息等。

界面初始化完成后,安装界面就会呈现在用户面前,如果用户想要安装这个应用程序,可以直接点击确认按钮,此时就会调用PackageInstallerActivity中的onClick方法:

public void onClick(View v) {
    if (v == mOk) {
        if (mOkCanInstall || mScrollView == null) {
            mInstallFlowAnalytics.setInstallButtonClicked();
            if (mSessionId != -1) {
                mInstaller.setPermissionsResult(mSessionId, true);

                // We're only confirming permissions, so we don't really know how the
                // story ends; assume success.
                mInstallFlowAnalytics.setFlowFinishedWithPackageManagerResult(
                        PackageManager.INSTALL_SUCCEEDED);
                finish();
            } else {
                startInstall();//注释1
            }
        } else {
            mScrollView.pageScroll(View.FOCUS_DOWN);
        }
    } else if(v == mCancel) {
        // Cancel and finish
        setResult(RESULT_CANCELED);
        if (mSessionId != -1) {
            mInstaller.setPermissionsResult(mSessionId, false);
        }
        mInstallFlowAnalytics.setFlowFinished(
                InstallFlowAnalytics.RESULT_CANCELLED_BY_USER);
        finish();
    }
}

onClick方法中分别会对取消和确定按钮做处理,如果是确定按钮,就会调用注释1处的startInstall方法。

(4) 深入到PackageInstallerActivity—>startInstall方法

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);
    newIntent.setClass(this, InstallAppProgress.class);//跳转的Activity
    newIntent.putExtra(InstallAppProgress.EXTRA_MANIFEST_DIGEST, mPkgDigest);
    newIntent.putExtra(
            InstallAppProgress.EXTRA_INSTALL_FLOW_ANALYTICS, mInstallFlowAnalytics);
    String installerPackageName = getIntent().getStringExtra(
            Intent.EXTRA_INSTALLER_PACKAGE_NAME);

    ......省略.....
    
    if(localLOGV) Log.i(TAG, "downloaded app uri="+mPackageURI);
    startActivity(newIntent);//注释1
    finish();
}

startInstall方法启动后,将会跳转到注释1处的InstallAppProgress界面。并关掉当前的PackageInstallerActivity

(5) 深入到InstallAppProgress.java文件中

当InstallAppProgress.java启动时,会先调用InstallAppProgress.java—>onCreate方法:

@Override
public void onCreate(Bundle icicle) {
    
    ......省略.....

    initView();
}

onCreate方法中会调用initView方法初始化界面:

public void initView() {
    
    ......省略.....

    if ("package".equals(mPackageURI.getScheme())) {
        try {
            pm.installExistingPackage(mAppInfo.packageName);
            observer.packageInstalled(mAppInfo.packageName,
                    PackageManager.INSTALL_SUCCEEDED);
        } catch (PackageManager.NameNotFoundException e) {
            observer.packageInstalled(mAppInfo.packageName,
                    PackageManager.INSTALL_FAILED_INVALID_APK);
        }
    } else {
        pm.installPackageWithVerificationAndEncryption(mPackageURI, observer, installFlags,
                installerPackageName, verificationParams, null);
    }
}

可以看到有两条安装逻辑,我这里只探索else中的逻辑。else中实际上是调用了ApplicationPackageManager的installPackageWithVerificationAndEncryption方法来安装。

(6) 深入到ApplicationPackageManager—>installPackageWithVerificationAndEncryption方法:

@Override
public void installPackageWithVerificationAndEncryption(Uri packageURI,
        PackageInstallObserver observer, int flags, String installerPackageName,
        VerificationParams verificationParams, ContainerEncryptionParams encryptionParams) {
    installCommon(packageURI, observer, flags, installerPackageName, verificationParams,
            encryptionParams);//注释1
}

注意installPackageWithVerificationAndEncryption方法上有个Override,说明继承于父类的PackageManager中。

从注释1中可以看到调用的是installCommon方法:

private void installCommon(Uri packageURI,
        PackageInstallObserver observer, int flags, String installerPackageName,
        VerificationParams verificationParams, ContainerEncryptionParams encryptionParams) {
    
    ......省略.....

    try {
        mPM.installPackage(originPath, observer.getBinder(), flags, installerPackageName,
                verificationParams, null);//注释1
    } catch (RemoteException ignored) {
    }
}

从注释1中可以看到调用的是PMS中的installPackage方法。到这里后续的逻辑就与前面的“网络下载应用安装”中的逻辑一样了。
 

转载于:https://my.oschina.net/u/4132401/blog/3048975

你可能感兴趣的:(移动开发,java,runtime)