Android 9.0 源碼_核心篇 -- 深入研究 PMS 系列(4)之 PackageManager

開篇

核心源碼

關鍵類 路徑
Context.java frameworks/base/core/java/android/content/Context.java
ContextImpl.java frameworks/base/core/java/android/app/ContextImpl.java
PackageManager.java frameworks/base/core/java/android/content/pm/PackageManager.java
ApplicationPackageManager.java frameworks/base/core/java/android/app/ApplicationPackageManager.java
ActivityThread.java frameworks/base/core/java/android/app/ActivityThread.java

PackageManager

簡介

PackageManager 是一個抽象類:

/**
 * Class for retrieving various kinds of information related to the application
 * packages that are currently installed on the device.
 *
 * You can find this class through {@link Context#getPackageManager}.
 */
public abstract class PackageManager {

註釋可以看出:PackageManager 這個類是檢測當前已經安裝在當前設備上的應用程序包的信息。你可以調用 Context 類的 getPackageManager() 方法來獲取 PackageManager。

安裝 APK

PackageManager 是一個實際上管理應用程序安裝、卸載和升級的 API。當我們安裝 APK 文件時,PackageManager 會解析 APK 包文件和顯示確認信息。當我們點擊 OK 按鈕後,PackageManager 會調用一個叫 "InstallPackage" 的方法,這個方法有 4 個參數,也就是 uri、installFlags、observer、installPackagename。PackageManager 會啓動一個叫 "package" 的 servcie 服務,現在所有模糊的東西會發生在這個 service 中。
5713484-0a2df5e912885229.png

實現功能

        ✨ 1、安裝、卸載應用
        ✨ 2、查詢 permission 相關信息
        ✨ 3、查詢 Application 相關信息(application、activity、receiver、service、provider 及相應屬性等)
        ✨ 4、查詢已安裝應用
        ✨ 5、增加、刪除 permission
        ✨ 6、清除用戶數據、緩存、代碼等

抽象方法

getPackageInfo

    /**
     * Retrieve overall information about an application package that is
     * installed on the system.
     *
     * @param packageName The full name (i.e. com.google.apps.contacts) of the
     *            desired package.
     * @param flags Additional option flags to modify the data returned.
     * @return A PackageInfo object containing information about the package. If
     *         flag {@code MATCH_UNINSTALLED_PACKAGES} is set and if the package
     *         is not found in the list of installed applications, the package
     *         information is retrieved from the list of uninstalled
     *         applications (which includes installed applications as well as
     *         applications with data directory i.e. applications which had been
     *         deleted with {@code DONT_DELETE_DATA} flag set).
     * @throws NameNotFoundException if a package with the given name cannot be
     *             found on the system.
     * 
     * 通過包名獲取該包名對應的應用程序的 PackageInfo 對象,
     * PackageInfo 類包含了從 AndroidManifest.xml 文件中收集的所有信息。
     */
    public abstract PackageInfo getPackageInfo(String packageName, @PackageInfoFlags int flags)
            throws NameNotFoundException;

getApplicationInfo

    /**
     * Retrieve all of the information we know about a particular
     * package/application.
     *
     * @param packageName The full name (i.e. com.google.apps.contacts) of an
     *            application.
     * @param flags Additional option flags to modify the data returned.
     * @return An {@link ApplicationInfo} containing information about the
     *         package. If flag {@code MATCH_UNINSTALLED_PACKAGES} is set and if
     *         the package is not found in the list of installed applications,
     *         the application information is retrieved from the list of
     *         uninstalled applications (which includes installed applications
     *         as well as applications with data directory i.e. applications
     *         which had been deleted with {@code DONT_DELETE_DATA} flag set).
     * @throws NameNotFoundException if a package with the given name cannot be
     *             found on the system.
     *
     * 根據包名返回其對應的 ApplicationInfo 信息。
     */
    public abstract ApplicationInfo getApplicationInfo(String packageName,
            @ApplicationInfoFlags int flags) throws NameNotFoundException;

getActivityInfo

    /**
     * Retrieve all of the information we know about a particular activity
     * class.
     *
     * @param component The full component name (i.e.
     *            com.google.apps.contacts/com.google.apps.contacts.
     *            ContactsList) of an Activity class.
     * @param flags Additional option flags to modify the data returned.
     * @return An {@link ActivityInfo} containing information about the
     *         activity.
     * @throws NameNotFoundException if a package with the given name cannot be
     *             found on the system.
     *
     * 檢索出一個特定的 Activity 類的所有信息。
     */
    public abstract ActivityInfo getActivityInfo(ComponentName component,
            @ComponentInfoFlags int flags) throws NameNotFoundException;

getReceiverInfo

    /**
     * Retrieve all of the information we know about a particular receiver
     * class.
     *
     * @param component The full component name (i.e.
     *            com.google.apps.calendar/com.google.apps.calendar.
     *            CalendarAlarm) of a Receiver class.
     * @param flags Additional option flags to modify the data returned.
     * @return An {@link ActivityInfo} containing information about the
     *         receiver.
     * @throws NameNotFoundException if a package with the given name cannot be
     *             found on the system.
     *
     * 檢索出一個特定的 Receiver 類的所有信息(這裏主要指 ActivityInfo)。
     */
    public abstract ActivityInfo getReceiverInfo(ComponentName component,
            @ComponentInfoFlags int flags) throws NameNotFoundException;

getServiceInfo

    /**
     * Retrieve all of the information we know about a particular service class.
     *
     * @param component The full component name (i.e.
     *            com.google.apps.media/com.google.apps.media.
     *            BackgroundPlayback) of a Service class.
     * @param flags Additional option flags to modify the data returned.
     * @return A {@link ServiceInfo} object containing information about the
     *         service.
     * @throws NameNotFoundException if a package with the given name cannot be
     *             found on the system.
     *
     * 檢索出一個特定的 Service 類的所有信息(這裏主要指 ServiceInfo)。
     */
    public abstract ServiceInfo getServiceInfo(ComponentName component,
            @ComponentInfoFlags int flags) throws NameNotFoundException;

getProviderInfo

    /**
     * Retrieve all of the information we know about a particular content
     * provider class.
     *
     * @param component The full component name (i.e.
     *            com.google.providers.media/com.google.providers.media.
     *            MediaProvider) of a ContentProvider class.
     * @param flags Additional option flags to modify the data returned.
     * @return A {@link ProviderInfo} object containing information about the
     *         provider.
     * @throws NameNotFoundException if a package with the given name cannot be
     *             found on the system.
     *
     * 檢索出一個特定的 content provider 類的所有信息(這裏主要指 ProviderInfo)。
     */
    public abstract ProviderInfo getProviderInfo(ComponentName component,
            @ComponentInfoFlags int flags) throws NameNotFoundException;

getInstalledPackages

    /**
     * Return a List of all packages that are installed for the current user.
     *
     * @param flags Additional option flags to modify the data returned.
     * @return A List of PackageInfo objects, one for each installed package,
     *         containing information about the package. In the unlikely case
     *         there are no installed packages, an empty list is returned. If
     *         flag {@code MATCH_UNINSTALLED_PACKAGES} is set, the package
     *         information is retrieved from the list of uninstalled
     *         applications (which includes installed applications as well as
     *         applications with data directory i.e. applications which had been
     *         deleted with {@code DONT_DELETE_DATA} flag set).
     *
     * 獲取設備上安裝的所有軟件包。
     */
    public abstract List<PackageInfo> getInstalledPackages(@PackageInfoFlags int flags);

currentToCanonicalPackageNames

    /**
     * Map from the current package names in use on the device to whatever
     * the current canonical name of that package is.
     * @param names Array of current names to be mapped.
     * @return Returns an array of the same size as the original, containing
     * the canonical name for each package.
     *
     * 從設備上使用當前包名映射到該軟件包名的當前規範名稱,
     * 如果修改包名會用到,沒有修改過包名一般不會用到。
     */
    public abstract String[] currentToCanonicalPackageNames(String[] names);

canonicalToCurrentPackageNames

    /**
     * Map from a packages canonical name to the current name in use on the device.
     * @param names Array of new names to be mapped.
     * @return Returns an array of the same size as the original, containing
     * the current name for each package.
     *
     * 將軟件包規範名稱映射到設備上正在使用的當前名稱,
     * 我們發現:canonicalToCurrentPackageNames() 和 currentToCanonicalPackageNames() 方法是相反的兩個方法
     */
    public abstract String[] canonicalToCurrentPackageNames(String[] names);

getPermissionInfo

    /**
     * Retrieve all of the information we know about a particular permission.
     *
     * @param name The fully qualified name (i.e. com.google.permission.LOGIN)
     *            of the permission you are interested in.
     * @param flags Additional option flags to modify the data returned.
     * @return Returns a {@link PermissionInfo} containing information about the
     *         permission.
     * @throws NameNotFoundException if a package with the given name cannot be
     *             found on the system.
     *
     * 檢測出我們想要知道的所有關於權限的信息。
     */
    public abstract PermissionInfo getPermissionInfo(String name, @PermissionInfoFlags int flags)
            throws NameNotFoundException;

queryPermissionsByGroup

    /**
     * Query for all of the permissions associated with a particular group.
     *
     * @param group The fully qualified name (i.e. com.google.permission.LOGIN)
     *            of the permission group you are interested in. Use null to
     *            find all of the permissions not associated with a group.
     * @param flags Additional option flags to modify the data returned.
     * @return Returns a list of {@link PermissionInfo} containing information
     *         about all of the permissions in the given group.
     * @throws NameNotFoundException if a package with the given name cannot be
     *             found on the system.
     *
     * 查詢與特定組相關的所有權限。
     */
    public abstract List<PermissionInfo> queryPermissionsByGroup(String group,
            @PermissionInfoFlags int flags) throws NameNotFoundException;

getAllPermissionGroups

    /**
     * Retrieve all of the known permission groups in the system.
     *
     * @param flags Additional option flags to modify the data returned.
     * @return Returns a list of {@link PermissionGroupInfo} containing
     *         information about all of the known permission groups.
     *
     * 檢索出系統中所有已知的權限。
     */
    public abstract List<PermissionGroupInfo> getAllPermissionGroups(
            @PermissionGroupInfoFlags int flags);

當然除了上面列舉出來的方法以外,還有其他很多方法,我們不再一一列出來,如果後面分析遇到會再單獨拿出來分析!

安裝方法

接下來我們來看看 PackageManager 中關於安裝的幾個方法!

InstallPackage

    /**
     * packageURI:表示安裝的路徑,可以是 "file:" 或者 "content:" 的 URI
     * observer:  一個回調的觀察者,有了這個觀察者,就可以在軟件包安裝完成後得到安裝結果的通知。
     *             如果安裝完成會調用這個觀察者 IPackageInstallObserver 的 packageInstalled(String,int)方法,observer這個入參不能爲空。
     * flags:     標誌位參數
     * nstallerPackageName:正在進行安裝的安裝包包名
     */
     
    /**
     * @deprecated replaced by {@link PackageInstaller}
     * @hide
     */
    @Deprecated
    public abstract void installPackage(      // 棄用
            Uri packageURI,
            IPackageInstallObserver observer,
            @InstallFlags int flags,
            String installerPackageName);
    /**
     * @deprecated replaced by {@link PackageInstaller}
     * @hide
     */
    @Deprecated
    public abstract void installPackage(      // 棄用
            Uri packageURI,
            PackageInstallObserver observer,
            @InstallFlags int flags,
            String installerPackageName);

從 8.1 開始,已經棄用了 installPackage 方法(9.0 的代碼中已經去除 installPackage 代碼了),而是使用 PackageInstaller 執行應用的安裝、升級和刪除操作。

    /**
     * Return interface that offers the ability to install, upgrade, and remove
     * applications on the device.
     */
    public abstract @NonNull PackageInstaller getPackageInstaller();

installExistingPackage

    /**
     * If there is already an application with the given package name installed
     * on the system for other users, also install it for the calling user.
     * @hide
     */
    @SystemApi   // 系統 API
    public abstract int installExistingPackage(String packageName) throws NameNotFoundException;

通過註釋可以看出這個方法的用途:如果系統上已經安裝相同包名的應用程序,則重複重新安裝。

具體實現類

我們知道 PackageManager 是一個抽象類,它裏面很重要的方法都是抽象的,所以在具體執行的時候,肯定是它的實現子類,那麼我們就來看下它的具體實現類。

前面我們講解 PackageManager 類的時候,官網推薦獲取 PackageManager 對象的方法是 Context 的 Context#getPackageManager() 方法,那我們來看下:

// frameworks/base/core/java/android/content/Context.java

/** Return PackageManager instance to find global package information. */
public abstract PackageManager getPackageManager();

我們知道 Context 也是一個抽象類,而它的 getPackageManager() 也是抽象方法,但 Context 的具體實現類是 ContextImpl,那我們就去 ContextImpl 裏面看下:

/**
 * Common implementation of Context API, which provides the base
 * context object for Activity and other application components.
 * @hide
 */
public class ContextImpl extends Context {
    private PackageManager mPackageManager;
    ... ...

    @Override
    public PackageManager getPackageManager() {
        // 判斷 mPackageManager 是否爲空,如果爲空,則說明是第一次調用
        if (mPackageManager != null) {
            return mPackageManager;
        }
        
        // 調用 ActivityThread 的靜態方法 getPackageManager() 獲取一個 IPackageManager 對象
        IPackageManager pm = ActivityThread.getPackageManager();
        
        // 如果獲取的 IPackageManager 對象不爲空,則構造一個 ApplicationPackageManager 對象,ApplicationPackageManager 是 PackageManager 的子類
        if (pm != null) {
            // Doesn't matter if we make more than one instance.
            return (mPackageManager = new ApplicationPackageManager(this, pm));
        }

        return null;
    }
    ... ...
}

所以,在我們平時調用 Context 的 getPackageManager()方法後,其實返回的是 ApplicationPackageManager 這個類。

ApplicationPackageManager

簡介

我們先來看下 ApplicationPackageManager 類的源碼:

/** @hide */
public class ApplicationPackageManager extends PackageManager {

通過源碼我們知道 ApplicationPackageManager 繼承自PackageManager,而且 ApplicationPackageManager 類不是抽象的,所以 ApplicationPackageManager 必然實現了 PackageManager 的所有抽象方法。

構造函數

    protected ApplicationPackageManager(ContextImpl context, IPackageManager pm) {
        mContext = context;
        mPM = pm;
    }

InstallPackage

在講解 PackageManager 的時候,我們提到過安裝 APK 會調用 InstallPackage 方法(Android 8.1,9.0 已棄用),我們看下在 ApplicationPackageManager 中的具體實現:

    @Override
    public void installPackage(Uri packageURI, IPackageInstallObserver observer, int flags,
                               String installerPackageName) {
        installCommon(packageURI, new LegacyPackageInstallObserver(observer), flags,
                installerPackageName, mContext.getUserId());
    }

installCommon

// 源碼來自:Android 8.1,9.0 已棄用
    private void installCommon(Uri packageURI,
            PackageInstallObserver observer, int flags, String installerPackageName,
            int userId) {
        // scheme 判斷,如果非 "file" 則拋異常,因爲只支持 file 格式的 URI
        if (!"file".equals(packageURI.getScheme())) {
            throw new UnsupportedOperationException("Only file:// URIs are supported");
        }

        // 獲取相應的路徑
        final String originPath = packageURI.getPath();
        try {
            // 調用 installPackageAsUser 方法
            mPM.installPackageAsUser(originPath, observer.getBinder(), flags, installerPackageName, userId);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

可以發現,public abstract void installPackage() 方法其內部本質是調用的 IPackageManager 的 installPackageAsUser() 方法。

IPackageManager

ActivityThread

我們先來看看如下代碼(上面沒有分析):

// 調用 ActivityThread 的靜態方法 getPackageManager() 獲取一個 IPackageManager 對象
IPackageManager pm = ActivityThread.getPackageManager();

跟蹤 getPackageManager():

    static volatile IPackageManager sPackageManager;
    
    public static IPackageManager getPackageManager() {
        // 判斷 sPackageManager 是否爲空,如果爲空,則說明是的第一次調用,走第二步,如果不爲空,則直接返回 sPackageManager
        if (sPackageManager != null) {
            return sPackageManager;
        }
        
        // 能走到第二步,說明這是第一次調用,則調用 ServiceManager 的 getService(String) 方法獲取一個 IBinder 對象
        IBinder b = ServiceManager.getService("package");
        
        // 調用 IPackageManager.Stub.asInterface(IBinder) 獲取一個 sPackageManager 對象
        sPackageManager = IPackageManager.Stub.asInterface(b);
        return sPackageManager;
    }

IPM/PM/PMS 三者關聯

APM/PMS/IPM

在上面分析 ContextImpl 的 getPackageManager() 方法裏面,我們知道:

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

而在 ActivityThread 的靜態方法 getPackageManager() 裏面:

        sPackageManager = IPackageManager.Stub.asInterface(b);
        return sPackageManager;

所以在 ApplicationPackageManager 裏面的 mPM 其實就是 IPackageManager.Stub 內部類 Proxy 對象。

那對應的 IPackageManager.Stub 是什麼?其實就是 PackageManagerService.java

public class PackageManagerService extends IPackageManager.Stub implements PackageSender {

如下圖所示:
5713484-3e75d4de4b56b02f.png

小結

結合上面的知識,再結合 PackageManager、ApplicationPackageManager 和 PackageManagerService 總結如下:

        ✨  IPackageManager 負責通信:IPackageManager 接口類中定義了很多業務方法,但是由於安全等方面的考慮,Android 對外(即SDK)提供的僅僅是一個子集,該子集被封裝在抽象類 PackageManager 中。客戶端一般通過 Context 的 getPackageManager 函數返回一個類型爲 PackageManager 的對象,該對象的實際類型是 PackageManager 的子類 ApplicationPackageManager 。ApplicationPackageManager 並沒有直接參與 Binder 通信,而是通過 mPM 成員變量指向了一個 IPackageManager.Stub.Proxy 類型的對象。
        ✨  AIDL中 的 Binder 服務端是 PackageManagerService,因爲 PackageManagerService 繼承自 IPackageManager.Stub 。由於 IPackageManager.Stub 類從 Binder 派生,所以 PackageManagerService 將作爲服務端參與 Binder 通信。
        ✨  AIDL中 的 Binder 客戶端是 ApplicationPackageManager 中成員變量 mPM,因爲mPM內部指向的是 IPackageManager.Stub.Proxy。

整體流程的 Binder 結構大致如下:
5713484-5bc8b7f82efd3cf3.png

參考

                  01. https://www.jianshu.com/p/c56...
                  02. https://www.jianshu.com/p/a30...

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章