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