平时在开发调试的时候用pm指令用得蛮多的,pm指令可以列出系统支持的feature、permission和当前已安装的apk等信息。所以今天我们来阅读一下pm指令的代码,做下简单分析。
Android.mk
首先我们来查看一下编译pm指令的Android.mk,看看pm指令包含哪些源码文件。Android.mk文件路劲为frameworks/base/cmds/pm
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
# Copyright 2007 The Android Open Source Project # LOCAL_PATH:= $(call my-dir) # pmlib库由当前文件夹下所有java文件编译得到 include $(CLEAR_VARS) LOCAL_SRC_FILES := $(call all-subdir-java-files) LOCAL_MODULE := pmlib # 用于指定目标文件名为pm.jar LOCAL_MODULE_STEM := pm include $(BUILD_JAVA_LIBRARY) # pm指令使用的src文件是当前目录下的pm include $(CLEAR_VARS) LOCAL_MODULE := pm # pm可执行文件 LOCAL_MODULE_CLASS := EXECUTABLES LOCAL_SRC_FILES := pm # pm指令依赖pmlib LOCAL_REQUIRED_MODULES := pmlib include $(BUILD_PREBUILT) |
pm指令源码
从上面的Android.mk可以了解都pm是一个预编译的可执行文件,文件源码为pm,接下来我们查看该文件的源码
1 2 3 4 5 6 |
# 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 "$@" |
从注释处就可以看出,这是一个shell文件。因为shell在native层执行,所以需要在shell文件中通过app_process来创建一个Java进程,随后执行com.android.commands.pm.Pm包的main方法。接下来我们接着分析com.android.commands.pm.Pm的main方法。
pm.jar
main方法如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
public static void main(String[] args) { int exitCode = 1; try { // 转而调用run方法 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); } |
main方法只是个幌子,真正做事情的是run方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 |
public int run(String[] args) throws RemoteException { boolean validCommand = false; if (args.length < 1) { // 参数个数不对,显示pm的使用方法 return showUsage(); } // 获取AcountManagerService mAm = IAccountManager.Stub.asInterface(ServiceManager.getService(Context.ACCOUNT_SERVICE)); // 获取UserManagerService mUm = IUserManager.Stub.asInterface(ServiceManager.getService(Context.USER_SERVICE)); // 获取PackageManagerService mPm = IPackageManager.Stub.asInterface(ServiceManager.getService("package")); // 获取PackageInstaller if (mPm == null) { System.err.println(PM_NOT_RUNNING_ERR); return 1; } mInstaller = mPm.getPackageInstaller(); mArgs = args; String op = args[0]; mNextArg = 1; // 根据第一个参数判断需要调用的方法 // 列出相关信息 if ("list".equals(op)) { return runList(); } if ("path".equals(op)) { return runPath(); } if ("dump".equals(op)) { return runDump(); } // 安装应用 if ("install".equals(op)) { return runInstall(); } if ("install-create".equals(op)) { return runInstallCreate(); } if ("install-write".equals(op)) { return runInstallWrite(); } if ("install-commit".equals(op)) { return runInstallCommit(); } if ("install-abandon".equals(op) || "install-destroy".equals(op)) { return runInstallAbandon(); } if ("set-installer".equals(op)) { return runSetInstaller(); } // 卸载应用 if ("uninstall".equals(op)) { return runUninstall(); } // 下面还有一堆的指令可以用 ................ // 简化用法,例如: pm -lf和pm list packages -f是一样的 try { if (args.length == 1) { if (args[0].equalsIgnoreCase("-l")) { validCommand = true; return runShellCommand("package", new String[] { "list", "package" }); } else if (args[0].equalsIgnoreCase("-lf")) { validCommand = true; return runShellCommand("package", new String[] { "list", "package", "-f" }); } } else if (args.length == 2) { if (args[0].equalsIgnoreCase("-p")) { validCommand = true; return displayPackageFilePath(args[1], UserHandle.USER_SYSTEM); } } return 1; } finally { if (validCommand == false) { if (op != null) { System.err.println("Error: unknown command '" + op + "'"); } showUsage(); } } } |
pm支持的功能用法很多,如果不清楚可以adb shell连接到手机后直接执行pm列出pm支持的用法。这一篇我们分析先分析pm isntall xxx.apk
和pm list packages -f
方法。
pm install安装apk
从上面run方法可知,pm install方法主要是调用runInstall方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
private int runInstall() throws RemoteException { long startedTime = SystemClock.elapsedRealtime(); // 初始化安装需要使用的参数 final InstallParams params = makeInstallParams(); // 待安装应用的路径 final String inPath = nextArg(); if (params.sessionParams.sizeBytes == -1 && !STDIN_PATH.equals(inPath)) { // 根据文件创建文件解析 File file = new File(inPath); if (file.isFile()) { try { // 解析APK文件,获取相关信息 ApkLite baseApk = PackageParser.parseApkLite(file, 0); PackageLite pkgLite = new PackageLite(null, baseApk, null, null, null, null, null, null); // 计算APK所需空间大小 params.sessionParams.setSize( PackageHelper.calculateInstalledSize(pkgLite, false, params.sessionParams.abiOverride)); } catch (PackageParserException | IOException e) { System.err.println("Error: Failed to parse APK file: " + e); return 1; } } else { System.err.println("Error: Can't open non-file: " + inPath); return 1; } } // doCreateSession会调用Installers的createSession方法来创建SessionID final int sessionId = doCreateSession(params.sessionParams, params.installerPackageName, params.userId); try { if (inPath == null && params.sessionParams.sizeBytes == -1) { System.err.println("Error: must either specify a package size or an APK file"); return 1; } // 这里也和PackageInstaller类似,将Package的信息写入到PackageInstaller的session里面 if (doWriteSession(sessionId, inPath, params.sessionParams.sizeBytes, "base.apk", false /*logSuccess*/) != PackageInstaller.STATUS_SUCCESS) { return 1; } // 这里最后也是调用Session的commit方法,将安装工作交给PackageManagerService来处理 Pair<String, Integer> status = doCommitSession(sessionId, false /*logSuccess*/); if (status.second != PackageInstaller.STATUS_SUCCESS) { return 1; } // 按照成功,打印log Log.i(TAG, "Package " + status.first + " installed in " + (SystemClock.elapsedRealtime() - startedTime) + " ms"); System.out.println("Success"); return 0; } finally { try { mInstaller.abandonSession(sessionId); } catch (Exception ignore) { } } } |
如果有看博主前面对PackageInstaller源码分析的博文的话,对上面部分的代码就比较好了解了。所以这里也不多做解析。
pm list packages -f
list功能走的是runList方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
/** * 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 int runList() { final String type = nextArg(); if ("users".equals(type)) { return runShellCommand("user", new String[] { "list" }); } return runShellCommand("package", mArgs); } |
runList通过调用runShellCommand实现。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
private int runShellCommand(String serviceName, String[] args) { // 消息处理线程 final HandlerThread handlerThread = new HandlerThread("results"); handlerThread.start(); try { // 通过ServiceManager的getService方法获取到对应的service服务 ServiceManager.getService(serviceName).shellCommand( FileDescriptor.in, FileDescriptor.out, FileDescriptor.err, args, new MyShellCallback(), new ResultReceiver(new Handler(handlerThread.getLooper()))); return 0; } catch (RemoteException e) { e.printStackTrace(); } finally { handlerThread.quitSafely(); } return -1; } |
上面通过ServiceManager的getService方法获取到对应的service,从前面的调用可以看到,调用的service是package。博主前面对PackageManagerService的介绍有提及,这个package服务就是PackageManagerService。所以这里应该调用的是PackageManagerService的shellCommand方法。按照binder调用过程列举如下:
- BinderProxy.shellCommand
- BinderProxy.transact
- transactNative
- 通过JNI调用到android_util_Binder.cpp中的android_os_BinderProxy_transact方法
- binder.transact
- binder.shellCommand
- binder.onShellCommand
具体的调用大家可以按照上面的过程查看,这里就省略掉不写了。按照最后的binder.onShellCommand方法,这个方法一般到了具体的service里面,都是会被重载的,所以我们可以直接看PackageManagerService的onShellCommand方法。
1 2 3 4 5 6 7 8 |
@Override public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err, String[] args, ShellCallback callback, ResultReceiver resultReceiver) { // 转而调用PackageManagerShellCommand的exec方法 (new PackageManagerShellCommand(this)).exec( this, in, out, err, args, callback, resultReceiver); } |
onShellCommand方法继承自ShellCommand方法,所以这里调用的是ShellCommand的exec方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
public int exec(Binder target, FileDescriptor in, FileDescriptor out, FileDescriptor err, String[] args, ShellCallback callback, ResultReceiver resultReceiver) { String cmd; int start; if (args != null && args.length > 0) { cmd = args[0]; start = 1; } else { cmd = null; start = 0; } // 初始化参数 init(target, in, out, err, args, callback, start); // 记录参数 mCmd = cmd; mResultReceiver = resultReceiver; if (DEBUG) Slog.d(TAG, "Starting command " + mCmd + " on " + mTarget); int res = -1; try { // 调用onCommand方法 res = onCommand(mCmd); if (DEBUG) Slog.d(TAG, "Executed command " + mCmd + " on " + mTarget); } catch (SecurityException e) { PrintWriter eout = getErrPrintWriter(); eout.println("Security exception: " + e.getMessage()); ............... } |
紧接着调用onCommand方法进行处理。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 |
@Override public int onCommand(String cmd) { if (cmd == null) { return handleDefaultCommands(cmd); } final PrintWriter pw = getOutPrintWriter(); try { switch(cmd) { case "install": return runInstall(); case "install-abandon": case "install-destroy": return runInstallAbandon(); case "install-commit": return runInstallCommit(); case "install-create": return runInstallCreate(); case "install-remove": return runInstallRemove(); case "install-write": return runInstallWrite(); case "install-existing": return runInstallExisting(); case "compile": return runCompile(); case "reconcile-secondary-dex-files": return runreconcileSecondaryDexFiles(); case "bg-dexopt-job": return runDexoptJob(); case "dump-profiles": return runDumpProfiles(); case "list": return runList(); case "uninstall": return runUninstall(); case "resolve-activity": return runResolveActivity(); case "query-activities": return runQueryIntentActivities(); case "query-services": return runQueryIntentServices(); case "query-receivers": return runQueryIntentReceivers(); case "suspend": return runSuspend(true); case "unsuspend": return runSuspend(false); case "set-home-activity": return runSetHomeActivity(); case "get-privapp-permissions": return runGetPrivappPermissions(); case "get-privapp-deny-permissions": return runGetPrivappDenyPermissions(); case "get-instantapp-resolver": return runGetInstantAppResolver(); case "has-feature": return runHasFeature(); default: return handleDefaultCommands(cmd); } } catch (RemoteException e) { pw.println("Remote exception: " + e); } return -1; } |
又是各种判断后调用具体的方法进行处理。这里是runList
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
private int runList() throws RemoteException { final PrintWriter pw = getOutPrintWriter(); final String type = getNextArg(); if (type == null) { pw.println("Error: didn't specify type of data to list"); return -1; } switch(type) { case "features": return runListFeatures(); case "instrumentation": return runListInstrumentation(); case "libraries": return runListLibraries(); case "package": case "packages": return runListPackages(false /*showSourceDir*/); case "permission-groups": return runListPermissionGroups(); case "permissions": return runListPermissions(); } pw.println("Error: unknown list type '" + type + "'"); return -1; } |
紧接着是packages,所以调用runListPackages方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 |
private int runListPackages(boolean showSourceDir) throws RemoteException { final PrintWriter pw = getOutPrintWriter(); int getFlags = 0; boolean listDisabled = false, listEnabled = false; boolean listSystem = false, listThirdParty = false; boolean listInstaller = false; boolean showUid = false; boolean showVersionCode = false; int uid = -1; int userId = UserHandle.USER_SYSTEM; try { String opt; // 对后续选项参数进行解析处理 while ((opt = getNextOption()) != null) { switch (opt) { case "-d": listDisabled = true; break; case "-e": listEnabled = true; break; case "-f": showSourceDir = true; break; case "-i": listInstaller = true; break; case "-l": // old compat break; case "-s": listSystem = true; break; case "-U": showUid = true; break; case "-u": getFlags |= PackageManager.MATCH_UNINSTALLED_PACKAGES; break; case "-3": listThirdParty = true; break; case "--show-versioncode": showVersionCode = true; break; case "--user": userId = UserHandle.parseUserArg(getNextArgRequired()); break; case "--uid": showUid = true; uid = Integer.parseInt(getNextArgRequired()); break; default: pw.println("Error: Unknown option: " + opt); return -1; } } } catch (RuntimeException ex) { pw.println("Error: " + ex.toString()); return -1; } final String filter = getNextArg(); @SuppressWarnings("unchecked") // 调用PackageManagerService的getInstalledPackages方法获取安装的应用列表 final ParceledListSlice<PackageInfo> slice = mInterface.getInstalledPackages(getFlags, userId); final List<PackageInfo> packages = slice.getList(); final int count = packages.size(); // 遍历列表,按照上面的选项要求打印相关的应用信息 for (int p = 0; p < count; p++) { final PackageInfo info = packages.get(p); if (filter != null && !info.packageName.contains(filter)) { continue; } if (uid != -1 && info.applicationInfo.uid != uid) { continue; } final boolean isSystem = (info.applicationInfo.flags&ApplicationInfo.FLAG_SYSTEM) != 0; if ((!listDisabled || !info.applicationInfo.enabled) && (!listEnabled || info.applicationInfo.enabled) && (!listSystem || isSystem) && (!listThirdParty || !isSystem)) { pw.print("package:"); if (showSourceDir) { pw.print(info.applicationInfo.sourceDir); pw.print("="); } pw.print(info.packageName); if (showVersionCode) { pw.print(" versionCode:"); pw.print(info.applicationInfo.versionCode); } if (listInstaller) { pw.print(" installer="); pw.print(mInterface.getInstallerPackageName(info.packageName)); } if (showUid) { pw.print(" uid:"); pw.print(info.applicationInfo.uid); } pw.println(); } } return 0; } |
这篇就到这里了,pm的源码看起来还是比较简单的,但是却也是挺有用的,后续如果需要开发类似的工具,这里很多代码都是可以借鉴的。