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 {
                params.volumeUuid = PackageHelper.resolveInstallVolume(mContext, params);
            } finally {
                Binder.restoreCallingIdentity(ident);
            }
        }

        final int sessionId;
        final PackageInstallerSession session;
        synchronized (mSessions) {
            // Sanity check that installer isn't going crazy
            final int activeCount = getSessionCount(mSessions, callingUid);
            if (activeCount >= MAX_ACTIVE_SESSIONS) {
                throw new IllegalStateException(
                        "Too many active sessions for UID " + callingUid);
            }
            final int historicalCount = mHistoricalSessionsByInstaller.get(callingUid);
            if (historicalCount >= MAX_HISTORICAL_SESSIONS) {
                throw new IllegalStateException(
                        "Too many historical sessions for UID " + callingUid);
            }

            sessionId = allocateSessionIdLocked();
        }

        final long createdMillis = System.currentTimeMillis();
        // We're staging to exactly one location
        File stageDir = null;
        String stageCid = null;
        if ((params.installFlags & PackageManager.INSTALL_INTERNAL) != 0) {
            final boolean isInstant =
                    (params.installFlags & PackageManager.INSTALL_INSTANT_APP) != 0;
            stageDir = buildStageDir(params.volumeUuid, sessionId, isInstant);
        } else {
            stageCid = buildExternalStageCid(sessionId);
        }

        session = new PackageInstallerSession(mInternalCallback, mContext, mPm,
                mInstallThread.getLooper(), sessionId, userId, installerPackageName, callingUid,
                params, createdMillis, stageDir, stageCid, false, false);

        synchronized (mSessions) {
            mSessions.put(sessionId, session);
        }

        mCallbacks.notifySessionCreated(session.sessionId, session.userId);
        writeSessionsAsync();
        return sessionId;
    }
}

無界面安裝

上面說的有界面安裝是正常的安裝方式,可以讓用戶感知的。然而還有一種比較流氓的方式,就是靜默安裝。用戶無感知,這個應用就被安裝成功了。

這種方式就是通過 adb 命令進行安裝 adb install

這個命令我們可以在 commandline.cpp 中找到。可以看到通過命令匹配 執行 install_app 函數。與之相應的還有 adb uninstall 命令

int adb_commandline(int argc, const char** argv) {
    ......
    else if (!strcmp(argv[0], "install")) {
        if (argc < 2) return syntax_error("install requires an argument");
        if (_use_legacy_install()) {
            return install_app_legacy(argc, argv);
        }
        return install_app(argc, argv);
    }
    ......
    else if (!strcmp(argv[0], "uninstall")) {
        if (argc < 2) return syntax_error("uninstall requires an argument");
        if (_use_legacy_install()) {
            return uninstall_app_legacy(argc, argv);
        }
        return uninstall_app(argc, argv);
    }
    ......
}
static int install_app_legacy(TransportType transport, const char* serial, int argc, const char** argv) {
    ......
    result = pm_command(transport, serial, argc, argv);
    ......
    return result;
}
static int pm_command(TransportType transport, const char* serial, int argc, const char** argv) {
    std::string cmd = "pm";

    while (argc-- > 0) {
        cmd += " " + escape_arg(*argv++);
    }

    return send_shell_command(transport, serial, cmd, false);
}

順着調用棧走下去發現最終執行了 pm 命令。而這個命令會執行一個 pm.jar 文件,該 jar 包是通過 Pm.java 打包而成。

命令執行後會調用 Pm 的 main 函數。順着調用往下走,最終發現還是調用了 PackageInstallerService 的 createSession 函數。殊途同歸,和有界面安裝走入相同的流程了。

public final class Pm {
    public static void main(String[] args) {
        int exitCode = 1;
        try {
            exitCode = new Pm().run(args);
        } catch (Exception e) {
            Log.e(TAG, "Error", e);
            System.err.println("Error: " + e);
            if (e instanceof RemoteException) {
                System.err.println(PM_NOT_RUNNING_ERR);
            }
        }
        System.exit(exitCode);
    }
   public int run(String[] args) throws RemoteException {
        boolean validCommand = false;
        if (args.length < 1) {
            return showUsage();
        }
        mAm = IAccountManager.Stub.asInterface(ServiceManager.getService(Context.ACCOUNT_SERVICE));
        mUm = IUserManager.Stub.asInterface(ServiceManager.getService(Context.USER_SERVICE));
        mPm = IPackageManager.Stub.asInterface(ServiceManager.getService("package"));

        if (mPm == null) {
            System.err.println(PM_NOT_RUNNING_ERR);
            return 1;
        }
        mInstaller = mPm.getPackageInstaller();
        ......
        if ("install".equals(op)) {
            return runInstall();
        }
        ......
        if ("uninstall".equals(op)) {
            return runUninstall();
        }
        ......
   }
    private int runInstall() throws RemoteException {
        ......
        final int sessionId = doCreateSession(params.sessionParams,
                params.installerPackageName, params.userId);
        ......
    }
    private int doCreateSession(SessionParams params, String installerPackageName, int userId)
            throws RemoteException {
        userId = translateUserId(userId, "runInstallCreate");
        if (userId == UserHandle.USER_ALL) {
            userId = UserHandle.USER_SYSTEM;
            params.installFlags |= PackageManager.INSTALL_ALL_USERS;
        }

        final int sessionId = mInstaller.createSession(params, installerPackageName, userId);
        return sessionId;
    }
}

APK 安裝原理

至此對於 PMS 的啓動,和 APK 文件的兩種安裝方式我們都分析完了,下面對結合兩種安裝方式總結一下

對於 Android 系統安裝 APK 應用,就是將 APK 文件解壓,把相應的文件 copy 到相應的目錄中即可。

  1. data/app/package_name
    安裝時將 apk 文件複製到此目錄,可以將文件取出並安裝
  2. data/data/package_name
    開闢存放應用程序的文件數據的文件夾,包括我們的 so 庫、緩存文件等
  3. data/dalvik-cache
    將 apk 解壓出的 dex 文件複製到此目錄
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章