Am.java revision e2515eebf42c763c0a2d9f873a153711778cfc17
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 19package com.android.commands.am; 20 21import android.app.ActivityManagerNative; 22import android.app.IActivityController; 23import android.app.IActivityManager; 24import android.app.IInstrumentationWatcher; 25import android.app.Instrumentation; 26import android.content.ComponentName; 27import android.content.IIntentReceiver; 28import android.content.Intent; 29import android.net.Uri; 30import android.os.Bundle; 31import android.os.ParcelFileDescriptor; 32import android.os.RemoteException; 33import android.os.ServiceManager; 34import android.util.AndroidException; 35import android.view.IWindowManager; 36 37import java.io.BufferedReader; 38import java.io.File; 39import java.io.FileNotFoundException; 40import java.io.IOException; 41import java.io.InputStreamReader; 42import java.io.PrintStream; 43import java.net.URISyntaxException; 44import java.util.Iterator; 45import java.util.Set; 46 47public class Am { 48 49 private IActivityManager mAm; 50 private String[] mArgs; 51 private int mNextArg; 52 private String mCurArgData; 53 54 private boolean mDebugOption = false; 55 private boolean mWaitOption = false; 56 57 // These are magic strings understood by the Eclipse plugin. 58 private static final String FATAL_ERROR_CODE = "Error type 1"; 59 private static final String NO_SYSTEM_ERROR_CODE = "Error type 2"; 60 private static final String NO_CLASS_ERROR_CODE = "Error type 3"; 61 62 /** 63 * Command-line entry point. 64 * 65 * @param args The command-line arguments 66 */ 67 public static void main(String[] args) { 68 try { 69 (new Am()).run(args); 70 } catch (IllegalArgumentException e) { 71 showUsage(); 72 System.err.println("Error: " + e.getMessage()); 73 } catch (Exception e) { 74 System.err.println(e.toString()); 75 System.exit(1); 76 } 77 } 78 79 private void run(String[] args) throws Exception { 80 if (args.length < 1) { 81 showUsage(); 82 return; 83 } 84 85 mAm = ActivityManagerNative.getDefault(); 86 if (mAm == null) { 87 System.err.println(NO_SYSTEM_ERROR_CODE); 88 throw new AndroidException("Can't connect to activity manager; is the system running?"); 89 } 90 91 mArgs = args; 92 String op = args[0]; 93 mNextArg = 1; 94 95 if (op.equals("start")) { 96 runStart(); 97 } else if (op.equals("startservice")) { 98 runStartService(); 99 } else if (op.equals("instrument")) { 100 runInstrument(); 101 } else if (op.equals("broadcast")) { 102 sendBroadcast(); 103 } else if (op.equals("profile")) { 104 runProfile(); 105 } else if (op.equals("dumpheap")) { 106 runDumpHeap(); 107 } else if (op.equals("monitor")) { 108 runMonitor(); 109 } else if (op.equals("screen-compat")) { 110 runScreenCompat(); 111 } else { 112 throw new IllegalArgumentException("Unknown command: " + op); 113 } 114 } 115 116 private Intent makeIntent() throws URISyntaxException { 117 Intent intent = new Intent(); 118 boolean hasIntentInfo = false; 119 120 mDebugOption = false; 121 mWaitOption = false; 122 Uri data = null; 123 String type = null; 124 125 String opt; 126 while ((opt=nextOption()) != null) { 127 if (opt.equals("-a")) { 128 intent.setAction(nextArgRequired()); 129 hasIntentInfo = true; 130 } else if (opt.equals("-d")) { 131 data = Uri.parse(nextArgRequired()); 132 hasIntentInfo = true; 133 } else if (opt.equals("-t")) { 134 type = nextArgRequired(); 135 hasIntentInfo = true; 136 } else if (opt.equals("-c")) { 137 intent.addCategory(nextArgRequired()); 138 hasIntentInfo = true; 139 } else if (opt.equals("-e") || opt.equals("--es")) { 140 String key = nextArgRequired(); 141 String value = nextArgRequired(); 142 intent.putExtra(key, value); 143 hasIntentInfo = true; 144 } else if (opt.equals("--esn")) { 145 String key = nextArgRequired(); 146 intent.putExtra(key, (String) null); 147 hasIntentInfo = true; 148 } else if (opt.equals("--ei")) { 149 String key = nextArgRequired(); 150 String value = nextArgRequired(); 151 intent.putExtra(key, Integer.valueOf(value)); 152 hasIntentInfo = true; 153 } else if (opt.equals("--eia")) { 154 String key = nextArgRequired(); 155 String value = nextArgRequired(); 156 String[] strings = value.split(","); 157 int[] list = new int[strings.length]; 158 for (int i = 0; i < strings.length; i++) { 159 list[i] = Integer.valueOf(strings[i]); 160 } 161 intent.putExtra(key, list); 162 hasIntentInfo = true; 163 } else if (opt.equals("--el")) { 164 String key = nextArgRequired(); 165 String value = nextArgRequired(); 166 intent.putExtra(key, Long.valueOf(value)); 167 hasIntentInfo = true; 168 } else if (opt.equals("--ela")) { 169 String key = nextArgRequired(); 170 String value = nextArgRequired(); 171 String[] strings = value.split(","); 172 long[] list = new long[strings.length]; 173 for (int i = 0; i < strings.length; i++) { 174 list[i] = Long.valueOf(strings[i]); 175 } 176 intent.putExtra(key, list); 177 hasIntentInfo = true; 178 } else if (opt.equals("--ez")) { 179 String key = nextArgRequired(); 180 String value = nextArgRequired(); 181 intent.putExtra(key, Boolean.valueOf(value)); 182 hasIntentInfo = true; 183 } else if (opt.equals("-n")) { 184 String str = nextArgRequired(); 185 ComponentName cn = ComponentName.unflattenFromString(str); 186 if (cn == null) throw new IllegalArgumentException("Bad component name: " + str); 187 intent.setComponent(cn); 188 hasIntentInfo = true; 189 } else if (opt.equals("-f")) { 190 String str = nextArgRequired(); 191 intent.setFlags(Integer.decode(str).intValue()); 192 } else if (opt.equals("--grant-read-uri-permission")) { 193 intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); 194 } else if (opt.equals("--grant-write-uri-permission")) { 195 intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION); 196 } else if (opt.equals("--debug-log-resolution")) { 197 intent.addFlags(Intent.FLAG_DEBUG_LOG_RESOLUTION); 198 } else if (opt.equals("--activity-brought-to-front")) { 199 intent.addFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT); 200 } else if (opt.equals("--activity-clear-top")) { 201 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); 202 } else if (opt.equals("--activity-clear-when-task-reset")) { 203 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET); 204 } else if (opt.equals("--activity-exclude-from-recents")) { 205 intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); 206 } else if (opt.equals("--activity-launched-from-history")) { 207 intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY); 208 } else if (opt.equals("--activity-multiple-task")) { 209 intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK); 210 } else if (opt.equals("--activity-no-animation")) { 211 intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION); 212 } else if (opt.equals("--activity-no-history")) { 213 intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY); 214 } else if (opt.equals("--activity-no-user-action")) { 215 intent.addFlags(Intent.FLAG_ACTIVITY_NO_USER_ACTION); 216 } else if (opt.equals("--activity-previous-is-top")) { 217 intent.addFlags(Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP); 218 } else if (opt.equals("--activity-reorder-to-front")) { 219 intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT); 220 } else if (opt.equals("--activity-reset-task-if-needed")) { 221 intent.addFlags(Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); 222 } else if (opt.equals("--activity-single-top")) { 223 intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); 224 } else if (opt.equals("--receiver-registered-only")) { 225 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); 226 } else if (opt.equals("--receiver-replace-pending")) { 227 intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING); 228 } else if (opt.equals("-D")) { 229 mDebugOption = true; 230 } else if (opt.equals("-W")) { 231 mWaitOption = true; 232 } else { 233 System.err.println("Error: Unknown option: " + opt); 234 showUsage(); 235 return null; 236 } 237 } 238 intent.setDataAndType(data, type); 239 240 String uri = nextArg(); 241 if (uri != null) { 242 Intent oldIntent = intent; 243 intent = Intent.parseUri(uri, 0); 244 if (oldIntent.getAction() != null) { 245 intent.setAction(oldIntent.getAction()); 246 } 247 if (oldIntent.getData() != null || oldIntent.getType() != null) { 248 intent.setDataAndType(oldIntent.getData(), oldIntent.getType()); 249 } 250 Set cats = oldIntent.getCategories(); 251 if (cats != null) { 252 Iterator it = cats.iterator(); 253 while (it.hasNext()) { 254 intent.addCategory((String)it.next()); 255 } 256 } 257 hasIntentInfo = true; 258 } 259 260 if (!hasIntentInfo) throw new IllegalArgumentException("No intent supplied"); 261 return intent; 262 } 263 264 private void runStartService() throws Exception { 265 Intent intent = makeIntent(); 266 System.out.println("Starting service: " + intent); 267 ComponentName cn = mAm.startService(null, intent, intent.getType()); 268 if (cn == null) { 269 System.err.println("Error: Not found; no service started."); 270 } 271 } 272 273 private void runStart() throws Exception { 274 Intent intent = makeIntent(); 275 System.out.println("Starting: " + intent); 276 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 277 // XXX should do something to determine the MIME type. 278 IActivityManager.WaitResult result = null; 279 int res; 280 if (mWaitOption) { 281 result = mAm.startActivityAndWait(null, intent, intent.getType(), 282 null, 0, null, null, 0, false, mDebugOption); 283 res = result.result; 284 } else { 285 res = mAm.startActivity(null, intent, intent.getType(), 286 null, 0, null, null, 0, false, mDebugOption); 287 } 288 PrintStream out = mWaitOption ? System.out : System.err; 289 boolean launched = false; 290 switch (res) { 291 case IActivityManager.START_SUCCESS: 292 launched = true; 293 break; 294 case IActivityManager.START_SWITCHES_CANCELED: 295 launched = true; 296 out.println( 297 "Warning: Activity not started because the " 298 + " current activity is being kept for the user."); 299 break; 300 case IActivityManager.START_DELIVERED_TO_TOP: 301 launched = true; 302 out.println( 303 "Warning: Activity not started, intent has " 304 + "been delivered to currently running " 305 + "top-most instance."); 306 break; 307 case IActivityManager.START_RETURN_INTENT_TO_CALLER: 308 launched = true; 309 out.println( 310 "Warning: Activity not started because intent " 311 + "should be handled by the caller"); 312 break; 313 case IActivityManager.START_TASK_TO_FRONT: 314 launched = true; 315 out.println( 316 "Warning: Activity not started, its current " 317 + "task has been brought to the front"); 318 break; 319 case IActivityManager.START_INTENT_NOT_RESOLVED: 320 out.println( 321 "Error: Activity not started, unable to " 322 + "resolve " + intent.toString()); 323 break; 324 case IActivityManager.START_CLASS_NOT_FOUND: 325 out.println(NO_CLASS_ERROR_CODE); 326 out.println("Error: Activity class " + 327 intent.getComponent().toShortString() 328 + " does not exist."); 329 break; 330 case IActivityManager.START_FORWARD_AND_REQUEST_CONFLICT: 331 out.println( 332 "Error: Activity not started, you requested to " 333 + "both forward and receive its result"); 334 break; 335 case IActivityManager.START_PERMISSION_DENIED: 336 out.println( 337 "Error: Activity not started, you do not " 338 + "have permission to access it."); 339 break; 340 default: 341 out.println( 342 "Error: Activity not started, unknown error code " + res); 343 break; 344 } 345 if (mWaitOption && launched) { 346 if (result == null) { 347 result = new IActivityManager.WaitResult(); 348 result.who = intent.getComponent(); 349 } 350 System.out.println("Status: " + (result.timeout ? "timeout" : "ok")); 351 if (result.who != null) { 352 System.out.println("Activity: " + result.who.flattenToShortString()); 353 } 354 if (result.thisTime >= 0) { 355 System.out.println("ThisTime: " + result.thisTime); 356 } 357 if (result.totalTime >= 0) { 358 System.out.println("TotalTime: " + result.totalTime); 359 } 360 System.out.println("Complete"); 361 } 362 } 363 364 private void sendBroadcast() throws Exception { 365 Intent intent = makeIntent(); 366 IntentReceiver receiver = new IntentReceiver(); 367 System.out.println("Broadcasting: " + intent); 368 mAm.broadcastIntent(null, intent, null, receiver, 0, null, null, null, true, false); 369 receiver.waitForFinish(); 370 } 371 372 private void runInstrument() throws Exception { 373 String profileFile = null; 374 boolean wait = false; 375 boolean rawMode = false; 376 boolean no_window_animation = false; 377 Bundle args = new Bundle(); 378 String argKey = null, argValue = null; 379 IWindowManager wm = IWindowManager.Stub.asInterface(ServiceManager.getService("window")); 380 381 String opt; 382 while ((opt=nextOption()) != null) { 383 if (opt.equals("-p")) { 384 profileFile = nextArgRequired(); 385 } else if (opt.equals("-w")) { 386 wait = true; 387 } else if (opt.equals("-r")) { 388 rawMode = true; 389 } else if (opt.equals("-e")) { 390 argKey = nextArgRequired(); 391 argValue = nextArgRequired(); 392 args.putString(argKey, argValue); 393 } else if (opt.equals("--no_window_animation")) { 394 no_window_animation = true; 395 } else { 396 System.err.println("Error: Unknown option: " + opt); 397 showUsage(); 398 return; 399 } 400 } 401 402 String cnArg = nextArgRequired(); 403 ComponentName cn = ComponentName.unflattenFromString(cnArg); 404 if (cn == null) throw new IllegalArgumentException("Bad component name: " + cnArg); 405 406 InstrumentationWatcher watcher = null; 407 if (wait) { 408 watcher = new InstrumentationWatcher(); 409 watcher.setRawOutput(rawMode); 410 } 411 float[] oldAnims = null; 412 if (no_window_animation) { 413 oldAnims = wm.getAnimationScales(); 414 wm.setAnimationScale(0, 0.0f); 415 wm.setAnimationScale(1, 0.0f); 416 } 417 418 if (!mAm.startInstrumentation(cn, profileFile, 0, args, watcher)) { 419 throw new AndroidException("INSTRUMENTATION_FAILED: " + cn.flattenToString()); 420 } 421 422 if (watcher != null) { 423 if (!watcher.waitForFinish()) { 424 System.out.println("INSTRUMENTATION_ABORTED: System has crashed."); 425 } 426 } 427 428 if (oldAnims != null) { 429 wm.setAnimationScales(oldAnims); 430 } 431 } 432 433 private void runProfile() throws Exception { 434 String profileFile = null; 435 boolean start = false; 436 String process = nextArgRequired(); 437 ParcelFileDescriptor fd = null; 438 439 String cmd = nextArgRequired(); 440 if ("start".equals(cmd)) { 441 start = true; 442 profileFile = nextArgRequired(); 443 try { 444 fd = ParcelFileDescriptor.open( 445 new File(profileFile), 446 ParcelFileDescriptor.MODE_CREATE | 447 ParcelFileDescriptor.MODE_TRUNCATE | 448 ParcelFileDescriptor.MODE_READ_WRITE); 449 } catch (FileNotFoundException e) { 450 System.err.println("Error: Unable to open file: " + profileFile); 451 return; 452 } 453 } else if (!"stop".equals(cmd)) { 454 throw new IllegalArgumentException("Profile command " + cmd + " not valid"); 455 } 456 457 if (!mAm.profileControl(process, start, profileFile, fd)) { 458 throw new AndroidException("PROFILE FAILED on process " + process); 459 } 460 } 461 462 private void runDumpHeap() throws Exception { 463 boolean managed = !"-n".equals(nextOption()); 464 String process = nextArgRequired(); 465 String heapFile = nextArgRequired(); 466 ParcelFileDescriptor fd = null; 467 468 try { 469 fd = ParcelFileDescriptor.open( 470 new File(heapFile), 471 ParcelFileDescriptor.MODE_CREATE | 472 ParcelFileDescriptor.MODE_TRUNCATE | 473 ParcelFileDescriptor.MODE_READ_WRITE); 474 } catch (FileNotFoundException e) { 475 System.err.println("Error: Unable to open file: " + heapFile); 476 return; 477 } 478 479 if (!mAm.dumpHeap(process, managed, heapFile, fd)) { 480 throw new AndroidException("HEAP DUMP FAILED on process " + process); 481 } 482 } 483 484 class MyActivityController extends IActivityController.Stub { 485 final String mGdbPort; 486 487 static final int STATE_NORMAL = 0; 488 static final int STATE_CRASHED = 1; 489 static final int STATE_EARLY_ANR = 2; 490 static final int STATE_ANR = 3; 491 492 int mState; 493 494 static final int RESULT_DEFAULT = 0; 495 496 static final int RESULT_CRASH_DIALOG = 0; 497 static final int RESULT_CRASH_KILL = 1; 498 499 static final int RESULT_EARLY_ANR_CONTINUE = 0; 500 static final int RESULT_EARLY_ANR_KILL = 1; 501 502 static final int RESULT_ANR_DIALOG = 0; 503 static final int RESULT_ANR_KILL = 1; 504 static final int RESULT_ANR_WAIT = 1; 505 506 int mResult; 507 508 Process mGdbProcess; 509 Thread mGdbThread; 510 boolean mGotGdbPrint; 511 512 MyActivityController(String gdbPort) { 513 mGdbPort = gdbPort; 514 } 515 516 @Override 517 public boolean activityResuming(String pkg) throws RemoteException { 518 synchronized (this) { 519 System.out.println("** Activity resuming: " + pkg); 520 } 521 return true; 522 } 523 524 @Override 525 public boolean activityStarting(Intent intent, String pkg) throws RemoteException { 526 synchronized (this) { 527 System.out.println("** Activity starting: " + pkg); 528 } 529 return true; 530 } 531 532 @Override 533 public boolean appCrashed(String processName, int pid, String shortMsg, String longMsg, 534 long timeMillis, String stackTrace) throws RemoteException { 535 synchronized (this) { 536 System.out.println("** ERROR: PROCESS CRASHED"); 537 System.out.println("processName: " + processName); 538 System.out.println("processPid: " + pid); 539 System.out.println("shortMsg: " + shortMsg); 540 System.out.println("longMsg: " + longMsg); 541 System.out.println("timeMillis: " + timeMillis); 542 System.out.println("stack:"); 543 System.out.print(stackTrace); 544 System.out.println("#"); 545 int result = waitControllerLocked(pid, STATE_CRASHED); 546 return result == RESULT_CRASH_KILL ? false : true; 547 } 548 } 549 550 @Override 551 public int appEarlyNotResponding(String processName, int pid, String annotation) 552 throws RemoteException { 553 synchronized (this) { 554 System.out.println("** ERROR: EARLY PROCESS NOT RESPONDING"); 555 System.out.println("processName: " + processName); 556 System.out.println("processPid: " + pid); 557 System.out.println("annotation: " + annotation); 558 int result = waitControllerLocked(pid, STATE_EARLY_ANR); 559 if (result == RESULT_EARLY_ANR_KILL) return -1; 560 return 0; 561 } 562 } 563 564 @Override 565 public int appNotResponding(String processName, int pid, String processStats) 566 throws RemoteException { 567 synchronized (this) { 568 System.out.println("** ERROR: PROCESS NOT RESPONDING"); 569 System.out.println("processName: " + processName); 570 System.out.println("processPid: " + pid); 571 System.out.println("processStats:"); 572 System.out.print(processStats); 573 System.out.println("#"); 574 int result = waitControllerLocked(pid, STATE_ANR); 575 if (result == RESULT_ANR_KILL) return -1; 576 if (result == RESULT_ANR_WAIT) return 1; 577 return 0; 578 } 579 } 580 581 void killGdbLocked() { 582 mGotGdbPrint = false; 583 if (mGdbProcess != null) { 584 System.out.println("Stopping gdbserver"); 585 mGdbProcess.destroy(); 586 mGdbProcess = null; 587 } 588 if (mGdbThread != null) { 589 mGdbThread.interrupt(); 590 mGdbThread = null; 591 } 592 } 593 594 int waitControllerLocked(int pid, int state) { 595 if (mGdbPort != null) { 596 killGdbLocked(); 597 598 try { 599 System.out.println("Starting gdbserver on port " + mGdbPort); 600 System.out.println("Do the following:"); 601 System.out.println(" adb forward tcp:" + mGdbPort + " tcp:" + mGdbPort); 602 System.out.println(" gdbclient app_process :" + mGdbPort); 603 604 mGdbProcess = Runtime.getRuntime().exec(new String[] { 605 "gdbserver", ":" + mGdbPort, "--attach", Integer.toString(pid) 606 }); 607 final InputStreamReader converter = new InputStreamReader( 608 mGdbProcess.getInputStream()); 609 mGdbThread = new Thread() { 610 @Override 611 public void run() { 612 BufferedReader in = new BufferedReader(converter); 613 String line; 614 int count = 0; 615 while (true) { 616 synchronized (MyActivityController.this) { 617 if (mGdbThread == null) { 618 return; 619 } 620 if (count == 2) { 621 mGotGdbPrint = true; 622 MyActivityController.this.notifyAll(); 623 } 624 } 625 try { 626 line = in.readLine(); 627 if (line == null) { 628 return; 629 } 630 System.out.println("GDB: " + line); 631 count++; 632 } catch (IOException e) { 633 return; 634 } 635 } 636 } 637 }; 638 mGdbThread.start(); 639 640 // Stupid waiting for .5s. Doesn't matter if we end early. 641 try { 642 this.wait(500); 643 } catch (InterruptedException e) { 644 } 645 646 } catch (IOException e) { 647 System.err.println("Failure starting gdbserver: " + e); 648 killGdbLocked(); 649 } 650 } 651 mState = state; 652 System.out.println(""); 653 printMessageForState(); 654 655 while (mState != STATE_NORMAL) { 656 try { 657 wait(); 658 } catch (InterruptedException e) { 659 } 660 } 661 662 killGdbLocked(); 663 664 return mResult; 665 } 666 667 void resumeController(int result) { 668 synchronized (this) { 669 mState = STATE_NORMAL; 670 mResult = result; 671 notifyAll(); 672 } 673 } 674 675 void printMessageForState() { 676 switch (mState) { 677 case STATE_NORMAL: 678 System.out.println("Monitoring activity manager... available commands:"); 679 break; 680 case STATE_CRASHED: 681 System.out.println("Waiting after crash... available commands:"); 682 System.out.println("(c)ontinue: show crash dialog"); 683 System.out.println("(k)ill: immediately kill app"); 684 break; 685 case STATE_EARLY_ANR: 686 System.out.println("Waiting after early ANR... available commands:"); 687 System.out.println("(c)ontinue: standard ANR processing"); 688 System.out.println("(k)ill: immediately kill app"); 689 break; 690 case STATE_ANR: 691 System.out.println("Waiting after ANR... available commands:"); 692 System.out.println("(c)ontinue: show ANR dialog"); 693 System.out.println("(k)ill: immediately kill app"); 694 System.out.println("(w)ait: wait some more"); 695 break; 696 } 697 System.out.println("(q)uit: finish monitoring"); 698 } 699 700 void run() throws RemoteException { 701 try { 702 printMessageForState(); 703 704 mAm.setActivityController(this); 705 mState = STATE_NORMAL; 706 707 InputStreamReader converter = new InputStreamReader(System.in); 708 BufferedReader in = new BufferedReader(converter); 709 String line; 710 711 while ((line = in.readLine()) != null) { 712 boolean addNewline = true; 713 if (line.length() <= 0) { 714 addNewline = false; 715 } else if ("q".equals(line) || "quit".equals(line)) { 716 resumeController(RESULT_DEFAULT); 717 break; 718 } else if (mState == STATE_CRASHED) { 719 if ("c".equals(line) || "continue".equals(line)) { 720 resumeController(RESULT_CRASH_DIALOG); 721 } else if ("k".equals(line) || "kill".equals(line)) { 722 resumeController(RESULT_CRASH_KILL); 723 } else { 724 System.out.println("Invalid command: " + line); 725 } 726 } else if (mState == STATE_ANR) { 727 if ("c".equals(line) || "continue".equals(line)) { 728 resumeController(RESULT_ANR_DIALOG); 729 } else if ("k".equals(line) || "kill".equals(line)) { 730 resumeController(RESULT_ANR_KILL); 731 } else if ("w".equals(line) || "wait".equals(line)) { 732 resumeController(RESULT_ANR_WAIT); 733 } else { 734 System.out.println("Invalid command: " + line); 735 } 736 } else if (mState == STATE_EARLY_ANR) { 737 if ("c".equals(line) || "continue".equals(line)) { 738 resumeController(RESULT_EARLY_ANR_CONTINUE); 739 } else if ("k".equals(line) || "kill".equals(line)) { 740 resumeController(RESULT_EARLY_ANR_KILL); 741 } else { 742 System.out.println("Invalid command: " + line); 743 } 744 } else { 745 System.out.println("Invalid command: " + line); 746 } 747 748 synchronized (this) { 749 if (addNewline) { 750 System.out.println(""); 751 } 752 printMessageForState(); 753 } 754 } 755 756 } catch (IOException e) { 757 e.printStackTrace(); 758 } finally { 759 mAm.setActivityController(null); 760 } 761 } 762 } 763 764 private void runMonitor() throws Exception { 765 String opt; 766 String gdbPort = null; 767 while ((opt=nextOption()) != null) { 768 if (opt.equals("--gdb")) { 769 gdbPort = nextArgRequired(); 770 } else { 771 System.err.println("Error: Unknown option: " + opt); 772 showUsage(); 773 return; 774 } 775 } 776 777 MyActivityController controller = new MyActivityController(gdbPort); 778 controller.run(); 779 } 780 781 private void runScreenCompat() throws Exception { 782 String mode = nextArgRequired(); 783 boolean enabled; 784 if ("on".equals(mode)) { 785 enabled = true; 786 } else if ("off".equals(mode)) { 787 enabled = false; 788 } else { 789 System.err.println("Error: enabled mode must be 'on' or 'off' at " + mode); 790 showUsage(); 791 return; 792 } 793 794 String packageName = nextArgRequired(); 795 do { 796 try { 797 mAm.setPackageScreenCompatMode(packageName, enabled); 798 } catch (RemoteException e) { 799 } 800 packageName = nextArg(); 801 } while (packageName != null); 802 } 803 804 private class IntentReceiver extends IIntentReceiver.Stub { 805 private boolean mFinished = false; 806 807 public synchronized void performReceive( 808 Intent intent, int rc, String data, Bundle ext, boolean ord, 809 boolean sticky) { 810 String line = "Broadcast completed: result=" + rc; 811 if (data != null) line = line + ", data=\"" + data + "\""; 812 if (ext != null) line = line + ", extras: " + ext; 813 System.out.println(line); 814 mFinished = true; 815 notifyAll(); 816 } 817 818 public synchronized void waitForFinish() { 819 try { 820 while (!mFinished) wait(); 821 } catch (InterruptedException e) { 822 throw new IllegalStateException(e); 823 } 824 } 825 } 826 827 private class InstrumentationWatcher extends IInstrumentationWatcher.Stub { 828 private boolean mFinished = false; 829 private boolean mRawMode = false; 830 831 /** 832 * Set or reset "raw mode". In "raw mode", all bundles are dumped. In "pretty mode", 833 * if a bundle includes Instrumentation.REPORT_KEY_STREAMRESULT, just print that. 834 * @param rawMode true for raw mode, false for pretty mode. 835 */ 836 public void setRawOutput(boolean rawMode) { 837 mRawMode = rawMode; 838 } 839 840 public void instrumentationStatus(ComponentName name, int resultCode, Bundle results) { 841 synchronized (this) { 842 // pretty printer mode? 843 String pretty = null; 844 if (!mRawMode && results != null) { 845 pretty = results.getString(Instrumentation.REPORT_KEY_STREAMRESULT); 846 } 847 if (pretty != null) { 848 System.out.print(pretty); 849 } else { 850 if (results != null) { 851 for (String key : results.keySet()) { 852 System.out.println( 853 "INSTRUMENTATION_STATUS: " + key + "=" + results.get(key)); 854 } 855 } 856 System.out.println("INSTRUMENTATION_STATUS_CODE: " + resultCode); 857 } 858 notifyAll(); 859 } 860 } 861 862 public void instrumentationFinished(ComponentName name, int resultCode, 863 Bundle results) { 864 synchronized (this) { 865 // pretty printer mode? 866 String pretty = null; 867 if (!mRawMode && results != null) { 868 pretty = results.getString(Instrumentation.REPORT_KEY_STREAMRESULT); 869 } 870 if (pretty != null) { 871 System.out.println(pretty); 872 } else { 873 if (results != null) { 874 for (String key : results.keySet()) { 875 System.out.println( 876 "INSTRUMENTATION_RESULT: " + key + "=" + results.get(key)); 877 } 878 } 879 System.out.println("INSTRUMENTATION_CODE: " + resultCode); 880 } 881 mFinished = true; 882 notifyAll(); 883 } 884 } 885 886 public boolean waitForFinish() { 887 synchronized (this) { 888 while (!mFinished) { 889 try { 890 if (!mAm.asBinder().pingBinder()) { 891 return false; 892 } 893 wait(1000); 894 } catch (InterruptedException e) { 895 throw new IllegalStateException(e); 896 } 897 } 898 } 899 return true; 900 } 901 } 902 903 private String nextOption() { 904 if (mCurArgData != null) { 905 String prev = mArgs[mNextArg - 1]; 906 throw new IllegalArgumentException("No argument expected after \"" + prev + "\""); 907 } 908 if (mNextArg >= mArgs.length) { 909 return null; 910 } 911 String arg = mArgs[mNextArg]; 912 if (!arg.startsWith("-")) { 913 return null; 914 } 915 mNextArg++; 916 if (arg.equals("--")) { 917 return null; 918 } 919 if (arg.length() > 1 && arg.charAt(1) != '-') { 920 if (arg.length() > 2) { 921 mCurArgData = arg.substring(2); 922 return arg.substring(0, 2); 923 } else { 924 mCurArgData = null; 925 return arg; 926 } 927 } 928 mCurArgData = null; 929 return arg; 930 } 931 932 private String nextArg() { 933 if (mCurArgData != null) { 934 String arg = mCurArgData; 935 mCurArgData = null; 936 return arg; 937 } else if (mNextArg < mArgs.length) { 938 return mArgs[mNextArg++]; 939 } else { 940 return null; 941 } 942 } 943 944 private String nextArgRequired() { 945 String arg = nextArg(); 946 if (arg == null) { 947 String prev = mArgs[mNextArg - 1]; 948 throw new IllegalArgumentException("Argument expected after \"" + prev + "\""); 949 } 950 return arg; 951 } 952 953 private static void showUsage() { 954 System.err.println( 955 "usage: am [subcommand] [options]\n" + 956 "\n" + 957 " start an Activity: am start [-D] [-W] <INTENT>\n" + 958 " -D: enable debugging\n" + 959 " -W: wait for launch to complete\n" + 960 "\n" + 961 " start a Service: am startservice <INTENT>\n" + 962 "\n" + 963 " send a broadcast Intent: am broadcast <INTENT>\n" + 964 "\n" + 965 " start an Instrumentation: am instrument [flags] <COMPONENT>\n" + 966 " -r: print raw results (otherwise decode REPORT_KEY_STREAMRESULT)\n" + 967 " -e <NAME> <VALUE>: set argument <NAME> to <VALUE>\n" + 968 " -p <FILE>: write profiling data to <FILE>\n" + 969 " -w: wait for instrumentation to finish before returning\n" + 970 "\n" + 971 " run a test package against an application: am instrument [flags] <TEST_PACKAGE>/<RUNNER_CLASS>\n" + 972 " -e <testrunner_flag> <testrunner_value> [,<testrunner_value>]\n" + 973 " -w wait for the test to finish (required)\n" + 974 " -r use with -e perf true to generate raw output for performance measurements\n" + 975 "\n" + 976 " start profiling: am profile <PROCESS> start <FILE>\n" + 977 " stop profiling: am profile <PROCESS> stop\n" + 978 " dump heap: am dumpheap [flags] <PROCESS> <FILE>\n" + 979 " -n: dump native heap instead of managed heap\n" + 980 "\n" + 981 " start monitoring: am monitor [--gdb <port>]\n" + 982 " --gdb: start gdbserv on the given port at crash/ANR\n" + 983 "\n" + 984 " control screen compatibility: am screen-compat [on|off] [package]\n" + 985 "\n" + 986 " <INTENT> specifications include these flags:\n" + 987 " [-a <ACTION>] [-d <DATA_URI>] [-t <MIME_TYPE>]\n" + 988 " [-c <CATEGORY> [-c <CATEGORY>] ...]\n" + 989 " [-e|--es <EXTRA_KEY> <EXTRA_STRING_VALUE> ...]\n" + 990 " [--esn <EXTRA_KEY> ...]\n" + 991 " [--ez <EXTRA_KEY> <EXTRA_BOOLEAN_VALUE> ...]\n" + 992 " [--ei <EXTRA_KEY> <EXTRA_INT_VALUE> ...]\n" + 993 " [--el <EXTRA_KEY> <EXTRA_LONG_VALUE> ...]\n" + 994 " [--eia <EXTRA_KEY> <EXTRA_INT_VALUE>[,<EXTRA_INT_VALUE...]]\n" + 995 " [--ela <EXTRA_KEY> <EXTRA_LONG_VALUE>[,<EXTRA_LONG_VALUE...]]\n" + 996 " [-n <COMPONENT>] [-f <FLAGS>]\n" + 997 " [--grant-read-uri-permission] [--grant-write-uri-permission]\n" + 998 " [--debug-log-resolution]\n" + 999 " [--activity-brought-to-front] [--activity-clear-top]\n" + 1000 " [--activity-clear-when-task-reset] [--activity-exclude-from-recents]\n" + 1001 " [--activity-launched-from-history] [--activity-multiple-task]\n" + 1002 " [--activity-no-animation] [--activity-no-history]\n" + 1003 " [--activity-no-user-action] [--activity-previous-is-top]\n" + 1004 " [--activity-reorder-to-front] [--activity-reset-task-if-needed]\n" + 1005 " [--activity-single-top]\n" + 1006 " [--receiver-registered-only] [--receiver-replace-pending]\n" + 1007 " [<URI>]\n" 1008 ); 1009 } 1010} 1011