1 /* 2 ** 3 ** Copyright 2007, The Android Open Source Project 4 ** 5 ** Licensed under the Apache License, Version 2.0 (the "License"); 6 ** you may not use this file except in compliance with the License. 7 ** You may obtain a copy of the License at 8 ** 9 ** http://www.apache.org/licenses/LICENSE-2.0 10 ** 11 ** Unless required by applicable law or agreed to in writing, software 12 ** distributed under the License is distributed on an "AS IS" BASIS, 13 ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 ** See the License for the specific language governing permissions and 15 ** limitations under the License. 16 */ 17 18 19 package com.android.commands.am; 20 21 import android.app.ActivityManager; 22 import android.app.ActivityManagerNative; 23 import android.app.IActivityController; 24 import android.app.IActivityManager; 25 import android.app.IInstrumentationWatcher; 26 import android.app.Instrumentation; 27 import android.content.ComponentName; 28 import android.content.Context; 29 import android.content.IIntentReceiver; 30 import android.content.Intent; 31 import android.content.pm.IPackageManager; 32 import android.content.pm.ResolveInfo; 33 import android.net.Uri; 34 import android.os.Bundle; 35 import android.os.ParcelFileDescriptor; 36 import android.os.RemoteException; 37 import android.os.ServiceManager; 38 import android.os.SystemProperties; 39 import android.util.AndroidException; 40 import android.view.IWindowManager; 41 42 import java.io.BufferedReader; 43 import java.io.File; 44 import java.io.FileNotFoundException; 45 import java.io.IOException; 46 import java.io.InputStreamReader; 47 import java.io.PrintStream; 48 import java.net.URISyntaxException; 49 import java.util.HashSet; 50 import java.util.List; 51 52 public class Am { 53 54 private IActivityManager mAm; 55 private String[] mArgs; 56 private int mNextArg; 57 private String mCurArgData; 58 59 private boolean mDebugOption = false; 60 private boolean mWaitOption = false; 61 private boolean mStopOption = false; 62 63 private String mProfileFile; 64 private boolean mProfileAutoStop; 65 66 // These are magic strings understood by the Eclipse plugin. 67 private static final String FATAL_ERROR_CODE = "Error type 1"; 68 private static final String NO_SYSTEM_ERROR_CODE = "Error type 2"; 69 private static final String NO_CLASS_ERROR_CODE = "Error type 3"; 70 71 /** 72 * Command-line entry point. 73 * 74 * @param args The command-line arguments 75 */ main(String[] args)76 public static void main(String[] args) { 77 try { 78 (new Am()).run(args); 79 } catch (IllegalArgumentException e) { 80 showUsage(); 81 System.err.println("Error: " + e.getMessage()); 82 } catch (Exception e) { 83 e.printStackTrace(System.err); 84 System.exit(1); 85 } 86 } 87 run(String[] args)88 private void run(String[] args) throws Exception { 89 if (args.length < 1) { 90 showUsage(); 91 return; 92 } 93 94 mAm = ActivityManagerNative.getDefault(); 95 if (mAm == null) { 96 System.err.println(NO_SYSTEM_ERROR_CODE); 97 throw new AndroidException("Can't connect to activity manager; is the system running?"); 98 } 99 100 mArgs = args; 101 String op = args[0]; 102 mNextArg = 1; 103 104 if (op.equals("start")) { 105 runStart(); 106 } else if (op.equals("startservice")) { 107 runStartService(); 108 } else if (op.equals("force-stop")) { 109 runForceStop(); 110 } else if (op.equals("instrument")) { 111 runInstrument(); 112 } else if (op.equals("broadcast")) { 113 sendBroadcast(); 114 } else if (op.equals("profile")) { 115 runProfile(); 116 } else if (op.equals("dumpheap")) { 117 runDumpHeap(); 118 } else if (op.equals("monitor")) { 119 runMonitor(); 120 } else if (op.equals("screen-compat")) { 121 runScreenCompat(); 122 } else if (op.equals("display-size")) { 123 runDisplaySize(); 124 } else { 125 throw new IllegalArgumentException("Unknown command: " + op); 126 } 127 } 128 makeIntent()129 private Intent makeIntent() throws URISyntaxException { 130 Intent intent = new Intent(); 131 boolean hasIntentInfo = false; 132 133 mDebugOption = false; 134 mWaitOption = false; 135 mStopOption = false; 136 mProfileFile = null; 137 Uri data = null; 138 String type = null; 139 140 String opt; 141 while ((opt=nextOption()) != null) { 142 if (opt.equals("-a")) { 143 intent.setAction(nextArgRequired()); 144 hasIntentInfo = true; 145 } else if (opt.equals("-d")) { 146 data = Uri.parse(nextArgRequired()); 147 hasIntentInfo = true; 148 } else if (opt.equals("-t")) { 149 type = nextArgRequired(); 150 hasIntentInfo = true; 151 } else if (opt.equals("-c")) { 152 intent.addCategory(nextArgRequired()); 153 hasIntentInfo = true; 154 } else if (opt.equals("-e") || opt.equals("--es")) { 155 String key = nextArgRequired(); 156 String value = nextArgRequired(); 157 intent.putExtra(key, value); 158 hasIntentInfo = true; 159 } else if (opt.equals("--esn")) { 160 String key = nextArgRequired(); 161 intent.putExtra(key, (String) null); 162 hasIntentInfo = true; 163 } else if (opt.equals("--ei")) { 164 String key = nextArgRequired(); 165 String value = nextArgRequired(); 166 intent.putExtra(key, Integer.valueOf(value)); 167 hasIntentInfo = true; 168 } else if (opt.equals("--eu")) { 169 String key = nextArgRequired(); 170 String value = nextArgRequired(); 171 intent.putExtra(key, Uri.parse(value)); 172 hasIntentInfo = true; 173 } else if (opt.equals("--eia")) { 174 String key = nextArgRequired(); 175 String value = nextArgRequired(); 176 String[] strings = value.split(","); 177 int[] list = new int[strings.length]; 178 for (int i = 0; i < strings.length; i++) { 179 list[i] = Integer.valueOf(strings[i]); 180 } 181 intent.putExtra(key, list); 182 hasIntentInfo = true; 183 } else if (opt.equals("--el")) { 184 String key = nextArgRequired(); 185 String value = nextArgRequired(); 186 intent.putExtra(key, Long.valueOf(value)); 187 hasIntentInfo = true; 188 } else if (opt.equals("--ela")) { 189 String key = nextArgRequired(); 190 String value = nextArgRequired(); 191 String[] strings = value.split(","); 192 long[] list = new long[strings.length]; 193 for (int i = 0; i < strings.length; i++) { 194 list[i] = Long.valueOf(strings[i]); 195 } 196 intent.putExtra(key, list); 197 hasIntentInfo = true; 198 } else if (opt.equals("--ez")) { 199 String key = nextArgRequired(); 200 String value = nextArgRequired(); 201 intent.putExtra(key, Boolean.valueOf(value)); 202 hasIntentInfo = true; 203 } else if (opt.equals("-n")) { 204 String str = nextArgRequired(); 205 ComponentName cn = ComponentName.unflattenFromString(str); 206 if (cn == null) throw new IllegalArgumentException("Bad component name: " + str); 207 intent.setComponent(cn); 208 hasIntentInfo = true; 209 } else if (opt.equals("-f")) { 210 String str = nextArgRequired(); 211 intent.setFlags(Integer.decode(str).intValue()); 212 } else if (opt.equals("--grant-read-uri-permission")) { 213 intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); 214 } else if (opt.equals("--grant-write-uri-permission")) { 215 intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION); 216 } else if (opt.equals("--exclude-stopped-packages")) { 217 intent.addFlags(Intent.FLAG_EXCLUDE_STOPPED_PACKAGES); 218 } else if (opt.equals("--include-stopped-packages")) { 219 intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES); 220 } else if (opt.equals("--debug-log-resolution")) { 221 intent.addFlags(Intent.FLAG_DEBUG_LOG_RESOLUTION); 222 } else if (opt.equals("--activity-brought-to-front")) { 223 intent.addFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT); 224 } else if (opt.equals("--activity-clear-top")) { 225 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); 226 } else if (opt.equals("--activity-clear-when-task-reset")) { 227 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET); 228 } else if (opt.equals("--activity-exclude-from-recents")) { 229 intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); 230 } else if (opt.equals("--activity-launched-from-history")) { 231 intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY); 232 } else if (opt.equals("--activity-multiple-task")) { 233 intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK); 234 } else if (opt.equals("--activity-no-animation")) { 235 intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION); 236 } else if (opt.equals("--activity-no-history")) { 237 intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY); 238 } else if (opt.equals("--activity-no-user-action")) { 239 intent.addFlags(Intent.FLAG_ACTIVITY_NO_USER_ACTION); 240 } else if (opt.equals("--activity-previous-is-top")) { 241 intent.addFlags(Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP); 242 } else if (opt.equals("--activity-reorder-to-front")) { 243 intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT); 244 } else if (opt.equals("--activity-reset-task-if-needed")) { 245 intent.addFlags(Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); 246 } else if (opt.equals("--activity-single-top")) { 247 intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); 248 } else if (opt.equals("--activity-clear-task")) { 249 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK); 250 } else if (opt.equals("--activity-task-on-home")) { 251 intent.addFlags(Intent.FLAG_ACTIVITY_TASK_ON_HOME); 252 } else if (opt.equals("--receiver-registered-only")) { 253 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); 254 } else if (opt.equals("--receiver-replace-pending")) { 255 intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING); 256 } else if (opt.equals("-D")) { 257 mDebugOption = true; 258 } else if (opt.equals("-W")) { 259 mWaitOption = true; 260 } else if (opt.equals("-P")) { 261 mProfileFile = nextArgRequired(); 262 mProfileAutoStop = true; 263 } else if (opt.equals("--start-profiler")) { 264 mProfileFile = nextArgRequired(); 265 mProfileAutoStop = false; 266 } else if (opt.equals("-S")) { 267 mStopOption = true; 268 } else { 269 System.err.println("Error: Unknown option: " + opt); 270 showUsage(); 271 return null; 272 } 273 } 274 intent.setDataAndType(data, type); 275 276 String arg = nextArg(); 277 if (arg != null) { 278 Intent baseIntent; 279 if (arg.indexOf(':') >= 0) { 280 // The argument is a URI. Fully parse it, and use that result 281 // to fill in any data not specified so far. 282 baseIntent = Intent.parseUri(arg, Intent.URI_INTENT_SCHEME); 283 } else if (arg.indexOf('/') >= 0) { 284 // The argument is a component name. Build an Intent to launch 285 // it. 286 baseIntent = new Intent(Intent.ACTION_MAIN); 287 baseIntent.addCategory(Intent.CATEGORY_LAUNCHER); 288 baseIntent.setComponent(ComponentName.unflattenFromString(arg)); 289 } else { 290 // Assume the argument is a package name. 291 baseIntent = new Intent(Intent.ACTION_MAIN); 292 baseIntent.addCategory(Intent.CATEGORY_LAUNCHER); 293 baseIntent.setPackage(arg); 294 } 295 Bundle extras = intent.getExtras(); 296 intent.replaceExtras((Bundle)null); 297 Bundle uriExtras = baseIntent.getExtras(); 298 baseIntent.replaceExtras((Bundle)null); 299 if (intent.getAction() != null && baseIntent.getCategories() != null) { 300 HashSet<String> cats = new HashSet<String>(baseIntent.getCategories()); 301 for (String c : cats) { 302 baseIntent.removeCategory(c); 303 } 304 } 305 intent.fillIn(baseIntent, Intent.FILL_IN_COMPONENT); 306 if (extras == null) { 307 extras = uriExtras; 308 } else if (uriExtras != null) { 309 uriExtras.putAll(extras); 310 extras = uriExtras; 311 } 312 intent.replaceExtras(extras); 313 hasIntentInfo = true; 314 } 315 316 if (!hasIntentInfo) throw new IllegalArgumentException("No intent supplied"); 317 return intent; 318 } 319 runStartService()320 private void runStartService() throws Exception { 321 Intent intent = makeIntent(); 322 System.out.println("Starting service: " + intent); 323 ComponentName cn = mAm.startService(null, intent, intent.getType()); 324 if (cn == null) { 325 System.err.println("Error: Not found; no service started."); 326 } 327 } 328 runStart()329 private void runStart() throws Exception { 330 Intent intent = makeIntent(); 331 332 String mimeType = intent.getType(); 333 if (mimeType == null && intent.getData() != null 334 && "content".equals(intent.getData().getScheme())) { 335 mimeType = mAm.getProviderMimeType(intent.getData()); 336 } 337 338 if (mStopOption) { 339 String packageName; 340 if (intent.getComponent() != null) { 341 packageName = intent.getComponent().getPackageName(); 342 } else { 343 IPackageManager pm = IPackageManager.Stub.asInterface( 344 ServiceManager.getService("package")); 345 if (pm == null) { 346 System.err.println("Error: Package manager not running; aborting"); 347 return; 348 } 349 List<ResolveInfo> activities = pm.queryIntentActivities(intent, mimeType, 0); 350 if (activities == null || activities.size() <= 0) { 351 System.err.println("Error: Intent does not match any activities: " 352 + intent); 353 return; 354 } else if (activities.size() > 1) { 355 System.err.println("Error: Intent matches multiple activities; can't stop: " 356 + intent); 357 return; 358 } 359 packageName = activities.get(0).activityInfo.packageName; 360 } 361 System.out.println("Stopping: " + packageName); 362 mAm.forceStopPackage(packageName); 363 Thread.sleep(250); 364 } 365 366 System.out.println("Starting: " + intent); 367 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 368 369 ParcelFileDescriptor fd = null; 370 371 if (mProfileFile != null) { 372 try { 373 fd = ParcelFileDescriptor.open( 374 new File(mProfileFile), 375 ParcelFileDescriptor.MODE_CREATE | 376 ParcelFileDescriptor.MODE_TRUNCATE | 377 ParcelFileDescriptor.MODE_READ_WRITE); 378 } catch (FileNotFoundException e) { 379 System.err.println("Error: Unable to open file: " + mProfileFile); 380 return; 381 } 382 } 383 384 IActivityManager.WaitResult result = null; 385 int res; 386 if (mWaitOption) { 387 result = mAm.startActivityAndWait(null, intent, mimeType, 388 null, 0, null, null, 0, false, mDebugOption, 389 mProfileFile, fd, mProfileAutoStop); 390 res = result.result; 391 } else { 392 res = mAm.startActivity(null, intent, mimeType, 393 null, 0, null, null, 0, false, mDebugOption, 394 mProfileFile, fd, mProfileAutoStop); 395 } 396 PrintStream out = mWaitOption ? System.out : System.err; 397 boolean launched = false; 398 switch (res) { 399 case IActivityManager.START_SUCCESS: 400 launched = true; 401 break; 402 case IActivityManager.START_SWITCHES_CANCELED: 403 launched = true; 404 out.println( 405 "Warning: Activity not started because the " 406 + " current activity is being kept for the user."); 407 break; 408 case IActivityManager.START_DELIVERED_TO_TOP: 409 launched = true; 410 out.println( 411 "Warning: Activity not started, intent has " 412 + "been delivered to currently running " 413 + "top-most instance."); 414 break; 415 case IActivityManager.START_RETURN_INTENT_TO_CALLER: 416 launched = true; 417 out.println( 418 "Warning: Activity not started because intent " 419 + "should be handled by the caller"); 420 break; 421 case IActivityManager.START_TASK_TO_FRONT: 422 launched = true; 423 out.println( 424 "Warning: Activity not started, its current " 425 + "task has been brought to the front"); 426 break; 427 case IActivityManager.START_INTENT_NOT_RESOLVED: 428 out.println( 429 "Error: Activity not started, unable to " 430 + "resolve " + intent.toString()); 431 break; 432 case IActivityManager.START_CLASS_NOT_FOUND: 433 out.println(NO_CLASS_ERROR_CODE); 434 out.println("Error: Activity class " + 435 intent.getComponent().toShortString() 436 + " does not exist."); 437 break; 438 case IActivityManager.START_FORWARD_AND_REQUEST_CONFLICT: 439 out.println( 440 "Error: Activity not started, you requested to " 441 + "both forward and receive its result"); 442 break; 443 case IActivityManager.START_PERMISSION_DENIED: 444 out.println( 445 "Error: Activity not started, you do not " 446 + "have permission to access it."); 447 break; 448 default: 449 out.println( 450 "Error: Activity not started, unknown error code " + res); 451 break; 452 } 453 if (mWaitOption && launched) { 454 if (result == null) { 455 result = new IActivityManager.WaitResult(); 456 result.who = intent.getComponent(); 457 } 458 System.out.println("Status: " + (result.timeout ? "timeout" : "ok")); 459 if (result.who != null) { 460 System.out.println("Activity: " + result.who.flattenToShortString()); 461 } 462 if (result.thisTime >= 0) { 463 System.out.println("ThisTime: " + result.thisTime); 464 } 465 if (result.totalTime >= 0) { 466 System.out.println("TotalTime: " + result.totalTime); 467 } 468 System.out.println("Complete"); 469 } 470 } 471 runForceStop()472 private void runForceStop() throws Exception { 473 mAm.forceStopPackage(nextArgRequired()); 474 } 475 sendBroadcast()476 private void sendBroadcast() throws Exception { 477 Intent intent = makeIntent(); 478 IntentReceiver receiver = new IntentReceiver(); 479 System.out.println("Broadcasting: " + intent); 480 mAm.broadcastIntent(null, intent, null, receiver, 0, null, null, null, true, false); 481 receiver.waitForFinish(); 482 } 483 runInstrument()484 private void runInstrument() throws Exception { 485 String profileFile = null; 486 boolean wait = false; 487 boolean rawMode = false; 488 boolean no_window_animation = false; 489 Bundle args = new Bundle(); 490 String argKey = null, argValue = null; 491 IWindowManager wm = IWindowManager.Stub.asInterface(ServiceManager.getService("window")); 492 493 String opt; 494 while ((opt=nextOption()) != null) { 495 if (opt.equals("-p")) { 496 profileFile = nextArgRequired(); 497 } else if (opt.equals("-w")) { 498 wait = true; 499 } else if (opt.equals("-r")) { 500 rawMode = true; 501 } else if (opt.equals("-e")) { 502 argKey = nextArgRequired(); 503 argValue = nextArgRequired(); 504 args.putString(argKey, argValue); 505 } else if (opt.equals("--no_window_animation") 506 || opt.equals("--no-window-animation")) { 507 no_window_animation = true; 508 } else { 509 System.err.println("Error: Unknown option: " + opt); 510 showUsage(); 511 return; 512 } 513 } 514 515 String cnArg = nextArgRequired(); 516 ComponentName cn = ComponentName.unflattenFromString(cnArg); 517 if (cn == null) throw new IllegalArgumentException("Bad component name: " + cnArg); 518 519 InstrumentationWatcher watcher = null; 520 if (wait) { 521 watcher = new InstrumentationWatcher(); 522 watcher.setRawOutput(rawMode); 523 } 524 float[] oldAnims = null; 525 if (no_window_animation) { 526 oldAnims = wm.getAnimationScales(); 527 wm.setAnimationScale(0, 0.0f); 528 wm.setAnimationScale(1, 0.0f); 529 } 530 531 if (!mAm.startInstrumentation(cn, profileFile, 0, args, watcher)) { 532 throw new AndroidException("INSTRUMENTATION_FAILED: " + cn.flattenToString()); 533 } 534 535 if (watcher != null) { 536 if (!watcher.waitForFinish()) { 537 System.out.println("INSTRUMENTATION_ABORTED: System has crashed."); 538 } 539 } 540 541 if (oldAnims != null) { 542 wm.setAnimationScales(oldAnims); 543 } 544 } 545 removeWallOption()546 static void removeWallOption() { 547 String props = SystemProperties.get("dalvik.vm.extra-opts"); 548 if (props != null && props.contains("-Xprofile:wallclock")) { 549 props = props.replace("-Xprofile:wallclock", ""); 550 props = props.trim(); 551 SystemProperties.set("dalvik.vm.extra-opts", props); 552 } 553 } 554 runProfile()555 private void runProfile() throws Exception { 556 String profileFile = null; 557 boolean start = false; 558 boolean wall = false; 559 int profileType = 0; 560 561 String process = null; 562 563 String cmd = nextArgRequired(); 564 if ("looper".equals(cmd)) { 565 cmd = nextArgRequired(); 566 profileType = 1; 567 } 568 569 if ("start".equals(cmd)) { 570 start = true; 571 wall = "--wall".equals(nextOption()); 572 process = nextArgRequired(); 573 } else if ("stop".equals(cmd)) { 574 process = nextArg(); 575 } else { 576 // Compatibility with old syntax: process is specified first. 577 process = cmd; 578 cmd = nextArgRequired(); 579 if ("start".equals(cmd)) { 580 start = true; 581 } else if (!"stop".equals(cmd)) { 582 throw new IllegalArgumentException("Profile command " + process + " not valid"); 583 } 584 } 585 586 ParcelFileDescriptor fd = null; 587 588 if (start) { 589 profileFile = nextArgRequired(); 590 try { 591 fd = ParcelFileDescriptor.open( 592 new File(profileFile), 593 ParcelFileDescriptor.MODE_CREATE | 594 ParcelFileDescriptor.MODE_TRUNCATE | 595 ParcelFileDescriptor.MODE_READ_WRITE); 596 } catch (FileNotFoundException e) { 597 System.err.println("Error: Unable to open file: " + profileFile); 598 return; 599 } 600 } 601 602 try { 603 if (wall) { 604 // XXX doesn't work -- this needs to be set before booting. 605 String props = SystemProperties.get("dalvik.vm.extra-opts"); 606 if (props == null || !props.contains("-Xprofile:wallclock")) { 607 props = props + " -Xprofile:wallclock"; 608 //SystemProperties.set("dalvik.vm.extra-opts", props); 609 } 610 } else if (start) { 611 //removeWallOption(); 612 } 613 if (!mAm.profileControl(process, start, profileFile, fd, profileType)) { 614 wall = false; 615 throw new AndroidException("PROFILE FAILED on process " + process); 616 } 617 } finally { 618 if (!wall) { 619 //removeWallOption(); 620 } 621 } 622 } 623 runDumpHeap()624 private void runDumpHeap() throws Exception { 625 boolean managed = !"-n".equals(nextOption()); 626 String process = nextArgRequired(); 627 String heapFile = nextArgRequired(); 628 ParcelFileDescriptor fd = null; 629 630 try { 631 fd = ParcelFileDescriptor.open( 632 new File(heapFile), 633 ParcelFileDescriptor.MODE_CREATE | 634 ParcelFileDescriptor.MODE_TRUNCATE | 635 ParcelFileDescriptor.MODE_READ_WRITE); 636 } catch (FileNotFoundException e) { 637 System.err.println("Error: Unable to open file: " + heapFile); 638 return; 639 } 640 641 if (!mAm.dumpHeap(process, managed, heapFile, fd)) { 642 throw new AndroidException("HEAP DUMP FAILED on process " + process); 643 } 644 } 645 646 class MyActivityController extends IActivityController.Stub { 647 final String mGdbPort; 648 649 static final int STATE_NORMAL = 0; 650 static final int STATE_CRASHED = 1; 651 static final int STATE_EARLY_ANR = 2; 652 static final int STATE_ANR = 3; 653 654 int mState; 655 656 static final int RESULT_DEFAULT = 0; 657 658 static final int RESULT_CRASH_DIALOG = 0; 659 static final int RESULT_CRASH_KILL = 1; 660 661 static final int RESULT_EARLY_ANR_CONTINUE = 0; 662 static final int RESULT_EARLY_ANR_KILL = 1; 663 664 static final int RESULT_ANR_DIALOG = 0; 665 static final int RESULT_ANR_KILL = 1; 666 static final int RESULT_ANR_WAIT = 1; 667 668 int mResult; 669 670 Process mGdbProcess; 671 Thread mGdbThread; 672 boolean mGotGdbPrint; 673 MyActivityController(String gdbPort)674 MyActivityController(String gdbPort) { 675 mGdbPort = gdbPort; 676 } 677 678 @Override activityResuming(String pkg)679 public boolean activityResuming(String pkg) throws RemoteException { 680 synchronized (this) { 681 System.out.println("** Activity resuming: " + pkg); 682 } 683 return true; 684 } 685 686 @Override activityStarting(Intent intent, String pkg)687 public boolean activityStarting(Intent intent, String pkg) throws RemoteException { 688 synchronized (this) { 689 System.out.println("** Activity starting: " + pkg); 690 } 691 return true; 692 } 693 694 @Override appCrashed(String processName, int pid, String shortMsg, String longMsg, long timeMillis, String stackTrace)695 public boolean appCrashed(String processName, int pid, String shortMsg, String longMsg, 696 long timeMillis, String stackTrace) throws RemoteException { 697 synchronized (this) { 698 System.out.println("** ERROR: PROCESS CRASHED"); 699 System.out.println("processName: " + processName); 700 System.out.println("processPid: " + pid); 701 System.out.println("shortMsg: " + shortMsg); 702 System.out.println("longMsg: " + longMsg); 703 System.out.println("timeMillis: " + timeMillis); 704 System.out.println("stack:"); 705 System.out.print(stackTrace); 706 System.out.println("#"); 707 int result = waitControllerLocked(pid, STATE_CRASHED); 708 return result == RESULT_CRASH_KILL ? false : true; 709 } 710 } 711 712 @Override appEarlyNotResponding(String processName, int pid, String annotation)713 public int appEarlyNotResponding(String processName, int pid, String annotation) 714 throws RemoteException { 715 synchronized (this) { 716 System.out.println("** ERROR: EARLY PROCESS NOT RESPONDING"); 717 System.out.println("processName: " + processName); 718 System.out.println("processPid: " + pid); 719 System.out.println("annotation: " + annotation); 720 int result = waitControllerLocked(pid, STATE_EARLY_ANR); 721 if (result == RESULT_EARLY_ANR_KILL) return -1; 722 return 0; 723 } 724 } 725 726 @Override appNotResponding(String processName, int pid, String processStats)727 public int appNotResponding(String processName, int pid, String processStats) 728 throws RemoteException { 729 synchronized (this) { 730 System.out.println("** ERROR: PROCESS NOT RESPONDING"); 731 System.out.println("processName: " + processName); 732 System.out.println("processPid: " + pid); 733 System.out.println("processStats:"); 734 System.out.print(processStats); 735 System.out.println("#"); 736 int result = waitControllerLocked(pid, STATE_ANR); 737 if (result == RESULT_ANR_KILL) return -1; 738 if (result == RESULT_ANR_WAIT) return 1; 739 return 0; 740 } 741 } 742 killGdbLocked()743 void killGdbLocked() { 744 mGotGdbPrint = false; 745 if (mGdbProcess != null) { 746 System.out.println("Stopping gdbserver"); 747 mGdbProcess.destroy(); 748 mGdbProcess = null; 749 } 750 if (mGdbThread != null) { 751 mGdbThread.interrupt(); 752 mGdbThread = null; 753 } 754 } 755 waitControllerLocked(int pid, int state)756 int waitControllerLocked(int pid, int state) { 757 if (mGdbPort != null) { 758 killGdbLocked(); 759 760 try { 761 System.out.println("Starting gdbserver on port " + mGdbPort); 762 System.out.println("Do the following:"); 763 System.out.println(" adb forward tcp:" + mGdbPort + " tcp:" + mGdbPort); 764 System.out.println(" gdbclient app_process :" + mGdbPort); 765 766 mGdbProcess = Runtime.getRuntime().exec(new String[] { 767 "gdbserver", ":" + mGdbPort, "--attach", Integer.toString(pid) 768 }); 769 final InputStreamReader converter = new InputStreamReader( 770 mGdbProcess.getInputStream()); 771 mGdbThread = new Thread() { 772 @Override 773 public void run() { 774 BufferedReader in = new BufferedReader(converter); 775 String line; 776 int count = 0; 777 while (true) { 778 synchronized (MyActivityController.this) { 779 if (mGdbThread == null) { 780 return; 781 } 782 if (count == 2) { 783 mGotGdbPrint = true; 784 MyActivityController.this.notifyAll(); 785 } 786 } 787 try { 788 line = in.readLine(); 789 if (line == null) { 790 return; 791 } 792 System.out.println("GDB: " + line); 793 count++; 794 } catch (IOException e) { 795 return; 796 } 797 } 798 } 799 }; 800 mGdbThread.start(); 801 802 // Stupid waiting for .5s. Doesn't matter if we end early. 803 try { 804 this.wait(500); 805 } catch (InterruptedException e) { 806 } 807 808 } catch (IOException e) { 809 System.err.println("Failure starting gdbserver: " + e); 810 killGdbLocked(); 811 } 812 } 813 mState = state; 814 System.out.println(""); 815 printMessageForState(); 816 817 while (mState != STATE_NORMAL) { 818 try { 819 wait(); 820 } catch (InterruptedException e) { 821 } 822 } 823 824 killGdbLocked(); 825 826 return mResult; 827 } 828 resumeController(int result)829 void resumeController(int result) { 830 synchronized (this) { 831 mState = STATE_NORMAL; 832 mResult = result; 833 notifyAll(); 834 } 835 } 836 printMessageForState()837 void printMessageForState() { 838 switch (mState) { 839 case STATE_NORMAL: 840 System.out.println("Monitoring activity manager... available commands:"); 841 break; 842 case STATE_CRASHED: 843 System.out.println("Waiting after crash... available commands:"); 844 System.out.println("(c)ontinue: show crash dialog"); 845 System.out.println("(k)ill: immediately kill app"); 846 break; 847 case STATE_EARLY_ANR: 848 System.out.println("Waiting after early ANR... available commands:"); 849 System.out.println("(c)ontinue: standard ANR processing"); 850 System.out.println("(k)ill: immediately kill app"); 851 break; 852 case STATE_ANR: 853 System.out.println("Waiting after ANR... available commands:"); 854 System.out.println("(c)ontinue: show ANR dialog"); 855 System.out.println("(k)ill: immediately kill app"); 856 System.out.println("(w)ait: wait some more"); 857 break; 858 } 859 System.out.println("(q)uit: finish monitoring"); 860 } 861 run()862 void run() throws RemoteException { 863 try { 864 printMessageForState(); 865 866 mAm.setActivityController(this); 867 mState = STATE_NORMAL; 868 869 InputStreamReader converter = new InputStreamReader(System.in); 870 BufferedReader in = new BufferedReader(converter); 871 String line; 872 873 while ((line = in.readLine()) != null) { 874 boolean addNewline = true; 875 if (line.length() <= 0) { 876 addNewline = false; 877 } else if ("q".equals(line) || "quit".equals(line)) { 878 resumeController(RESULT_DEFAULT); 879 break; 880 } else if (mState == STATE_CRASHED) { 881 if ("c".equals(line) || "continue".equals(line)) { 882 resumeController(RESULT_CRASH_DIALOG); 883 } else if ("k".equals(line) || "kill".equals(line)) { 884 resumeController(RESULT_CRASH_KILL); 885 } else { 886 System.out.println("Invalid command: " + line); 887 } 888 } else if (mState == STATE_ANR) { 889 if ("c".equals(line) || "continue".equals(line)) { 890 resumeController(RESULT_ANR_DIALOG); 891 } else if ("k".equals(line) || "kill".equals(line)) { 892 resumeController(RESULT_ANR_KILL); 893 } else if ("w".equals(line) || "wait".equals(line)) { 894 resumeController(RESULT_ANR_WAIT); 895 } else { 896 System.out.println("Invalid command: " + line); 897 } 898 } else if (mState == STATE_EARLY_ANR) { 899 if ("c".equals(line) || "continue".equals(line)) { 900 resumeController(RESULT_EARLY_ANR_CONTINUE); 901 } else if ("k".equals(line) || "kill".equals(line)) { 902 resumeController(RESULT_EARLY_ANR_KILL); 903 } else { 904 System.out.println("Invalid command: " + line); 905 } 906 } else { 907 System.out.println("Invalid command: " + line); 908 } 909 910 synchronized (this) { 911 if (addNewline) { 912 System.out.println(""); 913 } 914 printMessageForState(); 915 } 916 } 917 918 } catch (IOException e) { 919 e.printStackTrace(); 920 } finally { 921 mAm.setActivityController(null); 922 } 923 } 924 } 925 runMonitor()926 private void runMonitor() throws Exception { 927 String opt; 928 String gdbPort = null; 929 while ((opt=nextOption()) != null) { 930 if (opt.equals("--gdb")) { 931 gdbPort = nextArgRequired(); 932 } else { 933 System.err.println("Error: Unknown option: " + opt); 934 showUsage(); 935 return; 936 } 937 } 938 939 MyActivityController controller = new MyActivityController(gdbPort); 940 controller.run(); 941 } 942 runScreenCompat()943 private void runScreenCompat() throws Exception { 944 String mode = nextArgRequired(); 945 boolean enabled; 946 if ("on".equals(mode)) { 947 enabled = true; 948 } else if ("off".equals(mode)) { 949 enabled = false; 950 } else { 951 System.err.println("Error: enabled mode must be 'on' or 'off' at " + mode); 952 showUsage(); 953 return; 954 } 955 956 String packageName = nextArgRequired(); 957 do { 958 try { 959 mAm.setPackageScreenCompatMode(packageName, enabled 960 ? ActivityManager.COMPAT_MODE_ENABLED 961 : ActivityManager.COMPAT_MODE_DISABLED); 962 } catch (RemoteException e) { 963 } 964 packageName = nextArg(); 965 } while (packageName != null); 966 } 967 runDisplaySize()968 private void runDisplaySize() throws Exception { 969 String size = nextArgRequired(); 970 int m, n; 971 if ("reset".equals(size)) { 972 m = n = -1; 973 } else { 974 int div = size.indexOf('x'); 975 if (div <= 0 || div >= (size.length()-1)) { 976 System.err.println("Error: bad size " + size); 977 showUsage(); 978 return; 979 } 980 String mstr = size.substring(0, div); 981 String nstr = size.substring(div+1); 982 try { 983 m = Integer.parseInt(mstr); 984 n = Integer.parseInt(nstr); 985 } catch (NumberFormatException e) { 986 System.err.println("Error: bad number " + e); 987 showUsage(); 988 return; 989 } 990 } 991 992 if (m < n) { 993 int tmp = m; 994 m = n; 995 n = tmp; 996 } 997 998 IWindowManager wm = IWindowManager.Stub.asInterface(ServiceManager.checkService( 999 Context.WINDOW_SERVICE)); 1000 if (wm == null) { 1001 System.err.println(NO_SYSTEM_ERROR_CODE); 1002 throw new AndroidException("Can't connect to window manager; is the system running?"); 1003 } 1004 1005 try { 1006 if (m >= 0 && n >= 0) { 1007 wm.setForcedDisplaySize(m, n); 1008 } else { 1009 wm.clearForcedDisplaySize(); 1010 } 1011 } catch (RemoteException e) { 1012 } 1013 } 1014 1015 private class IntentReceiver extends IIntentReceiver.Stub { 1016 private boolean mFinished = false; 1017 performReceive( Intent intent, int rc, String data, Bundle ext, boolean ord, boolean sticky)1018 public synchronized void performReceive( 1019 Intent intent, int rc, String data, Bundle ext, boolean ord, 1020 boolean sticky) { 1021 String line = "Broadcast completed: result=" + rc; 1022 if (data != null) line = line + ", data=\"" + data + "\""; 1023 if (ext != null) line = line + ", extras: " + ext; 1024 System.out.println(line); 1025 mFinished = true; 1026 notifyAll(); 1027 } 1028 waitForFinish()1029 public synchronized void waitForFinish() { 1030 try { 1031 while (!mFinished) wait(); 1032 } catch (InterruptedException e) { 1033 throw new IllegalStateException(e); 1034 } 1035 } 1036 } 1037 1038 private class InstrumentationWatcher extends IInstrumentationWatcher.Stub { 1039 private boolean mFinished = false; 1040 private boolean mRawMode = false; 1041 1042 /** 1043 * Set or reset "raw mode". In "raw mode", all bundles are dumped. In "pretty mode", 1044 * if a bundle includes Instrumentation.REPORT_KEY_STREAMRESULT, just print that. 1045 * @param rawMode true for raw mode, false for pretty mode. 1046 */ setRawOutput(boolean rawMode)1047 public void setRawOutput(boolean rawMode) { 1048 mRawMode = rawMode; 1049 } 1050 instrumentationStatus(ComponentName name, int resultCode, Bundle results)1051 public void instrumentationStatus(ComponentName name, int resultCode, Bundle results) { 1052 synchronized (this) { 1053 // pretty printer mode? 1054 String pretty = null; 1055 if (!mRawMode && results != null) { 1056 pretty = results.getString(Instrumentation.REPORT_KEY_STREAMRESULT); 1057 } 1058 if (pretty != null) { 1059 System.out.print(pretty); 1060 } else { 1061 if (results != null) { 1062 for (String key : results.keySet()) { 1063 System.out.println( 1064 "INSTRUMENTATION_STATUS: " + key + "=" + results.get(key)); 1065 } 1066 } 1067 System.out.println("INSTRUMENTATION_STATUS_CODE: " + resultCode); 1068 } 1069 notifyAll(); 1070 } 1071 } 1072 instrumentationFinished(ComponentName name, int resultCode, Bundle results)1073 public void instrumentationFinished(ComponentName name, int resultCode, 1074 Bundle results) { 1075 synchronized (this) { 1076 // pretty printer mode? 1077 String pretty = null; 1078 if (!mRawMode && results != null) { 1079 pretty = results.getString(Instrumentation.REPORT_KEY_STREAMRESULT); 1080 } 1081 if (pretty != null) { 1082 System.out.println(pretty); 1083 } else { 1084 if (results != null) { 1085 for (String key : results.keySet()) { 1086 System.out.println( 1087 "INSTRUMENTATION_RESULT: " + key + "=" + results.get(key)); 1088 } 1089 } 1090 System.out.println("INSTRUMENTATION_CODE: " + resultCode); 1091 } 1092 mFinished = true; 1093 notifyAll(); 1094 } 1095 } 1096 waitForFinish()1097 public boolean waitForFinish() { 1098 synchronized (this) { 1099 while (!mFinished) { 1100 try { 1101 if (!mAm.asBinder().pingBinder()) { 1102 return false; 1103 } 1104 wait(1000); 1105 } catch (InterruptedException e) { 1106 throw new IllegalStateException(e); 1107 } 1108 } 1109 } 1110 return true; 1111 } 1112 } 1113 nextOption()1114 private String nextOption() { 1115 if (mCurArgData != null) { 1116 String prev = mArgs[mNextArg - 1]; 1117 throw new IllegalArgumentException("No argument expected after \"" + prev + "\""); 1118 } 1119 if (mNextArg >= mArgs.length) { 1120 return null; 1121 } 1122 String arg = mArgs[mNextArg]; 1123 if (!arg.startsWith("-")) { 1124 return null; 1125 } 1126 mNextArg++; 1127 if (arg.equals("--")) { 1128 return null; 1129 } 1130 if (arg.length() > 1 && arg.charAt(1) != '-') { 1131 if (arg.length() > 2) { 1132 mCurArgData = arg.substring(2); 1133 return arg.substring(0, 2); 1134 } else { 1135 mCurArgData = null; 1136 return arg; 1137 } 1138 } 1139 mCurArgData = null; 1140 return arg; 1141 } 1142 nextArg()1143 private String nextArg() { 1144 if (mCurArgData != null) { 1145 String arg = mCurArgData; 1146 mCurArgData = null; 1147 return arg; 1148 } else if (mNextArg < mArgs.length) { 1149 return mArgs[mNextArg++]; 1150 } else { 1151 return null; 1152 } 1153 } 1154 nextArgRequired()1155 private String nextArgRequired() { 1156 String arg = nextArg(); 1157 if (arg == null) { 1158 String prev = mArgs[mNextArg - 1]; 1159 throw new IllegalArgumentException("Argument expected after \"" + prev + "\""); 1160 } 1161 return arg; 1162 } 1163 showUsage()1164 private static void showUsage() { 1165 System.err.println( 1166 "usage: am [subcommand] [options]\n" + 1167 "usage: am start [-D] [-W] [-P <FILE>] [--start-profiler <FILE>] [-S] <INTENT>\n" + 1168 " am startservice <INTENT>\n" + 1169 " am force-stop <PACKAGE>\n" + 1170 " am broadcast <INTENT>\n" + 1171 " am instrument [-r] [-e <NAME> <VALUE>] [-p <FILE>] [-w]\n" + 1172 " [--no-window-animation] <COMPONENT>\n" + 1173 " am profile [looper] start <PROCESS> <FILE>\n" + 1174 " am profile [looper] stop [<PROCESS>]\n" + 1175 " am dumpheap [flags] <PROCESS> <FILE>\n" + 1176 " am monitor [--gdb <port>]\n" + 1177 " am screen-compat [on|off] <PACKAGE>\n" + 1178 " am display-size [reset|MxN]\n" + 1179 "\n" + 1180 "am start: start an Activity. Options are:\n" + 1181 " -D: enable debugging\n" + 1182 " -W: wait for launch to complete\n" + 1183 " --start-profiler <FILE>: start profiler and send results to <FILE>\n" + 1184 " -P <FILE>: like above, but profiling stops when app goes idle\n" + 1185 " -S: force stop the target app before starting the activity\n" + 1186 "\n" + 1187 "am startservice: start a Service.\n" + 1188 "\n" + 1189 "am force-stop: force stop everything associated with <PACKAGE>.\n" + 1190 "\n" + 1191 "am broadcast: send a broadcast Intent.\n" + 1192 "\n" + 1193 "am instrument: start an Instrumentation. Typically this target <COMPONENT>\n" + 1194 " is the form <TEST_PACKAGE>/<RUNNER_CLASS>. Options are:\n" + 1195 " -r: print raw results (otherwise decode REPORT_KEY_STREAMRESULT). Use with\n" + 1196 " [-e perf true] to generate raw output for performance measurements.\n" + 1197 " -e <NAME> <VALUE>: set argument <NAME> to <VALUE>. For test runners a\n" + 1198 " common form is [-e <testrunner_flag> <value>[,<value>...]].\n" + 1199 " -p <FILE>: write profiling data to <FILE>\n" + 1200 " -w: wait for instrumentation to finish before returning. Required for\n" + 1201 " test runners.\n" + 1202 " --no-window-animation: turn off window animations will running.\n" + 1203 "\n" + 1204 "am profile: start and stop profiler on a process.\n" + 1205 "\n" + 1206 "am dumpheap: dump the heap of a process. Options are:\n" + 1207 " -n: dump native heap instead of managed heap\n" + 1208 "\n" + 1209 "am monitor: start monitoring for crashes or ANRs.\n" + 1210 " --gdb: start gdbserv on the given port at crash/ANR\n" + 1211 "\n" + 1212 "am screen-compat: control screen compatibility mode of <PACKAGE>.\n" + 1213 "\n" + 1214 "am display-size: override display size.\n" + 1215 "\n" + 1216 "<INTENT> specifications include these flags and arguments:\n" + 1217 " [-a <ACTION>] [-d <DATA_URI>] [-t <MIME_TYPE>]\n" + 1218 " [-c <CATEGORY> [-c <CATEGORY>] ...]\n" + 1219 " [-e|--es <EXTRA_KEY> <EXTRA_STRING_VALUE> ...]\n" + 1220 " [--esn <EXTRA_KEY> ...]\n" + 1221 " [--ez <EXTRA_KEY> <EXTRA_BOOLEAN_VALUE> ...]\n" + 1222 " [--ei <EXTRA_KEY> <EXTRA_INT_VALUE> ...]\n" + 1223 " [--el <EXTRA_KEY> <EXTRA_LONG_VALUE> ...]\n" + 1224 " [--eu <EXTRA_KEY> <EXTRA_URI_VALUE> ...]\n" + 1225 " [--eia <EXTRA_KEY> <EXTRA_INT_VALUE>[,<EXTRA_INT_VALUE...]]\n" + 1226 " [--ela <EXTRA_KEY> <EXTRA_LONG_VALUE>[,<EXTRA_LONG_VALUE...]]\n" + 1227 " [-n <COMPONENT>] [-f <FLAGS>]\n" + 1228 " [--grant-read-uri-permission] [--grant-write-uri-permission]\n" + 1229 " [--debug-log-resolution] [--exclude-stopped-packages]\n" + 1230 " [--include-stopped-packages]\n" + 1231 " [--activity-brought-to-front] [--activity-clear-top]\n" + 1232 " [--activity-clear-when-task-reset] [--activity-exclude-from-recents]\n" + 1233 " [--activity-launched-from-history] [--activity-multiple-task]\n" + 1234 " [--activity-no-animation] [--activity-no-history]\n" + 1235 " [--activity-no-user-action] [--activity-previous-is-top]\n" + 1236 " [--activity-reorder-to-front] [--activity-reset-task-if-needed]\n" + 1237 " [--activity-single-top] [--activity-clear-task]\n" + 1238 " [--activity-task-on-home]\n" + 1239 " [--receiver-registered-only] [--receiver-replace-pending]\n" + 1240 " [<URI> | <PACKAGE> | <COMPONENT>]\n" 1241 ); 1242 } 1243 } 1244