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