PackageManagerShellCommand.java revision bdd30d86ef98456161069d11481b2ccd25a11b4e
1/* 2 * Copyright (C) 2015 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.server.pm; 18 19import android.app.ActivityManager; 20import android.content.ComponentName; 21import android.content.IIntentReceiver; 22import android.content.IIntentSender; 23import android.content.Intent; 24import android.content.IntentSender; 25import android.content.pm.ApplicationInfo; 26import android.content.pm.FeatureInfo; 27import android.content.pm.IPackageManager; 28import android.content.pm.InstrumentationInfo; 29import android.content.pm.PackageInfo; 30import android.content.pm.PackageInstaller; 31import android.content.pm.PackageItemInfo; 32import android.content.pm.PackageManager; 33import android.content.pm.ParceledListSlice; 34import android.content.pm.PermissionGroupInfo; 35import android.content.pm.PermissionInfo; 36import android.content.pm.PackageInstaller.SessionInfo; 37import android.content.pm.PackageInstaller.SessionParams; 38import android.content.pm.ResolveInfo; 39import android.content.res.AssetManager; 40import android.content.res.Resources; 41import android.net.Uri; 42import android.os.Binder; 43import android.os.Build; 44import android.os.Bundle; 45import android.os.RemoteException; 46import android.os.ShellCommand; 47import android.os.SystemProperties; 48import android.os.UserHandle; 49import android.text.TextUtils; 50import android.util.PrintWriterPrinter; 51import com.android.internal.util.SizedInputStream; 52 53import dalvik.system.DexFile; 54 55import libcore.io.IoUtils; 56 57import java.io.File; 58import java.io.FileInputStream; 59import java.io.IOException; 60import java.io.InputStream; 61import java.io.OutputStream; 62import java.io.PrintWriter; 63import java.net.URISyntaxException; 64import java.util.ArrayList; 65import java.util.Collections; 66import java.util.Comparator; 67import java.util.List; 68import java.util.WeakHashMap; 69import java.util.concurrent.SynchronousQueue; 70import java.util.concurrent.TimeUnit; 71 72class PackageManagerShellCommand extends ShellCommand { 73 final IPackageManager mInterface; 74 final private WeakHashMap<String, Resources> mResourceCache = 75 new WeakHashMap<String, Resources>(); 76 int mTargetUser; 77 78 PackageManagerShellCommand(PackageManagerService service) { 79 mInterface = service; 80 } 81 82 @Override 83 public int onCommand(String cmd) { 84 if (cmd == null) { 85 return handleDefaultCommands(cmd); 86 } 87 88 final PrintWriter pw = getOutPrintWriter(); 89 try { 90 switch(cmd) { 91 case "install": 92 return runInstall(); 93 case "install-abandon": 94 case "install-destroy": 95 return runInstallAbandon(); 96 case "install-commit": 97 return runInstallCommit(); 98 case "install-create": 99 return runInstallCreate(); 100 case "install-remove": 101 return runInstallRemove(); 102 case "install-write": 103 return runInstallWrite(); 104 case "compile": 105 return runCompile(); 106 case "list": 107 return runList(); 108 case "uninstall": 109 return runUninstall(); 110 case "resolve-activity": 111 return runResolveActivity(); 112 case "query-activities": 113 return runQueryIntentActivities(); 114 case "query-services": 115 return runQueryIntentServices(); 116 case "query-receivers": 117 return runQueryIntentReceivers(); 118 case "suspend": 119 return runSuspend(true); 120 case "unsuspend": 121 return runSuspend(false); 122 case "set-home-activity": 123 return runSetHomeActivity(); 124 default: 125 return handleDefaultCommands(cmd); 126 } 127 } catch (RemoteException e) { 128 pw.println("Remote exception: " + e); 129 } 130 return -1; 131 } 132 133 private int runInstall() throws RemoteException { 134 final PrintWriter pw = getOutPrintWriter(); 135 final InstallParams params = makeInstallParams(); 136 final int sessionId = doCreateSession(params.sessionParams, 137 params.installerPackageName, params.userId); 138 boolean abandonSession = true; 139 try { 140 final String inPath = getNextArg(); 141 if (inPath == null && params.sessionParams.sizeBytes == 0) { 142 pw.println("Error: must either specify a package size or an APK file"); 143 return 1; 144 } 145 if (doWriteSplit(sessionId, inPath, params.sessionParams.sizeBytes, "base.apk", 146 false /*logSuccess*/) != PackageInstaller.STATUS_SUCCESS) { 147 return 1; 148 } 149 if (doCommitSession(sessionId, false /*logSuccess*/) 150 != PackageInstaller.STATUS_SUCCESS) { 151 return 1; 152 } 153 abandonSession = false; 154 pw.println("Success"); 155 return 0; 156 } finally { 157 if (abandonSession) { 158 try { 159 doAbandonSession(sessionId, false /*logSuccess*/); 160 } catch (Exception ignore) { 161 } 162 } 163 } 164 } 165 166 private int runSuspend(boolean suspendedState) { 167 final PrintWriter pw = getOutPrintWriter(); 168 int userId = UserHandle.USER_SYSTEM; 169 String opt; 170 while ((opt = getNextOption()) != null) { 171 switch (opt) { 172 case "--user": 173 userId = UserHandle.parseUserArg(getNextArgRequired()); 174 break; 175 default: 176 pw.println("Error: Unknown option: " + opt); 177 return 1; 178 } 179 } 180 181 String packageName = getNextArg(); 182 if (packageName == null) { 183 pw.println("Error: package name not specified"); 184 return 1; 185 } 186 187 try { 188 mInterface.setPackagesSuspendedAsUser(new String[]{packageName}, suspendedState, 189 userId); 190 pw.println("Package " + packageName + " new suspended state: " 191 + mInterface.isPackageSuspendedForUser(packageName, userId)); 192 return 0; 193 } catch (RemoteException e) { 194 pw.println(e.toString()); 195 return 1; 196 } 197 } 198 199 private int runInstallAbandon() throws RemoteException { 200 final int sessionId = Integer.parseInt(getNextArg()); 201 return doAbandonSession(sessionId, true /*logSuccess*/); 202 } 203 204 private int runInstallCommit() throws RemoteException { 205 final int sessionId = Integer.parseInt(getNextArg()); 206 return doCommitSession(sessionId, true /*logSuccess*/); 207 } 208 209 private int runInstallCreate() throws RemoteException { 210 final PrintWriter pw = getOutPrintWriter(); 211 final InstallParams installParams = makeInstallParams(); 212 final int sessionId = doCreateSession(installParams.sessionParams, 213 installParams.installerPackageName, installParams.userId); 214 215 // NOTE: adb depends on parsing this string 216 pw.println("Success: created install session [" + sessionId + "]"); 217 return 0; 218 } 219 220 private int runInstallWrite() throws RemoteException { 221 long sizeBytes = -1; 222 223 String opt; 224 while ((opt = getNextOption()) != null) { 225 if (opt.equals("-S")) { 226 sizeBytes = Long.parseLong(getNextArg()); 227 } else { 228 throw new IllegalArgumentException("Unknown option: " + opt); 229 } 230 } 231 232 final int sessionId = Integer.parseInt(getNextArg()); 233 final String splitName = getNextArg(); 234 final String path = getNextArg(); 235 return doWriteSplit(sessionId, path, sizeBytes, splitName, true /*logSuccess*/); 236 } 237 238 private int runInstallRemove() throws RemoteException { 239 final PrintWriter pw = getOutPrintWriter(); 240 241 final int sessionId = Integer.parseInt(getNextArg()); 242 243 final String splitName = getNextArg(); 244 if (splitName == null) { 245 pw.println("Error: split name not specified"); 246 return 1; 247 } 248 return doRemoveSplit(sessionId, splitName, true /*logSuccess*/); 249 } 250 251 private int runCompile() throws RemoteException { 252 final PrintWriter pw = getOutPrintWriter(); 253 boolean useJitProfiles = false; 254 boolean forceCompilation = false; 255 boolean allPackages = false; 256 boolean clearProfileData = false; 257 String compilerFilter = null; 258 String compilationReason = null; 259 260 if (peekNextArg() == null) { 261 // No arguments, show help. 262 pw.println("Usage: cmd package compile [-c] [-f] [--reset] [-m mode] " + 263 "[-r reason] [-a|pkg]"); 264 pw.println(); 265 pw.println(" -c Clear profile data"); 266 pw.println(" -f Force compilation"); 267 pw.println(" --reset Reset package"); 268 pw.println(" -m mode Compilation mode, one of the dex2oat compiler filters"); 269 pw.println(" verify-none"); 270 pw.println(" verify-at-runtime"); 271 pw.println(" verify-profile"); 272 pw.println(" interpret-only"); 273 pw.println(" space-profile"); 274 pw.println(" space"); 275 pw.println(" speed-profile"); 276 pw.println(" speed"); 277 pw.println(" everything"); 278 pw.println(" -r reason Compiler reason, one of the package manager reasons"); 279 for (int i = 0; i < PackageManagerServiceCompilerMapping.REASON_STRINGS.length; i++) { 280 pw.println(" " + 281 PackageManagerServiceCompilerMapping.REASON_STRINGS[i]); 282 } 283 pw.println(" -a Apply to all packages"); 284 return 1; 285 } 286 287 String opt; 288 while ((opt = getNextOption()) != null) { 289 switch (opt) { 290 case "-a": 291 allPackages = true; 292 break; 293 case "-c": 294 clearProfileData = true; 295 break; 296 case "-f": 297 forceCompilation = true; 298 break; 299 case "-m": 300 compilerFilter = getNextArgRequired(); 301 break; 302 case "-r": 303 compilationReason = getNextArgRequired(); 304 break; 305 case "--reset": 306 forceCompilation = true; 307 clearProfileData = true; 308 compilerFilter = "reset"; 309 break; 310 default: 311 pw.println("Error: Unknown option: " + opt); 312 return 1; 313 } 314 } 315 316 if (compilerFilter != null && compilationReason != null) { 317 pw.println("Cannot use compilation filter (\"-m\") and compilation reason (\"-r\") " + 318 "at the same time"); 319 return 1; 320 } 321 if (compilerFilter == null && compilationReason == null) { 322 pw.println("Cannot run without any of compilation filter (\"-m\") and compilation " + 323 "reason (\"-r\") at the same time"); 324 return 1; 325 } 326 327 String targetCompilerFilter; 328 if (compilerFilter != null) { 329 // Specially recognize default and reset. Otherwise, only accept valid modes. 330 if ("default".equals(compilerFilter)) { 331 // Use the default mode for background dexopt. 332 targetCompilerFilter = 333 PackageManagerServiceCompilerMapping.getCompilerFilterForReason( 334 PackageManagerServiceCompilerMapping.REASON_BACKGROUND_DEXOPT); 335 } else if ("reset".equals(compilerFilter)) { 336 // Use the default mode for install. 337 targetCompilerFilter = 338 PackageManagerServiceCompilerMapping.getCompilerFilterForReason( 339 PackageManagerServiceCompilerMapping.REASON_INSTALL); 340 } else { 341 if (!DexFile.isValidCompilerFilter(compilerFilter)) { 342 pw.println("Error: \"" + compilerFilter + 343 "\" is not a valid compilation filter."); 344 return 1; 345 } 346 targetCompilerFilter = compilerFilter; 347 } 348 } else { 349 int reason = -1; 350 for (int i = 0; i < PackageManagerServiceCompilerMapping.REASON_STRINGS.length; i++) { 351 if (PackageManagerServiceCompilerMapping.REASON_STRINGS[i].equals( 352 compilationReason)) { 353 reason = i; 354 break; 355 } 356 } 357 if (reason == -1) { 358 pw.println("Error: Unknown compilation reason: " + compilationReason); 359 return 1; 360 } 361 targetCompilerFilter = 362 PackageManagerServiceCompilerMapping.getCompilerFilterForReason(reason); 363 } 364 365 366 List<String> packageNames = null; 367 if (allPackages) { 368 packageNames = mInterface.getAllPackages(); 369 } else { 370 String packageName = getNextArg(); 371 if (packageName == null) { 372 pw.println("Error: package name not specified"); 373 return 1; 374 } 375 packageNames = Collections.singletonList(packageName); 376 } 377 378 List<String> failedPackages = new ArrayList<>(); 379 for (String packageName : packageNames) { 380 if (clearProfileData) { 381 mInterface.clearApplicationProfileData(packageName); 382 } 383 384 boolean result = mInterface.performDexOptMode(packageName, null /* instructionSet */, 385 useJitProfiles, targetCompilerFilter, forceCompilation); 386 if (!result) { 387 failedPackages.add(packageName); 388 } 389 } 390 391 if (failedPackages.isEmpty()) { 392 pw.println("Success"); 393 return 0; 394 } else if (failedPackages.size() == 1) { 395 pw.println("Failure: package " + failedPackages.get(0) + " could not be compiled"); 396 return 1; 397 } else { 398 pw.print("Failure: the following packages could not be compiled: "); 399 boolean is_first = true; 400 for (String packageName : failedPackages) { 401 if (is_first) { 402 is_first = false; 403 } else { 404 pw.print(", "); 405 } 406 pw.print(packageName); 407 } 408 pw.println(); 409 return 1; 410 } 411 } 412 413 private int runList() throws RemoteException { 414 final PrintWriter pw = getOutPrintWriter(); 415 final String type = getNextArg(); 416 if (type == null) { 417 pw.println("Error: didn't specify type of data to list"); 418 return -1; 419 } 420 switch(type) { 421 case "features": 422 return runListFeatures(); 423 case "instrumentation": 424 return runListInstrumentation(); 425 case "libraries": 426 return runListLibraries(); 427 case "package": 428 case "packages": 429 return runListPackages(false /*showSourceDir*/); 430 case "permission-groups": 431 return runListPermissionGroups(); 432 case "permissions": 433 return runListPermissions(); 434 } 435 pw.println("Error: unknown list type '" + type + "'"); 436 return -1; 437 } 438 439 private int runListFeatures() throws RemoteException { 440 final PrintWriter pw = getOutPrintWriter(); 441 final List<FeatureInfo> list = mInterface.getSystemAvailableFeatures().getList(); 442 443 // sort by name 444 Collections.sort(list, new Comparator<FeatureInfo>() { 445 public int compare(FeatureInfo o1, FeatureInfo o2) { 446 if (o1.name == o2.name) return 0; 447 if (o1.name == null) return -1; 448 if (o2.name == null) return 1; 449 return o1.name.compareTo(o2.name); 450 } 451 }); 452 453 final int count = (list != null) ? list.size() : 0; 454 for (int p = 0; p < count; p++) { 455 FeatureInfo fi = list.get(p); 456 pw.print("feature:"); 457 if (fi.name != null) { 458 pw.print(fi.name); 459 if (fi.version > 0) { 460 pw.print("="); 461 pw.print(fi.version); 462 } 463 pw.println(); 464 } else { 465 pw.println("reqGlEsVersion=0x" 466 + Integer.toHexString(fi.reqGlEsVersion)); 467 } 468 } 469 return 0; 470 } 471 472 private int runListInstrumentation() throws RemoteException { 473 final PrintWriter pw = getOutPrintWriter(); 474 boolean showSourceDir = false; 475 String targetPackage = null; 476 477 try { 478 String opt; 479 while ((opt = getNextArg()) != null) { 480 switch (opt) { 481 case "-f": 482 showSourceDir = true; 483 break; 484 default: 485 if (opt.charAt(0) != '-') { 486 targetPackage = opt; 487 } else { 488 pw.println("Error: Unknown option: " + opt); 489 return -1; 490 } 491 break; 492 } 493 } 494 } catch (RuntimeException ex) { 495 pw.println("Error: " + ex.toString()); 496 return -1; 497 } 498 499 final List<InstrumentationInfo> list = 500 mInterface.queryInstrumentation(targetPackage, 0 /*flags*/).getList(); 501 502 // sort by target package 503 Collections.sort(list, new Comparator<InstrumentationInfo>() { 504 public int compare(InstrumentationInfo o1, InstrumentationInfo o2) { 505 return o1.targetPackage.compareTo(o2.targetPackage); 506 } 507 }); 508 509 final int count = (list != null) ? list.size() : 0; 510 for (int p = 0; p < count; p++) { 511 final InstrumentationInfo ii = list.get(p); 512 pw.print("instrumentation:"); 513 if (showSourceDir) { 514 pw.print(ii.sourceDir); 515 pw.print("="); 516 } 517 final ComponentName cn = new ComponentName(ii.packageName, ii.name); 518 pw.print(cn.flattenToShortString()); 519 pw.print(" (target="); 520 pw.print(ii.targetPackage); 521 pw.println(")"); 522 } 523 return 0; 524 } 525 526 private int runListLibraries() throws RemoteException { 527 final PrintWriter pw = getOutPrintWriter(); 528 final List<String> list = new ArrayList<String>(); 529 final String[] rawList = mInterface.getSystemSharedLibraryNames(); 530 for (int i = 0; i < rawList.length; i++) { 531 list.add(rawList[i]); 532 } 533 534 // sort by name 535 Collections.sort(list, new Comparator<String>() { 536 public int compare(String o1, String o2) { 537 if (o1 == o2) return 0; 538 if (o1 == null) return -1; 539 if (o2 == null) return 1; 540 return o1.compareTo(o2); 541 } 542 }); 543 544 final int count = (list != null) ? list.size() : 0; 545 for (int p = 0; p < count; p++) { 546 String lib = list.get(p); 547 pw.print("library:"); 548 pw.println(lib); 549 } 550 return 0; 551 } 552 553 private int runListPackages(boolean showSourceDir) throws RemoteException { 554 final PrintWriter pw = getOutPrintWriter(); 555 int getFlags = 0; 556 boolean listDisabled = false, listEnabled = false; 557 boolean listSystem = false, listThirdParty = false; 558 boolean listInstaller = false; 559 int userId = UserHandle.USER_SYSTEM; 560 try { 561 String opt; 562 while ((opt = getNextOption()) != null) { 563 switch (opt) { 564 case "-d": 565 listDisabled = true; 566 break; 567 case "-e": 568 listEnabled = true; 569 break; 570 case "-f": 571 showSourceDir = true; 572 break; 573 case "-i": 574 listInstaller = true; 575 break; 576 case "-l": 577 // old compat 578 break; 579 case "-lf": 580 showSourceDir = true; 581 break; 582 case "-s": 583 listSystem = true; 584 break; 585 case "-u": 586 getFlags |= PackageManager.GET_UNINSTALLED_PACKAGES; 587 break; 588 case "-3": 589 listThirdParty = true; 590 break; 591 case "--user": 592 userId = UserHandle.parseUserArg(getNextArgRequired()); 593 break; 594 default: 595 pw.println("Error: Unknown option: " + opt); 596 return -1; 597 } 598 } 599 } catch (RuntimeException ex) { 600 pw.println("Error: " + ex.toString()); 601 return -1; 602 } 603 604 final String filter = getNextArg(); 605 606 @SuppressWarnings("unchecked") 607 final ParceledListSlice<PackageInfo> slice = 608 mInterface.getInstalledPackages(getFlags, userId); 609 final List<PackageInfo> packages = slice.getList(); 610 611 final int count = packages.size(); 612 for (int p = 0; p < count; p++) { 613 final PackageInfo info = packages.get(p); 614 if (filter != null && !info.packageName.contains(filter)) { 615 continue; 616 } 617 final boolean isSystem = 618 (info.applicationInfo.flags&ApplicationInfo.FLAG_SYSTEM) != 0; 619 if ((!listDisabled || !info.applicationInfo.enabled) && 620 (!listEnabled || info.applicationInfo.enabled) && 621 (!listSystem || isSystem) && 622 (!listThirdParty || !isSystem)) { 623 pw.print("package:"); 624 if (showSourceDir) { 625 pw.print(info.applicationInfo.sourceDir); 626 pw.print("="); 627 } 628 pw.print(info.packageName); 629 if (listInstaller) { 630 pw.print(" installer="); 631 pw.print(mInterface.getInstallerPackageName(info.packageName)); 632 } 633 pw.println(); 634 } 635 } 636 return 0; 637 } 638 639 private int runListPermissionGroups() throws RemoteException { 640 final PrintWriter pw = getOutPrintWriter(); 641 final List<PermissionGroupInfo> pgs = mInterface.getAllPermissionGroups(0).getList(); 642 643 final int count = pgs.size(); 644 for (int p = 0; p < count ; p++) { 645 final PermissionGroupInfo pgi = pgs.get(p); 646 pw.print("permission group:"); 647 pw.println(pgi.name); 648 } 649 return 0; 650 } 651 652 private int runListPermissions() throws RemoteException { 653 final PrintWriter pw = getOutPrintWriter(); 654 boolean labels = false; 655 boolean groups = false; 656 boolean userOnly = false; 657 boolean summary = false; 658 boolean dangerousOnly = false; 659 String opt; 660 while ((opt = getNextOption()) != null) { 661 switch (opt) { 662 case "-d": 663 dangerousOnly = true; 664 break; 665 case "-f": 666 labels = true; 667 break; 668 case "-g": 669 groups = true; 670 break; 671 case "-s": 672 groups = true; 673 labels = true; 674 summary = true; 675 break; 676 case "-u": 677 userOnly = true; 678 break; 679 default: 680 pw.println("Error: Unknown option: " + opt); 681 return 1; 682 } 683 } 684 685 final ArrayList<String> groupList = new ArrayList<String>(); 686 if (groups) { 687 final List<PermissionGroupInfo> infos = 688 mInterface.getAllPermissionGroups(0 /*flags*/).getList(); 689 final int count = infos.size(); 690 for (int i = 0; i < count; i++) { 691 groupList.add(infos.get(i).name); 692 } 693 groupList.add(null); 694 } else { 695 final String grp = getNextArg(); 696 groupList.add(grp); 697 } 698 699 if (dangerousOnly) { 700 pw.println("Dangerous Permissions:"); 701 pw.println(""); 702 doListPermissions(groupList, groups, labels, summary, 703 PermissionInfo.PROTECTION_DANGEROUS, 704 PermissionInfo.PROTECTION_DANGEROUS); 705 if (userOnly) { 706 pw.println("Normal Permissions:"); 707 pw.println(""); 708 doListPermissions(groupList, groups, labels, summary, 709 PermissionInfo.PROTECTION_NORMAL, 710 PermissionInfo.PROTECTION_NORMAL); 711 } 712 } else if (userOnly) { 713 pw.println("Dangerous and Normal Permissions:"); 714 pw.println(""); 715 doListPermissions(groupList, groups, labels, summary, 716 PermissionInfo.PROTECTION_NORMAL, 717 PermissionInfo.PROTECTION_DANGEROUS); 718 } else { 719 pw.println("All Permissions:"); 720 pw.println(""); 721 doListPermissions(groupList, groups, labels, summary, 722 -10000, 10000); 723 } 724 return 0; 725 } 726 727 private int runUninstall() throws RemoteException { 728 final PrintWriter pw = getOutPrintWriter(); 729 int flags = 0; 730 int userId = UserHandle.USER_ALL; 731 732 String opt; 733 while ((opt = getNextOption()) != null) { 734 switch (opt) { 735 case "-k": 736 flags |= PackageManager.DELETE_KEEP_DATA; 737 break; 738 case "--user": 739 userId = UserHandle.parseUserArg(getNextArgRequired()); 740 break; 741 default: 742 pw.println("Error: Unknown option: " + opt); 743 return 1; 744 } 745 } 746 747 final String packageName = getNextArg(); 748 if (packageName == null) { 749 pw.println("Error: package name not specified"); 750 return 1; 751 } 752 753 // if a split is specified, just remove it and not the whole package 754 final String splitName = getNextArg(); 755 if (splitName != null) { 756 return runRemoveSplit(packageName, splitName); 757 } 758 759 userId = translateUserId(userId, "runUninstall"); 760 if (userId == UserHandle.USER_ALL) { 761 userId = UserHandle.USER_SYSTEM; 762 flags |= PackageManager.DELETE_ALL_USERS; 763 } else { 764 final PackageInfo info = mInterface.getPackageInfo(packageName, 0, userId); 765 if (info == null) { 766 pw.println("Failure [not installed for " + userId + "]"); 767 return 1; 768 } 769 final boolean isSystem = 770 (info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0; 771 // If we are being asked to delete a system app for just one 772 // user set flag so it disables rather than reverting to system 773 // version of the app. 774 if (isSystem) { 775 flags |= PackageManager.DELETE_SYSTEM_APP; 776 } 777 } 778 779 final LocalIntentReceiver receiver = new LocalIntentReceiver(); 780 mInterface.getPackageInstaller().uninstall(packageName, null /*callerPackageName*/, flags, 781 receiver.getIntentSender(), userId); 782 783 final Intent result = receiver.getResult(); 784 final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS, 785 PackageInstaller.STATUS_FAILURE); 786 if (status == PackageInstaller.STATUS_SUCCESS) { 787 pw.println("Success"); 788 return 0; 789 } else { 790 pw.println("Failure [" 791 + result.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE) + "]"); 792 return 1; 793 } 794 } 795 796 private int runRemoveSplit(String packageName, String splitName) throws RemoteException { 797 final PrintWriter pw = getOutPrintWriter(); 798 final SessionParams sessionParams = new SessionParams(SessionParams.MODE_INHERIT_EXISTING); 799 sessionParams.installFlags |= PackageManager.INSTALL_REPLACE_EXISTING; 800 sessionParams.appPackageName = packageName; 801 final int sessionId = 802 doCreateSession(sessionParams, null /*installerPackageName*/, UserHandle.USER_ALL); 803 boolean abandonSession = true; 804 try { 805 if (doRemoveSplit(sessionId, splitName, false /*logSuccess*/) 806 != PackageInstaller.STATUS_SUCCESS) { 807 return 1; 808 } 809 if (doCommitSession(sessionId, false /*logSuccess*/) 810 != PackageInstaller.STATUS_SUCCESS) { 811 return 1; 812 } 813 abandonSession = false; 814 pw.println("Success"); 815 return 0; 816 } finally { 817 if (abandonSession) { 818 try { 819 doAbandonSession(sessionId, false /*logSuccess*/); 820 } catch (Exception ignore) { 821 } 822 } 823 } 824 } 825 826 private Intent parseIntentAndUser() throws URISyntaxException { 827 mTargetUser = UserHandle.USER_CURRENT; 828 Intent intent = Intent.parseCommandArgs(this, new Intent.CommandOptionHandler() { 829 @Override 830 public boolean handleOption(String opt, ShellCommand cmd) { 831 if ("--user".equals(opt)) { 832 mTargetUser = UserHandle.parseUserArg(cmd.getNextArgRequired()); 833 return true; 834 } 835 return false; 836 } 837 }); 838 mTargetUser = ActivityManager.handleIncomingUser(Binder.getCallingPid(), 839 Binder.getCallingUid(), mTargetUser, false, false, null, null); 840 return intent; 841 } 842 843 private int runResolveActivity() { 844 Intent intent; 845 try { 846 intent = parseIntentAndUser(); 847 } catch (URISyntaxException e) { 848 throw new RuntimeException(e.getMessage(), e); 849 } 850 try { 851 ResolveInfo ri = mInterface.resolveIntent(intent, null, 0, mTargetUser); 852 PrintWriter pw = getOutPrintWriter(); 853 if (ri == null) { 854 pw.println("No activity found"); 855 } else { 856 PrintWriterPrinter pr = new PrintWriterPrinter(pw); 857 ri.dump(pr, ""); 858 } 859 } catch (RemoteException e) { 860 throw new RuntimeException("Failed calling service", e); 861 } 862 return 0; 863 } 864 865 private int runQueryIntentActivities() { 866 Intent intent; 867 try { 868 intent = parseIntentAndUser(); 869 } catch (URISyntaxException e) { 870 throw new RuntimeException(e.getMessage(), e); 871 } 872 try { 873 List<ResolveInfo> result = mInterface.queryIntentActivities(intent, null, 0, 874 mTargetUser).getList(); 875 PrintWriter pw = getOutPrintWriter(); 876 if (result == null || result.size() <= 0) { 877 pw.println("No activities found"); 878 } else { 879 pw.print(result.size()); pw.println(" activities found:"); 880 PrintWriterPrinter pr = new PrintWriterPrinter(pw); 881 for (int i=0; i<result.size(); i++) { 882 pw.print(" Activity #"); pw.print(i); pw.println(":"); 883 result.get(i).dump(pr, " "); 884 } 885 } 886 } catch (RemoteException e) { 887 throw new RuntimeException("Failed calling service", e); 888 } 889 return 0; 890 } 891 892 private int runQueryIntentServices() { 893 Intent intent; 894 try { 895 intent = parseIntentAndUser(); 896 } catch (URISyntaxException e) { 897 throw new RuntimeException(e.getMessage(), e); 898 } 899 try { 900 List<ResolveInfo> result = mInterface.queryIntentServices(intent, null, 0, 901 mTargetUser).getList(); 902 PrintWriter pw = getOutPrintWriter(); 903 if (result == null || result.size() <= 0) { 904 pw.println("No services found"); 905 } else { 906 pw.print(result.size()); pw.println(" services found:"); 907 PrintWriterPrinter pr = new PrintWriterPrinter(pw); 908 for (int i=0; i<result.size(); i++) { 909 pw.print(" Service #"); pw.print(i); pw.println(":"); 910 result.get(i).dump(pr, " "); 911 } 912 } 913 } catch (RemoteException e) { 914 throw new RuntimeException("Failed calling service", e); 915 } 916 return 0; 917 } 918 919 private int runQueryIntentReceivers() { 920 Intent intent; 921 try { 922 intent = parseIntentAndUser(); 923 } catch (URISyntaxException e) { 924 throw new RuntimeException(e.getMessage(), e); 925 } 926 try { 927 List<ResolveInfo> result = mInterface.queryIntentReceivers(intent, null, 0, 928 mTargetUser).getList(); 929 PrintWriter pw = getOutPrintWriter(); 930 if (result == null || result.size() <= 0) { 931 pw.println("No receivers found"); 932 } else { 933 pw.print(result.size()); pw.println(" receivers found:"); 934 PrintWriterPrinter pr = new PrintWriterPrinter(pw); 935 for (int i=0; i<result.size(); i++) { 936 pw.print(" Receiver #"); pw.print(i); pw.println(":"); 937 result.get(i).dump(pr, " "); 938 } 939 } 940 } catch (RemoteException e) { 941 throw new RuntimeException("Failed calling service", e); 942 } 943 return 0; 944 } 945 946 private static class InstallParams { 947 SessionParams sessionParams; 948 String installerPackageName; 949 int userId = UserHandle.USER_ALL; 950 } 951 952 private InstallParams makeInstallParams() { 953 final SessionParams sessionParams = new SessionParams(SessionParams.MODE_FULL_INSTALL); 954 final InstallParams params = new InstallParams(); 955 params.sessionParams = sessionParams; 956 String opt; 957 while ((opt = getNextOption()) != null) { 958 switch (opt) { 959 case "-l": 960 sessionParams.installFlags |= PackageManager.INSTALL_FORWARD_LOCK; 961 break; 962 case "-r": 963 sessionParams.installFlags |= PackageManager.INSTALL_REPLACE_EXISTING; 964 break; 965 case "-i": 966 params.installerPackageName = getNextArg(); 967 if (params.installerPackageName == null) { 968 throw new IllegalArgumentException("Missing installer package"); 969 } 970 break; 971 case "-t": 972 sessionParams.installFlags |= PackageManager.INSTALL_ALLOW_TEST; 973 break; 974 case "-s": 975 sessionParams.installFlags |= PackageManager.INSTALL_EXTERNAL; 976 break; 977 case "-f": 978 sessionParams.installFlags |= PackageManager.INSTALL_INTERNAL; 979 break; 980 case "-d": 981 sessionParams.installFlags |= PackageManager.INSTALL_ALLOW_DOWNGRADE; 982 break; 983 case "-g": 984 sessionParams.installFlags |= PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS; 985 break; 986 case "--originating-uri": 987 sessionParams.originatingUri = Uri.parse(getNextArg()); 988 break; 989 case "--referrer": 990 sessionParams.referrerUri = Uri.parse(getNextArg()); 991 break; 992 case "-p": 993 sessionParams.mode = SessionParams.MODE_INHERIT_EXISTING; 994 sessionParams.appPackageName = getNextArg(); 995 if (sessionParams.appPackageName == null) { 996 throw new IllegalArgumentException("Missing inherit package name"); 997 } 998 break; 999 case "-S": 1000 sessionParams.setSize(Long.parseLong(getNextArg())); 1001 break; 1002 case "--abi": 1003 sessionParams.abiOverride = checkAbiArgument(getNextArg()); 1004 break; 1005 case "--ephemeral": 1006 sessionParams.installFlags |= PackageManager.INSTALL_EPHEMERAL; 1007 break; 1008 case "--user": 1009 params.userId = UserHandle.parseUserArg(getNextArgRequired()); 1010 break; 1011 case "--install-location": 1012 sessionParams.installLocation = Integer.parseInt(getNextArg()); 1013 break; 1014 case "--force-uuid": 1015 sessionParams.installFlags |= PackageManager.INSTALL_FORCE_VOLUME_UUID; 1016 sessionParams.volumeUuid = getNextArg(); 1017 if ("internal".equals(sessionParams.volumeUuid)) { 1018 sessionParams.volumeUuid = null; 1019 } 1020 break; 1021 default: 1022 throw new IllegalArgumentException("Unknown option " + opt); 1023 } 1024 } 1025 return params; 1026 } 1027 1028 private int runSetHomeActivity() { 1029 final PrintWriter pw = getOutPrintWriter(); 1030 int userId = UserHandle.USER_SYSTEM; 1031 String opt; 1032 while ((opt = getNextOption()) != null) { 1033 switch (opt) { 1034 case "--user": 1035 userId = UserHandle.parseUserArg(getNextArgRequired()); 1036 break; 1037 default: 1038 pw.println("Error: Unknown option: " + opt); 1039 return 1; 1040 } 1041 } 1042 1043 String component = getNextArg(); 1044 ComponentName componentName = 1045 component != null ? ComponentName.unflattenFromString(component) : null; 1046 1047 if (componentName == null) { 1048 pw.println("Error: component name not specified or invalid"); 1049 return 1; 1050 } 1051 1052 try { 1053 mInterface.setHomeActivity(componentName, userId); 1054 return 0; 1055 } catch (RemoteException e) { 1056 pw.println(e.toString()); 1057 return 1; 1058 } 1059 } 1060 1061 private static String checkAbiArgument(String abi) { 1062 if (TextUtils.isEmpty(abi)) { 1063 throw new IllegalArgumentException("Missing ABI argument"); 1064 } 1065 1066 if ("-".equals(abi)) { 1067 return abi; 1068 } 1069 1070 final String[] supportedAbis = Build.SUPPORTED_ABIS; 1071 for (String supportedAbi : supportedAbis) { 1072 if (supportedAbi.equals(abi)) { 1073 return abi; 1074 } 1075 } 1076 1077 throw new IllegalArgumentException("ABI " + abi + " not supported on this device"); 1078 } 1079 1080 private int translateUserId(int userId, String logContext) { 1081 return ActivityManager.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), 1082 userId, true, true, logContext, "pm command"); 1083 } 1084 1085 private int doCreateSession(SessionParams params, String installerPackageName, int userId) 1086 throws RemoteException { 1087 userId = translateUserId(userId, "runInstallCreate"); 1088 if (userId == UserHandle.USER_ALL) { 1089 userId = UserHandle.USER_SYSTEM; 1090 params.installFlags |= PackageManager.INSTALL_ALL_USERS; 1091 } 1092 1093 final int sessionId = mInterface.getPackageInstaller() 1094 .createSession(params, installerPackageName, userId); 1095 return sessionId; 1096 } 1097 1098 private int doWriteSplit(int sessionId, String inPath, long sizeBytes, String splitName, 1099 boolean logSuccess) throws RemoteException { 1100 final PrintWriter pw = getOutPrintWriter(); 1101 if ("-".equals(inPath)) { 1102 inPath = null; 1103 } else if (inPath != null) { 1104 final File file = new File(inPath); 1105 if (file.isFile()) { 1106 sizeBytes = file.length(); 1107 } 1108 } 1109 1110 final SessionInfo info = mInterface.getPackageInstaller().getSessionInfo(sessionId); 1111 1112 PackageInstaller.Session session = null; 1113 InputStream in = null; 1114 OutputStream out = null; 1115 try { 1116 session = new PackageInstaller.Session( 1117 mInterface.getPackageInstaller().openSession(sessionId)); 1118 1119 if (inPath != null) { 1120 in = new FileInputStream(inPath); 1121 } else { 1122 in = new SizedInputStream(getRawInputStream(), sizeBytes); 1123 } 1124 out = session.openWrite(splitName, 0, sizeBytes); 1125 1126 int total = 0; 1127 byte[] buffer = new byte[65536]; 1128 int c; 1129 while ((c = in.read(buffer)) != -1) { 1130 total += c; 1131 out.write(buffer, 0, c); 1132 1133 if (info.sizeBytes > 0) { 1134 final float fraction = ((float) c / (float) info.sizeBytes); 1135 session.addProgress(fraction); 1136 } 1137 } 1138 session.fsync(out); 1139 1140 if (logSuccess) { 1141 pw.println("Success: streamed " + total + " bytes"); 1142 } 1143 return 0; 1144 } catch (IOException e) { 1145 pw.println("Error: failed to write; " + e.getMessage()); 1146 return 1; 1147 } finally { 1148 IoUtils.closeQuietly(out); 1149 IoUtils.closeQuietly(in); 1150 IoUtils.closeQuietly(session); 1151 } 1152 } 1153 1154 private int doRemoveSplit(int sessionId, String splitName, boolean logSuccess) 1155 throws RemoteException { 1156 final PrintWriter pw = getOutPrintWriter(); 1157 PackageInstaller.Session session = null; 1158 try { 1159 session = new PackageInstaller.Session( 1160 mInterface.getPackageInstaller().openSession(sessionId)); 1161 session.removeSplit(splitName); 1162 1163 if (logSuccess) { 1164 pw.println("Success"); 1165 } 1166 return 0; 1167 } catch (IOException e) { 1168 pw.println("Error: failed to remove split; " + e.getMessage()); 1169 return 1; 1170 } finally { 1171 IoUtils.closeQuietly(session); 1172 } 1173 } 1174 1175 private int doCommitSession(int sessionId, boolean logSuccess) throws RemoteException { 1176 final PrintWriter pw = getOutPrintWriter(); 1177 PackageInstaller.Session session = null; 1178 try { 1179 session = new PackageInstaller.Session( 1180 mInterface.getPackageInstaller().openSession(sessionId)); 1181 1182 final LocalIntentReceiver receiver = new LocalIntentReceiver(); 1183 session.commit(receiver.getIntentSender()); 1184 1185 final Intent result = receiver.getResult(); 1186 final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS, 1187 PackageInstaller.STATUS_FAILURE); 1188 if (status == PackageInstaller.STATUS_SUCCESS) { 1189 if (logSuccess) { 1190 System.out.println("Success"); 1191 } 1192 } else { 1193 pw.println("Failure [" 1194 + result.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE) + "]"); 1195 } 1196 return status; 1197 } finally { 1198 IoUtils.closeQuietly(session); 1199 } 1200 } 1201 1202 private int doAbandonSession(int sessionId, boolean logSuccess) throws RemoteException { 1203 final PrintWriter pw = getOutPrintWriter(); 1204 PackageInstaller.Session session = null; 1205 try { 1206 session = new PackageInstaller.Session( 1207 mInterface.getPackageInstaller().openSession(sessionId)); 1208 session.abandon(); 1209 if (logSuccess) { 1210 pw.println("Success"); 1211 } 1212 return 0; 1213 } finally { 1214 IoUtils.closeQuietly(session); 1215 } 1216 } 1217 1218 private void doListPermissions(ArrayList<String> groupList, boolean groups, boolean labels, 1219 boolean summary, int startProtectionLevel, int endProtectionLevel) 1220 throws RemoteException { 1221 final PrintWriter pw = getOutPrintWriter(); 1222 final int groupCount = groupList.size(); 1223 for (int i = 0; i < groupCount; i++) { 1224 String groupName = groupList.get(i); 1225 String prefix = ""; 1226 if (groups) { 1227 if (i > 0) { 1228 pw.println(""); 1229 } 1230 if (groupName != null) { 1231 PermissionGroupInfo pgi = 1232 mInterface.getPermissionGroupInfo(groupName, 0 /*flags*/); 1233 if (summary) { 1234 Resources res = getResources(pgi); 1235 if (res != null) { 1236 pw.print(loadText(pgi, pgi.labelRes, pgi.nonLocalizedLabel) + ": "); 1237 } else { 1238 pw.print(pgi.name + ": "); 1239 1240 } 1241 } else { 1242 pw.println((labels ? "+ " : "") + "group:" + pgi.name); 1243 if (labels) { 1244 pw.println(" package:" + pgi.packageName); 1245 Resources res = getResources(pgi); 1246 if (res != null) { 1247 pw.println(" label:" 1248 + loadText(pgi, pgi.labelRes, pgi.nonLocalizedLabel)); 1249 pw.println(" description:" 1250 + loadText(pgi, pgi.descriptionRes, 1251 pgi.nonLocalizedDescription)); 1252 } 1253 } 1254 } 1255 } else { 1256 pw.println(((labels && !summary) ? "+ " : "") + "ungrouped:"); 1257 } 1258 prefix = " "; 1259 } 1260 List<PermissionInfo> ps = 1261 mInterface.queryPermissionsByGroup(groupList.get(i), 0 /*flags*/).getList(); 1262 final int count = ps.size(); 1263 boolean first = true; 1264 for (int p = 0 ; p < count ; p++) { 1265 PermissionInfo pi = ps.get(p); 1266 if (groups && groupName == null && pi.group != null) { 1267 continue; 1268 } 1269 final int base = pi.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE; 1270 if (base < startProtectionLevel 1271 || base > endProtectionLevel) { 1272 continue; 1273 } 1274 if (summary) { 1275 if (first) { 1276 first = false; 1277 } else { 1278 pw.print(", "); 1279 } 1280 Resources res = getResources(pi); 1281 if (res != null) { 1282 pw.print(loadText(pi, pi.labelRes, 1283 pi.nonLocalizedLabel)); 1284 } else { 1285 pw.print(pi.name); 1286 } 1287 } else { 1288 pw.println(prefix + (labels ? "+ " : "") 1289 + "permission:" + pi.name); 1290 if (labels) { 1291 pw.println(prefix + " package:" + pi.packageName); 1292 Resources res = getResources(pi); 1293 if (res != null) { 1294 pw.println(prefix + " label:" 1295 + loadText(pi, pi.labelRes, 1296 pi.nonLocalizedLabel)); 1297 pw.println(prefix + " description:" 1298 + loadText(pi, pi.descriptionRes, 1299 pi.nonLocalizedDescription)); 1300 } 1301 pw.println(prefix + " protectionLevel:" 1302 + PermissionInfo.protectionToString(pi.protectionLevel)); 1303 } 1304 } 1305 } 1306 1307 if (summary) { 1308 pw.println(""); 1309 } 1310 } 1311 } 1312 1313 private String loadText(PackageItemInfo pii, int res, CharSequence nonLocalized) 1314 throws RemoteException { 1315 if (nonLocalized != null) { 1316 return nonLocalized.toString(); 1317 } 1318 if (res != 0) { 1319 Resources r = getResources(pii); 1320 if (r != null) { 1321 try { 1322 return r.getString(res); 1323 } catch (Resources.NotFoundException e) { 1324 } 1325 } 1326 } 1327 return null; 1328 } 1329 1330 private Resources getResources(PackageItemInfo pii) throws RemoteException { 1331 Resources res = mResourceCache.get(pii.packageName); 1332 if (res != null) return res; 1333 1334 ApplicationInfo ai = mInterface.getApplicationInfo(pii.packageName, 0, 0); 1335 AssetManager am = new AssetManager(); 1336 am.addAssetPath(ai.publicSourceDir); 1337 res = new Resources(am, null, null); 1338 mResourceCache.put(pii.packageName, res); 1339 return res; 1340 } 1341 1342 @Override 1343 public void onHelp() { 1344 final PrintWriter pw = getOutPrintWriter(); 1345 pw.println("Package manager (package) commands:"); 1346 pw.println(" help"); 1347 pw.println(" Print this help text."); 1348 pw.println(""); 1349 pw.println(" compile [-m MODE] [-f] [-c] [--reset] (-a | TARGET-PACKAGE)"); 1350 pw.println(" Trigger compilation of TARGET-PACKAGE or all packages if \"-a\"."); 1351 pw.println(" Options:"); 1352 pw.println(" -a: compile all packages"); 1353 pw.println(" -c: clear profile data before compiling"); 1354 pw.println(" -f: force compilation even if not needed"); 1355 pw.println(" -m: select compilation mode"); 1356 pw.println(" MODE can be one of \"default\", \"full\", \"profile\"," + 1357 " and \"extract\""); 1358 pw.println(" --reset: restore package to its post-install state"); 1359 pw.println(" shorthand for \"-c -f -m extract\""); 1360 pw.println(" list features"); 1361 pw.println(" Prints all features of the system."); 1362 pw.println(" list instrumentation [-f] [TARGET-PACKAGE]"); 1363 pw.println(" Prints all test packages; optionally only those targeting TARGET-PACKAGE"); 1364 pw.println(" Options:"); 1365 pw.println(" -f: dump the name of the .apk file containing the test package"); 1366 pw.println(" list libraries"); 1367 pw.println(" Prints all system libraries."); 1368 pw.println(" list packages [-f] [-d] [-e] [-s] [-3] [-i] [-u] [--user USER_ID] [FILTER]"); 1369 pw.println(" Prints all packages; optionally only those whose name contains"); 1370 pw.println(" the text in FILTER."); 1371 pw.println(" Options:"); 1372 pw.println(" -f: see their associated file"); 1373 pw.println(" -d: filter to only show disabled packages"); 1374 pw.println(" -e: filter to only show enabled packages"); 1375 pw.println(" -s: filter to only show system packages"); 1376 pw.println(" -3: filter to only show third party packages"); 1377 pw.println(" -i: see the installer for the packages"); 1378 pw.println(" -u: also include uninstalled packages"); 1379 pw.println(" list permission-groups"); 1380 pw.println(" Prints all known permission groups."); 1381 pw.println(" list permissions [-g] [-f] [-d] [-u] [GROUP]"); 1382 pw.println(" Prints all known permissions; optionally only those in GROUP."); 1383 pw.println(" Options:"); 1384 pw.println(" -g: organize by group"); 1385 pw.println(" -f: print all information"); 1386 pw.println(" -s: short summary"); 1387 pw.println(" -d: only list dangerous permissions"); 1388 pw.println(" -u: list only the permissions users will see"); 1389 pw.println(" resolve-activity [--user USER_ID] INTENT"); 1390 pw.println(" Prints the activity that resolves to the given Intent."); 1391 pw.println(" query-activities [--user USER_ID] INTENT"); 1392 pw.println(" Prints all activities that can handle the given Intent."); 1393 pw.println(" query-services [--user USER_ID] INTENT"); 1394 pw.println(" Prints all services that can handle the given Intent."); 1395 pw.println(" query-receivers [--user USER_ID] INTENT"); 1396 pw.println(" Prints all broadcast receivers that can handle the given Intent."); 1397 pw.println(" suspend [--user USER_ID] TARGET-PACKAGE"); 1398 pw.println(" Suspends the specified package (as user)."); 1399 pw.println(" unsuspend [--user USER_ID] TARGET-PACKAGE"); 1400 pw.println(" Unsuspends the specified package (as user)."); 1401 pw.println(" set-home-activity [--user USER_ID] TARGET-COMPONENT"); 1402 pw.println(" set the default home activity (aka launcher)."); 1403 pw.println(); 1404 Intent.printIntentArgsHelp(pw , ""); 1405 } 1406 1407 private static class LocalIntentReceiver { 1408 private final SynchronousQueue<Intent> mResult = new SynchronousQueue<>(); 1409 1410 private IIntentSender.Stub mLocalSender = new IIntentSender.Stub() { 1411 @Override 1412 public int send(int code, Intent intent, String resolvedType, 1413 IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) { 1414 try { 1415 mResult.offer(intent, 5, TimeUnit.SECONDS); 1416 } catch (InterruptedException e) { 1417 throw new RuntimeException(e); 1418 } 1419 return 0; 1420 } 1421 }; 1422 1423 public IntentSender getIntentSender() { 1424 return new IntentSender((IIntentSender) mLocalSender); 1425 } 1426 1427 public Intent getResult() { 1428 try { 1429 return mResult.take(); 1430 } catch (InterruptedException e) { 1431 throw new RuntimeException(e); 1432 } 1433 } 1434 } 1435} 1436