android pm命令实现

android终端提供了一些cmd,开发人员可以通过这些cmd获取信息和执行任务。比较常用的有am,pm,input等,今天讲讲pm命令是如何实现的。


pm经常用的是安装apk以及查询系统安装的apk有哪些,确定apk是否有安装成功,比如:

pm install  /mnt/usb/sda1/xxx.apk   ->android 5.0之后必须要写绝对路径,否则会提示url无效

pm list packages -f    //查询安装的apk并且显示apk存放路径


pm cmd对应source code的路径:

frameworks\base\cmds\pm

查看pm Android.mk,内容如下:

# Copyright 2007 The Android Open Source Project
#
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)

LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_MODULE := pm
include $(BUILD_JAVA_LIBRARY)


include $(CLEAR_VARS)
ALL_PREBUILT += $(TARGET_OUT)/bin/pm
$(TARGET_OUT)/bin/pm : $(LOCAL_PATH)/pm | $(ACP)
	$(transform-prebuilt-to-target)
第一部分是编译出pm.jar,pm cmd的主要实现就是在pm.jar实现。

第二部分是拷贝pm脚本到/syste/bin/目录下,否则无法执行pm命令。

# Script to start "pm" on the device, which has a very rudimentary
# shell.
#
base=/system
export CLASSPATH=$base/framework/pm.jar
exec app_process $base/bin com.android.commands.pm.Pm "$@"
pm脚本首先是export pm.jar到环境变量,然后通过zygote(zygote是app_process的别名)去启动Pm.java并将参数传给Pm.java的main函数,根据不同的参数,处理不同的场景。

pm等命令的实现,主要是通过Java层,并且封装形式是jar,这一点个人觉得很强大,也很神奇。对于可执行档,一般都是c/c++去实现,实现起来没有java灵活,现在java层也可以进行封装,实现交互的tool,google的这个设计和功能,确实非常强大。


因为pm命令的参数很多,这里只是讲讲上面提到的两种命令,以及pm --help命令。

Pm.java的main函数实现如下:

 public static void main(String[] args) {
        try {
            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);
            }
        }
    }
main函数实现很简单,拿到参数args,实例化Pm对象并将args参数给到run函数。

public void run(String[] args) throws IOException, RemoteException {
        boolean validCommand = false;
        if (args.length < 1) {
            showUsage();
            return;
        }

        mUm = IUserManager.Stub.asInterface(ServiceManager.getService("user"));
        mPm = IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
        if (mPm == null) {
            System.err.println(PM_NOT_RUNNING_ERR);
            return;
        }
        mInstaller = mPm.getPackageInstaller();

        mArgs = args;
        String op = args[0];
        mNextArg = 1;

        if ("list".equals(op)) {
            runList();
            return;
        }

        if ("path".equals(op)) {
            runPath();
            return;
        }

        if ("dump".equals(op)) {
            runDump();
            return;
        }

        if ("install".equals(op)) {
            runInstall();
            return;
        }

        if ("install-create".equals(op)) {
            runInstallCreate();
            return;
        }

        if ("install-write".equals(op)) {
            runInstallWrite();
            return;
        }

        if ("install-commit".equals(op)) {
            runInstallCommit();
            return;
        }

        if ("install-abandon".equals(op) || "install-destroy".equals(op)) {
            runInstallAbandon();
            return;
        }

        if ("set-installer".equals(op)) {
            runSetInstaller();
            return;
        }

        if ("uninstall".equals(op)) {
            runUninstall();
            return;
        }

        if ("clear".equals(op)) {
            runClear();
            return;
        }

        if ("enable".equals(op)) {
            runSetEnabledSetting(PackageManager.COMPONENT_ENABLED_STATE_ENABLED);
            return;
        }

        if ("disable".equals(op)) {
            runSetEnabledSetting(PackageManager.COMPONENT_ENABLED_STATE_DISABLED);
            return;
        }

        if ("disable-user".equals(op)) {
            runSetEnabledSetting(PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER);
            return;
        }

        if ("disable-until-used".equals(op)) {
            runSetEnabledSetting(PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED);
            return;
        }

        if ("hide".equals(op)) {
            runSetHiddenSetting(true);
            return;
        }

        if ("unhide".equals(op)) {
            runSetHiddenSetting(false);
            return;
        }

        if ("grant".equals(op)) {
            runGrantRevokePermission(true);
            return;
        }

        if ("revoke".equals(op)) {
            runGrantRevokePermission(false);
            return;
        }

        if ("set-permission-enforced".equals(op)) {
            runSetPermissionEnforced();
            return;
        }

        if ("set-install-location".equals(op)) {
            runSetInstallLocation();
            return;
        }

        if ("get-install-location".equals(op)) {
            runGetInstallLocation();
            return;
        }

        if ("trim-caches".equals(op)) {
            runTrimCaches();
            return;
        }

        if ("create-user".equals(op)) {
            runCreateUser();
            return;
        }

        if ("remove-user".equals(op)) {
            runRemoveUser();
            return;
        }

        if ("get-max-users".equals(op)) {
            runGetMaxUsers();
            return;
        }

        if ("force-dex-opt".equals(op)) {
            runForceDexOpt();
            return;
        }

        try {
            if (args.length == 1) {
                if (args[0].equalsIgnoreCase("-l")) {
                    validCommand = true;
                    runListPackages(false);
                } else if (args[0].equalsIgnoreCase("-lf")){
                    validCommand = true;
                    runListPackages(true);
                }
            } else if (args.length == 2) {
                if (args[0].equalsIgnoreCase("-p")) {
                    validCommand = true;
                    displayPackageFilePath(args[1]);
                }
            }
        } finally {
            if (validCommand == false) {
                if (op != null) {
                    System.err.println("Error: unknown command '" + op + "'");
                }
                showUsage();
            }
        }
    }
首先判定是否有参数传入,如果没有则显示帮助信息:

if (args.length < 1) {
            showUsage();
            return;
        }
showUsage函数实现如下:

  private static void showUsage() {
        System.err.println("usage: pm list packages [-f] [-d] [-e] [-s] [-3] [-i] [-u] [--user USER_ID] [FILTER]");
        System.err.println("       pm list permission-groups");
        System.err.println("       pm list permissions [-g] [-f] [-d] [-u] [GROUP]");
        System.err.println("       pm list instrumentation [-f] [TARGET-PACKAGE]");
        System.err.println("       pm list features");
通过System.err.println函数在中断输出打印信息。

当输入list命令时,会走到runList函数,函数实现如下:

    /**
     * Execute the list sub-command.
     *
     * pm list [package | packages]
     * pm list permission-groups
     * pm list permissions
     * pm list features
     * pm list libraries
     * pm list instrumentation
     */
    private void runList() {
        String type = nextArg();
        if (type == null) {
            System.err.println("Error: didn't specify type of data to list");
            return;
        }
        if ("package".equals(type) || "packages".equals(type)) {
            runListPackages(false);
        } else if ("permission-groups".equals(type)) {
            runListPermissionGroups();
        } else if ("permissions".equals(type)) {
            runListPermissions();
        } else if ("features".equals(type)) {
            runListFeatures();
        } else if ("libraries".equals(type)) {
            runListLibraries();
        } else if ("instrumentation".equals(type)) {
            runListInstrumentation();
        } else if ("users".equals(type)) {
            runListUsers();
        } else {
            System.err.println("Error: unknown list type '" + type + "'");
        }
    }
nextArg()函数获取下一个参数,即第2个参数。我们看的是pm list packages命令,所以看函数runListPackages(false)的实现。

    /**
     * Lists all the installed packages.
     */
    private void runListPackages(boolean showApplicationPackage) {
        int getFlags = 0;
        boolean listDisabled = false, listEnabled = false;
        boolean listSystem = false, listThirdParty = false;
        boolean listInstaller = false;
        int userId = UserHandle.USER_OWNER;
        try {
            String opt; //packages/package后的子操作
            while ((opt=nextOption()) != null) {
                if (opt.equals("-l")) {
                    // old compat
                } else if (opt.equals("-lf")) { //lf或f可以查看app source dir
                    showApplicationPackage = true;
                } else if (opt.equals("-f")) {
                    showApplicationPackage = true;
                } else if (opt.equals("-d")) {
                    listDisabled = true;
                } else if (opt.equals("-e")) {
                    listEnabled = true;
                } else if (opt.equals("-s")) {
                    listSystem = true;
                } else if (opt.equals("-3")) {
                    listThirdParty = true;
                } else if (opt.equals("-i")) {
                    listInstaller = true;
                } else if (opt.equals("--user")) {
                    userId = Integer.parseInt(nextArg());
                } else if (opt.equals("-u")) {
                    getFlags |= PackageManager.GET_UNINSTALLED_PACKAGES;
                } else {
                    System.err.println("Error: Unknown option: " + opt);
                    return;
                }
            }
        } catch (RuntimeException ex) {
            System.err.println("Error: " + ex.toString());
            return;
        }

        String filter = nextArg();

        try {
            final List packages = getInstalledPackages(mPm, getFlags, userId);  //获取所有安装的apps

            int count = packages.size();
            for (int p = 0 ; p < count ; p++) {
                PackageInfo info = packages.get(p);
                if (filter != null && !info.packageName.contains(filter)) {  //packages或package后面的参数是需要查询的PackageName或部分字符
                    continue; //没有符合的继续轮询
                }
                final boolean isSystem =
                        (info.applicationInfo.flags&ApplicationInfo.FLAG_SYSTEM) != 0;
                if ((!listDisabled || !info.applicationInfo.enabled) &&
                        (!listEnabled || info.applicationInfo.enabled) &&
                        (!listSystem || isSystem) &&
                        (!listThirdParty || !isSystem)) {
                    System.out.print("package:");
                    if (showApplicationPackage) { //f或lf会显示app source dir
                        System.out.print(info.applicationInfo.sourceDir); //例如/system/pri-app/Settings.apk
                        System.out.print("=");
                    }
                    System.out.print(info.packageName);//例如com.android.settings,一起就是/system/pri-app/Settings/apk=com.android.setttings
                    if (listInstaller) {
                        System.out.print("  installer=");
                        System.out.print(mPm.getInstallerPackageName(info.packageName));
                    }
                    System.out.println();
                }
            }
        } catch (RemoteException e) {
            System.err.println(e.toString());
            System.err.println(PM_NOT_RUNNING_ERR);
        }
    }
app即Package的信息会封装在PackageInfo,通过PackageManagerService或PackageManager可以获取系统所有已经安装的app,并根据opt或nextargs获取对应的数据。


当输入pm install时,会走到:

 if ("install".equals(op)) {
            runInstall();
            return;
        }
runInstall函数的实现如下:

    private void runInstall() {
        int installFlags = 0;
        int userId = UserHandle.USER_ALL;
        String installerPackageName = null;

        String opt;

        String originatingUriString = null;
        String referrer = null;
        String abi = null;

        while ((opt=nextOption()) != null) {
            if (opt.equals("-l")) {  //pm install -l
                installFlags |= PackageManager.INSTALL_FORWARD_LOCK;
            } else if (opt.equals("-r")) { // pm install -r 先卸载再安装
                installFlags |= PackageManager.INSTALL_REPLACE_EXISTING;
            } else if (opt.equals("-i")) {
                installerPackageName = nextOptionData();
                if (installerPackageName == null) {
                    System.err.println("Error: no value specified for -i");
                    return;
                }
            } else if (opt.equals("-t")) {
                installFlags |= PackageManager.INSTALL_ALLOW_TEST;
            } else if (opt.equals("-s")) {  //安装到sd卡
                // Override if -s option is specified.
                installFlags |= PackageManager.INSTALL_EXTERNAL;
            } else if (opt.equals("-f")) {
                // Override if -s option is specified.
                installFlags |= PackageManager.INSTALL_INTERNAL;
            } else if (opt.equals("-d")) {
                installFlags |= PackageManager.INSTALL_ALLOW_DOWNGRADE;
            } else if (opt.equals("--originating-uri")) {
                originatingUriString = nextOptionData();
                if (originatingUriString == null) {
                    System.err.println("Error: must supply argument for --originating-uri");
                    return;
                }
            } else if (opt.equals("--referrer")) {
                referrer = nextOptionData();
                if (referrer == null) {
                    System.err.println("Error: must supply argument for --referrer");
                    return;
                }
            } else if (opt.equals("--abi")) {
                abi = checkAbiArgument(nextOptionData());
            } else if (opt.equals("--user")) {
                userId = Integer.parseInt(nextOptionData());
            } else {
                System.err.println("Error: Unknown option: " + opt);
                return;
            }
        }

        if (userId == UserHandle.USER_ALL) {
            userId = UserHandle.USER_OWNER;
            installFlags |= PackageManager.INSTALL_ALL_USERS;
        }

        final Uri verificationURI;
        final Uri originatingURI;
        final Uri referrerURI;

        if (originatingUriString != null) {
            originatingURI = Uri.parse(originatingUriString);
        } else {
            originatingURI = null;
        }

        if (referrer != null) {
            referrerURI = Uri.parse(referrer);
        } else {
            referrerURI = null;
        }

        // Populate apkURI, must be present
        final String apkFilePath = nextArg();
        System.err.println("\tpkg: " + apkFilePath);
        if (apkFilePath == null) {
            System.err.println("Error: no package specified");
            return;
        }

        // Populate verificationURI, optionally present
        final String verificationFilePath = nextArg();
        if (verificationFilePath != null) {
            System.err.println("\tver: " + verificationFilePath);
            verificationURI = Uri.fromFile(new File(verificationFilePath));
        } else {
            verificationURI = null;
        }
        //LocalPackageInstallObserver实现PackageInstallObserver中的onPackageInstalled函数
        LocalPackageInstallObserver obs = new LocalPackageInstallObserver();
        try { //带校验安装,封装校验参数
            VerificationParams verificationParams = new VerificationParams(verificationURI,
                    originatingURI, referrerURI, VerificationParams.NO_UID, null);
             //开始安装
            mPm.installPackageAsUser(apkFilePath, obs.getBinder(), installFlags,  //installFlags即前面的-s,-r等opt
                    installerPackageName, verificationParams, abi, userId);

            synchronized (obs) {
                while (!obs.finished) { 
                    try {
                        obs.wait();  //没有走到onPackageInstalled函数,继续等待,走到后notifyAll唤醒
                    } catch (InterruptedException e) {
                    }
                }
                if (obs.result == PackageManager.INSTALL_SUCCEEDED) {  //安装成功
                    System.out.println("Success");
                } else {
                    System.err.println("Failure ["
                            + installFailureToString(obs)
                            + "]");
                }
            }
        } catch (RemoteException e) {
            System.err.println(e.toString());
            System.err.println(PM_NOT_RUNNING_ERR);
        }
    }

总结:

需要学习的是google实现pm cmd的设计思想以及对PackageManagerService函数的熟悉程度。


你可能感兴趣的:(android移动开发)