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