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函数的熟悉程度。