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