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