Pm.java revision 24713907fe4632d263aea82f7a35c8fb08918a09
1/* 2 * Copyright (C) 2007 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.commands.pm; 18 19import com.android.internal.content.PackageHelper; 20 21import android.app.ActivityManagerNative; 22import android.content.ComponentName; 23import android.content.pm.ApplicationInfo; 24import android.content.pm.ContainerEncryptionParams; 25import android.content.pm.FeatureInfo; 26import android.content.pm.IPackageDataObserver; 27import android.content.pm.IPackageDeleteObserver; 28import android.content.pm.IPackageInstallObserver; 29import android.content.pm.IPackageManager; 30import android.content.pm.InstrumentationInfo; 31import android.content.pm.PackageInfo; 32import android.content.pm.PackageItemInfo; 33import android.content.pm.PackageManager; 34import android.content.pm.ParceledListSlice; 35import android.content.pm.PermissionGroupInfo; 36import android.content.pm.PermissionInfo; 37import android.content.pm.UserInfo; 38import android.content.res.AssetManager; 39import android.content.res.Resources; 40import android.net.Uri; 41import android.os.Binder; 42import android.os.Process; 43import android.os.RemoteException; 44import android.os.ServiceManager; 45 46import java.io.File; 47import java.lang.reflect.Field; 48import java.lang.reflect.Modifier; 49import java.security.InvalidAlgorithmParameterException; 50import java.util.ArrayList; 51import java.util.Collections; 52import java.util.Comparator; 53import java.util.List; 54import java.util.WeakHashMap; 55 56import javax.crypto.SecretKey; 57import javax.crypto.spec.IvParameterSpec; 58import javax.crypto.spec.SecretKeySpec; 59 60public final class Pm { 61 IPackageManager mPm; 62 63 private WeakHashMap<String, Resources> mResourceCache 64 = new WeakHashMap<String, Resources>(); 65 66 private String[] mArgs; 67 private int mNextArg; 68 private String mCurArgData; 69 70 private static final String PM_NOT_RUNNING_ERR = 71 "Error: Could not access the Package Manager. Is the system running?"; 72 private static final int ROOT_UID = 0; 73 74 public static void main(String[] args) { 75 new Pm().run(args); 76 } 77 78 public void run(String[] args) { 79 boolean validCommand = false; 80 if (args.length < 1) { 81 showUsage(); 82 return; 83 } 84 85 mPm = IPackageManager.Stub.asInterface(ServiceManager.getService("package")); 86 if (mPm == null) { 87 System.err.println(PM_NOT_RUNNING_ERR); 88 return; 89 } 90 91 mArgs = args; 92 String op = args[0]; 93 mNextArg = 1; 94 95 if ("list".equals(op)) { 96 runList(); 97 return; 98 } 99 100 if ("path".equals(op)) { 101 runPath(); 102 return; 103 } 104 105 if ("install".equals(op)) { 106 runInstall(); 107 return; 108 } 109 110 if ("uninstall".equals(op)) { 111 runUninstall(); 112 return; 113 } 114 115 if ("clear".equals(op)) { 116 runClear(); 117 return; 118 } 119 120 if ("enable".equals(op)) { 121 runSetEnabledSetting(PackageManager.COMPONENT_ENABLED_STATE_ENABLED); 122 return; 123 } 124 125 if ("disable".equals(op)) { 126 runSetEnabledSetting(PackageManager.COMPONENT_ENABLED_STATE_DISABLED); 127 return; 128 } 129 130 if ("disable-user".equals(op)) { 131 runSetEnabledSetting(PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER); 132 return; 133 } 134 135 if ("grant".equals(op)) { 136 runGrantRevokePermission(true); 137 return; 138 } 139 140 if ("revoke".equals(op)) { 141 runGrantRevokePermission(false); 142 return; 143 } 144 145 if ("set-permission-enforced".equals(op)) { 146 runSetPermissionEnforced(); 147 return; 148 } 149 150 if ("set-install-location".equals(op)) { 151 runSetInstallLocation(); 152 return; 153 } 154 155 if ("get-install-location".equals(op)) { 156 runGetInstallLocation(); 157 return; 158 } 159 160 if ("trim-caches".equals(op)) { 161 runTrimCaches(); 162 return; 163 } 164 165 if ("create-user".equals(op)) { 166 runCreateUser(); 167 return; 168 } 169 170 if ("remove-user".equals(op)) { 171 runRemoveUser(); 172 return; 173 } 174 175 if ("list-users".equals(op)) { 176 runListUsers(); 177 return; 178 } 179 180 try { 181 if (args.length == 1) { 182 if (args[0].equalsIgnoreCase("-l")) { 183 validCommand = true; 184 runListPackages(false); 185 } else if (args[0].equalsIgnoreCase("-lf")){ 186 validCommand = true; 187 runListPackages(true); 188 } 189 } else if (args.length == 2) { 190 if (args[0].equalsIgnoreCase("-p")) { 191 validCommand = true; 192 displayPackageFilePath(args[1]); 193 } 194 } 195 } finally { 196 if (validCommand == false) { 197 if (op != null) { 198 System.err.println("Error: unknown command '" + op + "'"); 199 } 200 showUsage(); 201 } 202 } 203 } 204 205 /** 206 * Execute the list sub-command. 207 * 208 * pm list [package | packages] 209 * pm list permission-groups 210 * pm list permissions 211 * pm list features 212 * pm list libraries 213 * pm list instrumentation 214 */ 215 private void runList() { 216 String type = nextArg(); 217 if (type == null) { 218 System.err.println("Error: didn't specify type of data to list"); 219 showUsage(); 220 return; 221 } 222 if ("package".equals(type) || "packages".equals(type)) { 223 runListPackages(false); 224 } else if ("permission-groups".equals(type)) { 225 runListPermissionGroups(); 226 } else if ("permissions".equals(type)) { 227 runListPermissions(); 228 } else if ("features".equals(type)) { 229 runListFeatures(); 230 } else if ("libraries".equals(type)) { 231 runListLibraries(); 232 } else if ("instrumentation".equals(type)) { 233 runListInstrumentation(); 234 } else if ("users".equals(type)) { 235 runListUsers(); 236 } else { 237 System.err.println("Error: unknown list type '" + type + "'"); 238 showUsage(); 239 } 240 } 241 242 /** 243 * Lists all the installed packages. 244 */ 245 private void runListPackages(boolean showApplicationPackage) { 246 int getFlags = 0; 247 boolean listDisabled = false, listEnabled = false; 248 boolean listSystem = false, listThirdParty = false; 249 boolean listInstaller = false; 250 try { 251 String opt; 252 while ((opt=nextOption()) != null) { 253 if (opt.equals("-l")) { 254 // old compat 255 } else if (opt.equals("-lf")) { 256 showApplicationPackage = true; 257 } else if (opt.equals("-f")) { 258 showApplicationPackage = true; 259 } else if (opt.equals("-d")) { 260 listDisabled = true; 261 } else if (opt.equals("-e")) { 262 listEnabled = true; 263 } else if (opt.equals("-s")) { 264 listSystem = true; 265 } else if (opt.equals("-3")) { 266 listThirdParty = true; 267 } else if (opt.equals("-i")) { 268 listInstaller = true; 269 } else if (opt.equals("-u")) { 270 getFlags |= PackageManager.GET_UNINSTALLED_PACKAGES; 271 } else { 272 System.err.println("Error: Unknown option: " + opt); 273 showUsage(); 274 return; 275 } 276 } 277 } catch (RuntimeException ex) { 278 System.err.println("Error: " + ex.toString()); 279 showUsage(); 280 return; 281 } 282 283 String filter = nextArg(); 284 285 try { 286 final List<PackageInfo> packages = getInstalledPackages(mPm, getFlags); 287 288 int count = packages.size(); 289 for (int p = 0 ; p < count ; p++) { 290 PackageInfo info = packages.get(p); 291 if (filter != null && !info.packageName.contains(filter)) { 292 continue; 293 } 294 final boolean isSystem = 295 (info.applicationInfo.flags&ApplicationInfo.FLAG_SYSTEM) != 0; 296 if ((!listDisabled || !info.applicationInfo.enabled) && 297 (!listEnabled || info.applicationInfo.enabled) && 298 (!listSystem || isSystem) && 299 (!listThirdParty || !isSystem)) { 300 System.out.print("package:"); 301 if (showApplicationPackage) { 302 System.out.print(info.applicationInfo.sourceDir); 303 System.out.print("="); 304 } 305 System.out.print(info.packageName); 306 if (listInstaller) { 307 System.out.print(" installer="); 308 System.out.print(mPm.getInstallerPackageName(info.packageName)); 309 } 310 System.out.println(); 311 } 312 } 313 } catch (RemoteException e) { 314 System.err.println(e.toString()); 315 System.err.println(PM_NOT_RUNNING_ERR); 316 } 317 } 318 319 @SuppressWarnings("unchecked") 320 private List<PackageInfo> getInstalledPackages(IPackageManager pm, int flags) 321 throws RemoteException { 322 final List<PackageInfo> packageInfos = new ArrayList<PackageInfo>(); 323 PackageInfo lastItem = null; 324 ParceledListSlice<PackageInfo> slice; 325 326 do { 327 final String lastKey = lastItem != null ? lastItem.packageName : null; 328 slice = pm.getInstalledPackages(flags, lastKey); 329 lastItem = slice.populateList(packageInfos, PackageInfo.CREATOR); 330 } while (!slice.isLastSlice()); 331 332 return packageInfos; 333 } 334 335 /** 336 * Lists all of the features supported by the current device. 337 * 338 * pm list features 339 */ 340 private void runListFeatures() { 341 try { 342 List<FeatureInfo> list = new ArrayList<FeatureInfo>(); 343 FeatureInfo[] rawList = mPm.getSystemAvailableFeatures(); 344 for (int i=0; i<rawList.length; i++) { 345 list.add(rawList[i]); 346 } 347 348 349 // Sort by name 350 Collections.sort(list, new Comparator<FeatureInfo>() { 351 public int compare(FeatureInfo o1, FeatureInfo o2) { 352 if (o1.name == o2.name) return 0; 353 if (o1.name == null) return -1; 354 if (o2.name == null) return 1; 355 return o1.name.compareTo(o2.name); 356 } 357 }); 358 359 int count = (list != null) ? list.size() : 0; 360 for (int p = 0; p < count; p++) { 361 FeatureInfo fi = list.get(p); 362 System.out.print("feature:"); 363 if (fi.name != null) System.out.println(fi.name); 364 else System.out.println("reqGlEsVersion=0x" 365 + Integer.toHexString(fi.reqGlEsVersion)); 366 } 367 } catch (RemoteException e) { 368 System.err.println(e.toString()); 369 System.err.println(PM_NOT_RUNNING_ERR); 370 } 371 } 372 373 /** 374 * Lists all of the libraries supported by the current device. 375 * 376 * pm list libraries 377 */ 378 private void runListLibraries() { 379 try { 380 List<String> list = new ArrayList<String>(); 381 String[] rawList = mPm.getSystemSharedLibraryNames(); 382 for (int i=0; i<rawList.length; i++) { 383 list.add(rawList[i]); 384 } 385 386 387 // Sort by name 388 Collections.sort(list, new Comparator<String>() { 389 public int compare(String o1, String o2) { 390 if (o1 == o2) return 0; 391 if (o1 == null) return -1; 392 if (o2 == null) return 1; 393 return o1.compareTo(o2); 394 } 395 }); 396 397 int count = (list != null) ? list.size() : 0; 398 for (int p = 0; p < count; p++) { 399 String lib = list.get(p); 400 System.out.print("library:"); 401 System.out.println(lib); 402 } 403 } catch (RemoteException e) { 404 System.err.println(e.toString()); 405 System.err.println(PM_NOT_RUNNING_ERR); 406 } 407 } 408 409 /** 410 * Lists all of the installed instrumentation, or all for a given package 411 * 412 * pm list instrumentation [package] [-f] 413 */ 414 private void runListInstrumentation() { 415 int flags = 0; // flags != 0 is only used to request meta-data 416 boolean showPackage = false; 417 String targetPackage = null; 418 419 try { 420 String opt; 421 while ((opt=nextArg()) != null) { 422 if (opt.equals("-f")) { 423 showPackage = true; 424 } else if (opt.charAt(0) != '-') { 425 targetPackage = opt; 426 } else { 427 System.err.println("Error: Unknown option: " + opt); 428 showUsage(); 429 return; 430 } 431 } 432 } catch (RuntimeException ex) { 433 System.err.println("Error: " + ex.toString()); 434 showUsage(); 435 return; 436 } 437 438 try { 439 List<InstrumentationInfo> list = mPm.queryInstrumentation(targetPackage, flags); 440 441 // Sort by target package 442 Collections.sort(list, new Comparator<InstrumentationInfo>() { 443 public int compare(InstrumentationInfo o1, InstrumentationInfo o2) { 444 return o1.targetPackage.compareTo(o2.targetPackage); 445 } 446 }); 447 448 int count = (list != null) ? list.size() : 0; 449 for (int p = 0; p < count; p++) { 450 InstrumentationInfo ii = list.get(p); 451 System.out.print("instrumentation:"); 452 if (showPackage) { 453 System.out.print(ii.sourceDir); 454 System.out.print("="); 455 } 456 ComponentName cn = new ComponentName(ii.packageName, ii.name); 457 System.out.print(cn.flattenToShortString()); 458 System.out.print(" (target="); 459 System.out.print(ii.targetPackage); 460 System.out.println(")"); 461 } 462 } catch (RemoteException e) { 463 System.err.println(e.toString()); 464 System.err.println(PM_NOT_RUNNING_ERR); 465 } 466 } 467 468 /** 469 * Lists all the known permission groups. 470 */ 471 private void runListPermissionGroups() { 472 try { 473 List<PermissionGroupInfo> pgs = mPm.getAllPermissionGroups(0); 474 475 int count = pgs.size(); 476 for (int p = 0 ; p < count ; p++) { 477 PermissionGroupInfo pgi = pgs.get(p); 478 System.out.print("permission group:"); 479 System.out.println(pgi.name); 480 } 481 } catch (RemoteException e) { 482 System.err.println(e.toString()); 483 System.err.println(PM_NOT_RUNNING_ERR); 484 } 485 } 486 487 private String loadText(PackageItemInfo pii, int res, CharSequence nonLocalized) { 488 if (nonLocalized != null) { 489 return nonLocalized.toString(); 490 } 491 if (res != 0) { 492 Resources r = getResources(pii); 493 if (r != null) { 494 return r.getString(res); 495 } 496 } 497 return null; 498 } 499 500 /** 501 * Lists all the permissions in a group. 502 */ 503 private void runListPermissions() { 504 try { 505 boolean labels = false; 506 boolean groups = false; 507 boolean userOnly = false; 508 boolean summary = false; 509 boolean dangerousOnly = false; 510 String opt; 511 while ((opt=nextOption()) != null) { 512 if (opt.equals("-f")) { 513 labels = true; 514 } else if (opt.equals("-g")) { 515 groups = true; 516 } else if (opt.equals("-s")) { 517 groups = true; 518 labels = true; 519 summary = true; 520 } else if (opt.equals("-u")) { 521 userOnly = true; 522 } else if (opt.equals("-d")) { 523 dangerousOnly = true; 524 } else { 525 System.err.println("Error: Unknown option: " + opt); 526 showUsage(); 527 return; 528 } 529 } 530 531 String grp = nextOption(); 532 ArrayList<String> groupList = new ArrayList<String>(); 533 if (groups) { 534 List<PermissionGroupInfo> infos = 535 mPm.getAllPermissionGroups(0); 536 for (int i=0; i<infos.size(); i++) { 537 groupList.add(infos.get(i).name); 538 } 539 groupList.add(null); 540 } else { 541 groupList.add(grp); 542 } 543 544 if (dangerousOnly) { 545 System.out.println("Dangerous Permissions:"); 546 System.out.println(""); 547 doListPermissions(groupList, groups, labels, summary, 548 PermissionInfo.PROTECTION_DANGEROUS, 549 PermissionInfo.PROTECTION_DANGEROUS); 550 if (userOnly) { 551 System.out.println("Normal Permissions:"); 552 System.out.println(""); 553 doListPermissions(groupList, groups, labels, summary, 554 PermissionInfo.PROTECTION_NORMAL, 555 PermissionInfo.PROTECTION_NORMAL); 556 } 557 } else if (userOnly) { 558 System.out.println("Dangerous and Normal Permissions:"); 559 System.out.println(""); 560 doListPermissions(groupList, groups, labels, summary, 561 PermissionInfo.PROTECTION_NORMAL, 562 PermissionInfo.PROTECTION_DANGEROUS); 563 } else { 564 System.out.println("All Permissions:"); 565 System.out.println(""); 566 doListPermissions(groupList, groups, labels, summary, 567 -10000, 10000); 568 } 569 } catch (RemoteException e) { 570 System.err.println(e.toString()); 571 System.err.println(PM_NOT_RUNNING_ERR); 572 } 573 } 574 575 private void doListPermissions(ArrayList<String> groupList, 576 boolean groups, boolean labels, boolean summary, 577 int startProtectionLevel, int endProtectionLevel) 578 throws RemoteException { 579 for (int i=0; i<groupList.size(); i++) { 580 String groupName = groupList.get(i); 581 String prefix = ""; 582 if (groups) { 583 if (i > 0) System.out.println(""); 584 if (groupName != null) { 585 PermissionGroupInfo pgi = mPm.getPermissionGroupInfo( 586 groupName, 0); 587 if (summary) { 588 Resources res = getResources(pgi); 589 if (res != null) { 590 System.out.print(loadText(pgi, pgi.labelRes, 591 pgi.nonLocalizedLabel) + ": "); 592 } else { 593 System.out.print(pgi.name + ": "); 594 595 } 596 } else { 597 System.out.println((labels ? "+ " : "") 598 + "group:" + pgi.name); 599 if (labels) { 600 System.out.println(" package:" + pgi.packageName); 601 Resources res = getResources(pgi); 602 if (res != null) { 603 System.out.println(" label:" 604 + loadText(pgi, pgi.labelRes, 605 pgi.nonLocalizedLabel)); 606 System.out.println(" description:" 607 + loadText(pgi, pgi.descriptionRes, 608 pgi.nonLocalizedDescription)); 609 } 610 } 611 } 612 } else { 613 System.out.println(((labels && !summary) 614 ? "+ " : "") + "ungrouped:"); 615 } 616 prefix = " "; 617 } 618 List<PermissionInfo> ps = mPm.queryPermissionsByGroup( 619 groupList.get(i), 0); 620 int count = ps.size(); 621 boolean first = true; 622 for (int p = 0 ; p < count ; p++) { 623 PermissionInfo pi = ps.get(p); 624 if (groups && groupName == null && pi.group != null) { 625 continue; 626 } 627 final int base = pi.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE; 628 if (base < startProtectionLevel 629 || base > endProtectionLevel) { 630 continue; 631 } 632 if (summary) { 633 if (first) { 634 first = false; 635 } else { 636 System.out.print(", "); 637 } 638 Resources res = getResources(pi); 639 if (res != null) { 640 System.out.print(loadText(pi, pi.labelRes, 641 pi.nonLocalizedLabel)); 642 } else { 643 System.out.print(pi.name); 644 } 645 } else { 646 System.out.println(prefix + (labels ? "+ " : "") 647 + "permission:" + pi.name); 648 if (labels) { 649 System.out.println(prefix + " package:" + pi.packageName); 650 Resources res = getResources(pi); 651 if (res != null) { 652 System.out.println(prefix + " label:" 653 + loadText(pi, pi.labelRes, 654 pi.nonLocalizedLabel)); 655 System.out.println(prefix + " description:" 656 + loadText(pi, pi.descriptionRes, 657 pi.nonLocalizedDescription)); 658 } 659 System.out.println(prefix + " protectionLevel:" 660 + PermissionInfo.protectionToString(pi.protectionLevel)); 661 } 662 } 663 } 664 665 if (summary) { 666 System.out.println(""); 667 } 668 } 669 } 670 671 private void runPath() { 672 String pkg = nextArg(); 673 if (pkg == null) { 674 System.err.println("Error: no package specified"); 675 showUsage(); 676 return; 677 } 678 displayPackageFilePath(pkg); 679 } 680 681 class PackageInstallObserver extends IPackageInstallObserver.Stub { 682 boolean finished; 683 int result; 684 685 public void packageInstalled(String name, int status) { 686 synchronized( this) { 687 finished = true; 688 result = status; 689 notifyAll(); 690 } 691 } 692 } 693 694 /** 695 * Converts a failure code into a string by using reflection to find a matching constant 696 * in PackageManager. 697 */ 698 private String installFailureToString(int result) { 699 Field[] fields = PackageManager.class.getFields(); 700 for (Field f: fields) { 701 if (f.getType() == int.class) { 702 int modifiers = f.getModifiers(); 703 // only look at public final static fields. 704 if (((modifiers & Modifier.FINAL) != 0) && 705 ((modifiers & Modifier.PUBLIC) != 0) && 706 ((modifiers & Modifier.STATIC) != 0)) { 707 String fieldName = f.getName(); 708 if (fieldName.startsWith("INSTALL_FAILED_") || 709 fieldName.startsWith("INSTALL_PARSE_FAILED_")) { 710 // get the int value and compare it to result. 711 try { 712 if (result == f.getInt(null)) { 713 return fieldName; 714 } 715 } catch (IllegalAccessException e) { 716 // this shouldn't happen since we only look for public static fields. 717 } 718 } 719 } 720 } 721 } 722 723 // couldn't find a matching constant? return the value 724 return Integer.toString(result); 725 } 726 727 private void runSetInstallLocation() { 728 int loc; 729 730 String arg = nextArg(); 731 if (arg == null) { 732 System.err.println("Error: no install location specified."); 733 showUsage(); 734 return; 735 } 736 try { 737 loc = Integer.parseInt(arg); 738 } catch (NumberFormatException e) { 739 System.err.println("Error: install location has to be a number."); 740 showUsage(); 741 return; 742 } 743 try { 744 if (!mPm.setInstallLocation(loc)) { 745 System.err.println("Error: install location has to be a number."); 746 showUsage(); 747 } 748 } catch (RemoteException e) { 749 System.err.println(e.toString()); 750 System.err.println(PM_NOT_RUNNING_ERR); 751 } 752 } 753 754 private void runGetInstallLocation() { 755 try { 756 int loc = mPm.getInstallLocation(); 757 String locStr = "invalid"; 758 if (loc == PackageHelper.APP_INSTALL_AUTO) { 759 locStr = "auto"; 760 } else if (loc == PackageHelper.APP_INSTALL_INTERNAL) { 761 locStr = "internal"; 762 } else if (loc == PackageHelper.APP_INSTALL_EXTERNAL) { 763 locStr = "external"; 764 } 765 System.out.println(loc + "[" + locStr + "]"); 766 } catch (RemoteException e) { 767 System.err.println(e.toString()); 768 System.err.println(PM_NOT_RUNNING_ERR); 769 } 770 } 771 772 private void runInstall() { 773 int installFlags = 0; 774 String installerPackageName = null; 775 776 String opt; 777 778 String algo = null; 779 byte[] iv = null; 780 byte[] key = null; 781 782 String macAlgo = null; 783 byte[] macKey = null; 784 byte[] tag = null; 785 String referrer = null; 786 787 while ((opt=nextOption()) != null) { 788 if (opt.equals("-l")) { 789 installFlags |= PackageManager.INSTALL_FORWARD_LOCK; 790 } else if (opt.equals("-r")) { 791 installFlags |= PackageManager.INSTALL_REPLACE_EXISTING; 792 } else if (opt.equals("-i")) { 793 installerPackageName = nextOptionData(); 794 if (installerPackageName == null) { 795 System.err.println("Error: no value specified for -i"); 796 showUsage(); 797 return; 798 } 799 } else if (opt.equals("-t")) { 800 installFlags |= PackageManager.INSTALL_ALLOW_TEST; 801 } else if (opt.equals("-s")) { 802 // Override if -s option is specified. 803 installFlags |= PackageManager.INSTALL_EXTERNAL; 804 } else if (opt.equals("-f")) { 805 // Override if -s option is specified. 806 installFlags |= PackageManager.INSTALL_INTERNAL; 807 } else if (opt.equals("--algo")) { 808 algo = nextOptionData(); 809 if (algo == null) { 810 System.err.println("Error: must supply argument for --algo"); 811 showUsage(); 812 return; 813 } 814 } else if (opt.equals("--iv")) { 815 iv = hexToBytes(nextOptionData()); 816 if (iv == null) { 817 System.err.println("Error: must supply argument for --iv"); 818 showUsage(); 819 return; 820 } 821 } else if (opt.equals("--key")) { 822 key = hexToBytes(nextOptionData()); 823 if (key == null) { 824 System.err.println("Error: must supply argument for --key"); 825 showUsage(); 826 return; 827 } 828 } else if (opt.equals("--macalgo")) { 829 macAlgo = nextOptionData(); 830 if (macAlgo == null) { 831 System.err.println("Error: must supply argument for --macalgo"); 832 showUsage(); 833 return; 834 } 835 } else if (opt.equals("--mackey")) { 836 macKey = hexToBytes(nextOptionData()); 837 if (macKey == null) { 838 System.err.println("Error: must supply argument for --mackey"); 839 showUsage(); 840 return; 841 } 842 } else if (opt.equals("--tag")) { 843 tag = hexToBytes(nextOptionData()); 844 if (tag == null) { 845 System.err.println("Error: must supply argument for --tag"); 846 showUsage(); 847 return; 848 } 849 } else if (opt.equals("--referrer")) { 850 referrer = nextOptionData(); 851 if (referrer == null) { 852 System.err.println("Error: must supply argument for --referrer"); 853 showUsage(); 854 return; 855 } 856 } else { 857 System.err.println("Error: Unknown option: " + opt); 858 showUsage(); 859 return; 860 } 861 } 862 863 final ContainerEncryptionParams encryptionParams; 864 if (algo != null || iv != null || key != null || macAlgo != null || macKey != null 865 || tag != null) { 866 if (algo == null || iv == null || key == null) { 867 System.err.println("Error: all of --algo, --iv, and --key must be specified"); 868 showUsage(); 869 return; 870 } 871 872 if (macAlgo != null || macKey != null || tag != null) { 873 if (macAlgo == null || macKey == null || tag == null) { 874 System.err.println("Error: all of --macalgo, --mackey, and --tag must " 875 + "be specified"); 876 showUsage(); 877 return; 878 } 879 } 880 881 try { 882 final SecretKey encKey = new SecretKeySpec(key, "RAW"); 883 884 final SecretKey macSecretKey; 885 if (macKey == null || macKey.length == 0) { 886 macSecretKey = null; 887 } else { 888 macSecretKey = new SecretKeySpec(macKey, "RAW"); 889 } 890 891 encryptionParams = new ContainerEncryptionParams(algo, new IvParameterSpec(iv), 892 encKey, macAlgo, null, macSecretKey, tag, -1, -1, -1); 893 } catch (InvalidAlgorithmParameterException e) { 894 e.printStackTrace(); 895 return; 896 } 897 } else { 898 encryptionParams = null; 899 } 900 901 final Uri apkURI; 902 final Uri verificationURI; 903 final Uri referrerURI; 904 905 if (referrer != null) { 906 referrerURI = Uri.parse(referrer); 907 } else { 908 referrerURI = null; 909 } 910 911 // Populate apkURI, must be present 912 final String apkFilePath = nextArg(); 913 System.err.println("\tpkg: " + apkFilePath); 914 if (apkFilePath != null) { 915 apkURI = Uri.fromFile(new File(apkFilePath)); 916 } else { 917 System.err.println("Error: no package specified"); 918 showUsage(); 919 return; 920 } 921 922 // Populate verificationURI, optionally present 923 final String verificationFilePath = nextArg(); 924 if (verificationFilePath != null) { 925 System.err.println("\tver: " + verificationFilePath); 926 verificationURI = Uri.fromFile(new File(verificationFilePath)); 927 } else { 928 verificationURI = null; 929 } 930 931 PackageInstallObserver obs = new PackageInstallObserver(); 932 try { 933 mPm.installPackageWithVerification(apkURI, obs, installFlags, installerPackageName, 934 verificationURI, null, encryptionParams, apkURI, referrerURI); 935 936 synchronized (obs) { 937 while (!obs.finished) { 938 try { 939 obs.wait(); 940 } catch (InterruptedException e) { 941 } 942 } 943 if (obs.result == PackageManager.INSTALL_SUCCEEDED) { 944 System.out.println("Success"); 945 } else { 946 System.err.println("Failure [" 947 + installFailureToString(obs.result) 948 + "]"); 949 } 950 } 951 } catch (RemoteException e) { 952 System.err.println(e.toString()); 953 System.err.println(PM_NOT_RUNNING_ERR); 954 } 955 } 956 957 /** 958 * Convert a string containing hex-encoded bytes to a byte array. 959 * 960 * @param input String containing hex-encoded bytes 961 * @return input as an array of bytes 962 */ 963 private byte[] hexToBytes(String input) { 964 if (input == null) { 965 return null; 966 } 967 968 final int inputLength = input.length(); 969 if ((inputLength % 2) != 0) { 970 System.err.print("Invalid length; must be multiple of 2"); 971 return null; 972 } 973 974 final int byteLength = inputLength / 2; 975 final byte[] output = new byte[byteLength]; 976 977 int inputIndex = 0; 978 int byteIndex = 0; 979 while (inputIndex < inputLength) { 980 output[byteIndex++] = (byte) Integer.parseInt( 981 input.substring(inputIndex, inputIndex + 2), 16); 982 inputIndex += 2; 983 } 984 985 return output; 986 } 987 988 public void runCreateUser() { 989 // Need to be run as root 990 if (Process.myUid() != ROOT_UID) { 991 System.err.println("Error: create-user must be run as root"); 992 return; 993 } 994 String name; 995 String arg = nextArg(); 996 if (arg == null) { 997 System.err.println("Error: no user name specified."); 998 showUsage(); 999 return; 1000 } 1001 name = arg; 1002 try { 1003 if (mPm.createUser(name, 0) == null) { 1004 System.err.println("Error: couldn't create User."); 1005 showUsage(); 1006 } 1007 } catch (RemoteException e) { 1008 System.err.println(e.toString()); 1009 System.err.println(PM_NOT_RUNNING_ERR); 1010 } 1011 1012 } 1013 1014 public void runRemoveUser() { 1015 // Need to be run as root 1016 if (Process.myUid() != ROOT_UID) { 1017 System.err.println("Error: remove-user must be run as root"); 1018 return; 1019 } 1020 int userId; 1021 String arg = nextArg(); 1022 if (arg == null) { 1023 System.err.println("Error: no user id specified."); 1024 showUsage(); 1025 return; 1026 } 1027 try { 1028 userId = Integer.parseInt(arg); 1029 } catch (NumberFormatException e) { 1030 System.err.println("Error: user id has to be a number."); 1031 showUsage(); 1032 return; 1033 } 1034 try { 1035 if (!mPm.removeUser(userId)) { 1036 System.err.println("Error: couldn't remove user."); 1037 showUsage(); 1038 } 1039 } catch (RemoteException e) { 1040 System.err.println(e.toString()); 1041 System.err.println(PM_NOT_RUNNING_ERR); 1042 } 1043 } 1044 1045 public void runListUsers() { 1046 // Need to be run as root 1047 if (Process.myUid() != ROOT_UID) { 1048 System.err.println("Error: list-users must be run as root"); 1049 return; 1050 } 1051 try { 1052 List<UserInfo> users = mPm.getUsers(); 1053 if (users == null) { 1054 System.err.println("Error: couldn't get users"); 1055 } else { 1056 System.out.println("Users:"); 1057 for (int i = 0; i < users.size(); i++) { 1058 System.out.println("\t" + users.get(i).toString()); 1059 } 1060 } 1061 } catch (RemoteException e) { 1062 System.err.println(e.toString()); 1063 System.err.println(PM_NOT_RUNNING_ERR); 1064 } 1065 } 1066 class PackageDeleteObserver extends IPackageDeleteObserver.Stub { 1067 boolean finished; 1068 boolean result; 1069 1070 public void packageDeleted(String packageName, int returnCode) { 1071 synchronized (this) { 1072 finished = true; 1073 result = returnCode == PackageManager.DELETE_SUCCEEDED; 1074 notifyAll(); 1075 } 1076 } 1077 } 1078 1079 private void runUninstall() { 1080 int unInstallFlags = 0; 1081 1082 String opt = nextOption(); 1083 if (opt != null && opt.equals("-k")) { 1084 unInstallFlags = PackageManager.DONT_DELETE_DATA; 1085 } 1086 1087 String pkg = nextArg(); 1088 if (pkg == null) { 1089 System.err.println("Error: no package specified"); 1090 showUsage(); 1091 return; 1092 } 1093 boolean result = deletePackage(pkg, unInstallFlags); 1094 if (result) { 1095 System.out.println("Success"); 1096 } else { 1097 System.out.println("Failure"); 1098 } 1099 } 1100 1101 private boolean deletePackage(String pkg, int unInstallFlags) { 1102 PackageDeleteObserver obs = new PackageDeleteObserver(); 1103 try { 1104 mPm.deletePackage(pkg, obs, unInstallFlags); 1105 1106 synchronized (obs) { 1107 while (!obs.finished) { 1108 try { 1109 obs.wait(); 1110 } catch (InterruptedException e) { 1111 } 1112 } 1113 } 1114 } catch (RemoteException e) { 1115 System.err.println(e.toString()); 1116 System.err.println(PM_NOT_RUNNING_ERR); 1117 } 1118 return obs.result; 1119 } 1120 1121 static class ClearDataObserver extends IPackageDataObserver.Stub { 1122 boolean finished; 1123 boolean result; 1124 1125 @Override 1126 public void onRemoveCompleted(String packageName, boolean succeeded) throws RemoteException { 1127 synchronized (this) { 1128 finished = true; 1129 result = succeeded; 1130 notifyAll(); 1131 } 1132 } 1133 1134 } 1135 1136 private void runClear() { 1137 String pkg = nextArg(); 1138 if (pkg == null) { 1139 System.err.println("Error: no package specified"); 1140 showUsage(); 1141 return; 1142 } 1143 1144 ClearDataObserver obs = new ClearDataObserver(); 1145 try { 1146 if (!ActivityManagerNative.getDefault().clearApplicationUserData(pkg, obs, 1147 Binder.getOrigCallingUser())) { 1148 System.err.println("Failed"); 1149 } 1150 1151 synchronized (obs) { 1152 while (!obs.finished) { 1153 try { 1154 obs.wait(); 1155 } catch (InterruptedException e) { 1156 } 1157 } 1158 } 1159 1160 if (obs.result) { 1161 System.err.println("Success"); 1162 } else { 1163 System.err.println("Failed"); 1164 } 1165 } catch (RemoteException e) { 1166 System.err.println(e.toString()); 1167 System.err.println(PM_NOT_RUNNING_ERR); 1168 } 1169 } 1170 1171 private static String enabledSettingToString(int state) { 1172 switch (state) { 1173 case PackageManager.COMPONENT_ENABLED_STATE_DEFAULT: 1174 return "default"; 1175 case PackageManager.COMPONENT_ENABLED_STATE_ENABLED: 1176 return "enabled"; 1177 case PackageManager.COMPONENT_ENABLED_STATE_DISABLED: 1178 return "disabled"; 1179 case PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER: 1180 return "disabled-user"; 1181 } 1182 return "unknown"; 1183 } 1184 1185 private boolean isNumber(String s) { 1186 try { 1187 Integer.parseInt(s); 1188 } catch (NumberFormatException nfe) { 1189 return false; 1190 } 1191 return true; 1192 } 1193 1194 private void runSetEnabledSetting(int state) { 1195 int userId = 0; 1196 String option = nextOption(); 1197 if (option != null && option.equals("--user")) { 1198 String optionData = nextOptionData(); 1199 if (optionData == null || !isNumber(optionData)) { 1200 System.err.println("Error: no USER_ID specified"); 1201 showUsage(); 1202 return; 1203 } else { 1204 userId = Integer.parseInt(optionData); 1205 } 1206 } 1207 1208 String pkg = nextArg(); 1209 if (pkg == null) { 1210 System.err.println("Error: no package or component specified"); 1211 showUsage(); 1212 return; 1213 } 1214 ComponentName cn = ComponentName.unflattenFromString(pkg); 1215 if (cn == null) { 1216 try { 1217 mPm.setApplicationEnabledSetting(pkg, state, 0, userId); 1218 System.err.println("Package " + pkg + " new state: " 1219 + enabledSettingToString( 1220 mPm.getApplicationEnabledSetting(pkg, userId))); 1221 } catch (RemoteException e) { 1222 System.err.println(e.toString()); 1223 System.err.println(PM_NOT_RUNNING_ERR); 1224 } 1225 } else { 1226 try { 1227 mPm.setComponentEnabledSetting(cn, state, 0, userId); 1228 System.err.println("Component " + cn.toShortString() + " new state: " 1229 + enabledSettingToString( 1230 mPm.getComponentEnabledSetting(cn, userId))); 1231 } catch (RemoteException e) { 1232 System.err.println(e.toString()); 1233 System.err.println(PM_NOT_RUNNING_ERR); 1234 } 1235 } 1236 } 1237 1238 private void runGrantRevokePermission(boolean grant) { 1239 String pkg = nextArg(); 1240 if (pkg == null) { 1241 System.err.println("Error: no package specified"); 1242 showUsage(); 1243 return; 1244 } 1245 String perm = nextArg(); 1246 if (perm == null) { 1247 System.err.println("Error: no permission specified"); 1248 showUsage(); 1249 return; 1250 } 1251 try { 1252 if (grant) { 1253 mPm.grantPermission(pkg, perm); 1254 } else { 1255 mPm.revokePermission(pkg, perm); 1256 } 1257 } catch (RemoteException e) { 1258 System.err.println(e.toString()); 1259 System.err.println(PM_NOT_RUNNING_ERR); 1260 } catch (IllegalArgumentException e) { 1261 System.err.println("Bad argument: " + e.toString()); 1262 showUsage(); 1263 } catch (SecurityException e) { 1264 System.err.println("Operation not allowed: " + e.toString()); 1265 } 1266 } 1267 1268 private void runSetPermissionEnforced() { 1269 final String permission = nextArg(); 1270 if (permission == null) { 1271 System.err.println("Error: no permission specified"); 1272 showUsage(); 1273 return; 1274 } 1275 final String enforcedRaw = nextArg(); 1276 if (enforcedRaw == null) { 1277 System.err.println("Error: no enforcement specified"); 1278 showUsage(); 1279 return; 1280 } 1281 final boolean enforced = Boolean.parseBoolean(enforcedRaw); 1282 try { 1283 mPm.setPermissionEnforced(permission, enforced); 1284 } catch (RemoteException e) { 1285 System.err.println(e.toString()); 1286 System.err.println(PM_NOT_RUNNING_ERR); 1287 } catch (IllegalArgumentException e) { 1288 System.err.println("Bad argument: " + e.toString()); 1289 showUsage(); 1290 } catch (SecurityException e) { 1291 System.err.println("Operation not allowed: " + e.toString()); 1292 } 1293 } 1294 1295 static class ClearCacheObserver extends IPackageDataObserver.Stub { 1296 boolean finished; 1297 boolean result; 1298 1299 @Override 1300 public void onRemoveCompleted(String packageName, boolean succeeded) throws RemoteException { 1301 synchronized (this) { 1302 finished = true; 1303 result = succeeded; 1304 notifyAll(); 1305 } 1306 } 1307 1308 } 1309 1310 private void runTrimCaches() { 1311 String size = nextArg(); 1312 if (size == null) { 1313 System.err.println("Error: no size specified"); 1314 showUsage(); 1315 return; 1316 } 1317 int len = size.length(); 1318 long multiplier = 1; 1319 if (len > 1) { 1320 char c = size.charAt(len-1); 1321 if (c == 'K' || c == 'k') { 1322 multiplier = 1024L; 1323 } else if (c == 'M' || c == 'm') { 1324 multiplier = 1024L*1024L; 1325 } else if (c == 'G' || c == 'g') { 1326 multiplier = 1024L*1024L*1024L; 1327 } else { 1328 System.err.println("Invalid suffix: " + c); 1329 showUsage(); 1330 return; 1331 } 1332 size = size.substring(0, len-1); 1333 } 1334 long sizeVal; 1335 try { 1336 sizeVal = Long.parseLong(size) * multiplier; 1337 } catch (NumberFormatException e) { 1338 System.err.println("Error: expected number at: " + size); 1339 showUsage(); 1340 return; 1341 } 1342 ClearDataObserver obs = new ClearDataObserver(); 1343 try { 1344 mPm.freeStorageAndNotify(sizeVal, obs); 1345 synchronized (obs) { 1346 while (!obs.finished) { 1347 try { 1348 obs.wait(); 1349 } catch (InterruptedException e) { 1350 } 1351 } 1352 } 1353 } catch (RemoteException e) { 1354 System.err.println(e.toString()); 1355 System.err.println(PM_NOT_RUNNING_ERR); 1356 } catch (IllegalArgumentException e) { 1357 System.err.println("Bad argument: " + e.toString()); 1358 showUsage(); 1359 } catch (SecurityException e) { 1360 System.err.println("Operation not allowed: " + e.toString()); 1361 } 1362 } 1363 1364 /** 1365 * Displays the package file for a package. 1366 * @param pckg 1367 */ 1368 private void displayPackageFilePath(String pckg) { 1369 try { 1370 PackageInfo info = mPm.getPackageInfo(pckg, 0, 0); 1371 if (info != null && info.applicationInfo != null) { 1372 System.out.print("package:"); 1373 System.out.println(info.applicationInfo.sourceDir); 1374 } 1375 } catch (RemoteException e) { 1376 System.err.println(e.toString()); 1377 System.err.println(PM_NOT_RUNNING_ERR); 1378 } 1379 } 1380 1381 private Resources getResources(PackageItemInfo pii) { 1382 Resources res = mResourceCache.get(pii.packageName); 1383 if (res != null) return res; 1384 1385 try { 1386 ApplicationInfo ai = mPm.getApplicationInfo(pii.packageName, 0, 0); 1387 AssetManager am = new AssetManager(); 1388 am.addAssetPath(ai.publicSourceDir); 1389 res = new Resources(am, null, null); 1390 mResourceCache.put(pii.packageName, res); 1391 return res; 1392 } catch (RemoteException e) { 1393 System.err.println(e.toString()); 1394 System.err.println(PM_NOT_RUNNING_ERR); 1395 return null; 1396 } 1397 } 1398 1399 private String nextOption() { 1400 if (mNextArg >= mArgs.length) { 1401 return null; 1402 } 1403 String arg = mArgs[mNextArg]; 1404 if (!arg.startsWith("-")) { 1405 return null; 1406 } 1407 mNextArg++; 1408 if (arg.equals("--")) { 1409 return null; 1410 } 1411 if (arg.length() > 1 && arg.charAt(1) != '-') { 1412 if (arg.length() > 2) { 1413 mCurArgData = arg.substring(2); 1414 return arg.substring(0, 2); 1415 } else { 1416 mCurArgData = null; 1417 return arg; 1418 } 1419 } 1420 mCurArgData = null; 1421 return arg; 1422 } 1423 1424 private String nextOptionData() { 1425 if (mCurArgData != null) { 1426 return mCurArgData; 1427 } 1428 if (mNextArg >= mArgs.length) { 1429 return null; 1430 } 1431 String data = mArgs[mNextArg]; 1432 mNextArg++; 1433 return data; 1434 } 1435 1436 private String nextArg() { 1437 if (mNextArg >= mArgs.length) { 1438 return null; 1439 } 1440 String arg = mArgs[mNextArg]; 1441 mNextArg++; 1442 return arg; 1443 } 1444 1445 private static void showUsage() { 1446 System.err.println("usage: pm list packages [-f] [-d] [-e] [-s] [-3] [-i] [-u] [FILTER]"); 1447 System.err.println(" pm list permission-groups"); 1448 System.err.println(" pm list permissions [-g] [-f] [-d] [-u] [GROUP]"); 1449 System.err.println(" pm list instrumentation [-f] [TARGET-PACKAGE]"); 1450 System.err.println(" pm list features"); 1451 System.err.println(" pm list libraries"); 1452 System.err.println(" pm path PACKAGE"); 1453 System.err.println(" pm install [-l] [-r] [-t] [-i INSTALLER_PACKAGE_NAME] [-s] [-f]"); 1454 System.err.println(" [--algo <algorithm name> --key <key-in-hex> --iv <IV-in-hex>]"); 1455 System.err.println(" [--referrer <URI>] PATH"); 1456 System.err.println(" pm uninstall [-k] PACKAGE"); 1457 System.err.println(" pm clear PACKAGE"); 1458 System.err.println(" pm enable PACKAGE_OR_COMPONENT"); 1459 System.err.println(" pm disable PACKAGE_OR_COMPONENT"); 1460 System.err.println(" pm disable-user PACKAGE_OR_COMPONENT"); 1461 System.err.println(" pm grant PACKAGE PERMISSION"); 1462 System.err.println(" pm revoke PACKAGE PERMISSION"); 1463 System.err.println(" pm set-install-location [0/auto] [1/internal] [2/external]"); 1464 System.err.println(" pm get-install-location"); 1465 System.err.println(" pm set-permission-enforced PERMISSION [true|false]"); 1466 System.err.println(" pm trim-caches DESIRED_FREE_SPACE"); 1467 System.err.println(""); 1468 System.err.println("pm list packages: prints all packages, optionally only"); 1469 System.err.println(" those whose package name contains the text in FILTER. Options:"); 1470 System.err.println(" -f: see their associated file."); 1471 System.err.println(" -d: filter to only show disbled packages."); 1472 System.err.println(" -e: filter to only show enabled packages."); 1473 System.err.println(" -s: filter to only show system packages."); 1474 System.err.println(" -3: filter to only show third party packages."); 1475 System.err.println(" -i: see the installer for the packages."); 1476 System.err.println(" -u: also include uninstalled packages."); 1477 System.err.println(""); 1478 System.err.println("pm list permission-groups: prints all known permission groups."); 1479 System.err.println(""); 1480 System.err.println("pm list permissions: prints all known permissions, optionally only"); 1481 System.err.println(" those in GROUP. Options:"); 1482 System.err.println(" -g: organize by group."); 1483 System.err.println(" -f: print all information."); 1484 System.err.println(" -s: short summary."); 1485 System.err.println(" -d: only list dangerous permissions."); 1486 System.err.println(" -u: list only the permissions users will see."); 1487 System.err.println(""); 1488 System.err.println("pm list instrumentation: use to list all test packages; optionally"); 1489 System.err.println(" supply <TARGET-PACKAGE> to list the test packages for a particular"); 1490 System.err.println(" application. Options:"); 1491 System.err.println(" -f: list the .apk file for the test package."); 1492 System.err.println(""); 1493 System.err.println("pm list features: prints all features of the system."); 1494 System.err.println(""); 1495 System.err.println("pm path: print the path to the .apk of the given PACKAGE."); 1496 System.err.println(""); 1497 System.err.println("pm install: installs a package to the system. Options:"); 1498 System.err.println(" -l: install the package with FORWARD_LOCK."); 1499 System.err.println(" -r: reinstall an exisiting app, keeping its data."); 1500 System.err.println(" -t: allow test .apks to be installed."); 1501 System.err.println(" -i: specify the installer package name."); 1502 System.err.println(" -s: install package on sdcard."); 1503 System.err.println(" -f: install package on internal flash."); 1504 System.err.println(""); 1505 System.err.println("pm uninstall: removes a package from the system. Options:"); 1506 System.err.println(" -k: keep the data and cache directories around after package removal."); 1507 System.err.println(""); 1508 System.err.println("pm clear: deletes all data associated with a package."); 1509 System.err.println(""); 1510 System.err.println("pm enable, disable, disable-user: these commands change the enabled state"); 1511 System.err.println(" of a given package or component (written as \"package/class\")."); 1512 System.err.println(""); 1513 System.err.println("pm grant, revoke: these commands either grant or revoke permissions"); 1514 System.err.println(" to applications. Only optional permissions the application has"); 1515 System.err.println(" declared can be granted or revoked."); 1516 System.err.println(""); 1517 System.err.println("pm get-install-location: returns the current install location."); 1518 System.err.println(" 0 [auto]: Let system decide the best location"); 1519 System.err.println(" 1 [internal]: Install on internal device storage"); 1520 System.err.println(" 2 [external]: Install on external media"); 1521 System.err.println(""); 1522 System.err.println("pm set-install-location: changes the default install location."); 1523 System.err.println(" NOTE: this is only intended for debugging; using this can cause"); 1524 System.err.println(" applications to break and other undersireable behavior."); 1525 System.err.println(" 0 [auto]: Let system decide the best location"); 1526 System.err.println(" 1 [internal]: Install on internal device storage"); 1527 System.err.println(" 2 [external]: Install on external media"); 1528 System.err.println(""); 1529 System.err.println("pm trim-caches: trim cache files to reach the given free space."); 1530 } 1531} 1532