Am.java revision 6a69b4fbaee9a8251401453cd2d3509d52f5b91c
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.ActivityManagerNative; 22import android.app.IActivityManager; 23import android.app.IInstrumentationWatcher; 24import android.app.Instrumentation; 25import android.content.ComponentName; 26import android.content.IIntentReceiver; 27import android.content.Intent; 28import android.net.Uri; 29import android.os.Bundle; 30import android.os.ParcelFileDescriptor; 31import android.os.RemoteException; 32import android.os.ServiceManager; 33import android.util.AndroidException; 34import android.view.IWindowManager; 35 36import java.io.File; 37import java.io.FileNotFoundException; 38import java.io.PrintStream; 39import java.net.URISyntaxException; 40import java.util.Iterator; 41import java.util.Set; 42 43public class Am { 44 45 private IActivityManager mAm; 46 private String[] mArgs; 47 private int mNextArg; 48 private String mCurArgData; 49 50 private boolean mDebugOption = false; 51 private boolean mWaitOption = false; 52 53 // These are magic strings understood by the Eclipse plugin. 54 private static final String FATAL_ERROR_CODE = "Error type 1"; 55 private static final String NO_SYSTEM_ERROR_CODE = "Error type 2"; 56 private static final String NO_CLASS_ERROR_CODE = "Error type 3"; 57 58 /** 59 * Command-line entry point. 60 * 61 * @param args The command-line arguments 62 */ 63 public static void main(String[] args) { 64 try { 65 (new Am()).run(args); 66 } catch (IllegalArgumentException e) { 67 showUsage(); 68 System.err.println("Error: " + e.getMessage()); 69 } catch (Exception e) { 70 System.err.println(e.toString()); 71 System.exit(1); 72 } 73 } 74 75 private void run(String[] args) throws Exception { 76 if (args.length < 1) { 77 showUsage(); 78 return; 79 } 80 81 mAm = ActivityManagerNative.getDefault(); 82 if (mAm == null) { 83 System.err.println(NO_SYSTEM_ERROR_CODE); 84 throw new AndroidException("Can't connect to activity manager; is the system running?"); 85 } 86 87 mArgs = args; 88 String op = args[0]; 89 mNextArg = 1; 90 91 if (op.equals("start")) { 92 runStart(); 93 } else if (op.equals("startservice")) { 94 runStartService(); 95 } else if (op.equals("instrument")) { 96 runInstrument(); 97 } else if (op.equals("broadcast")) { 98 sendBroadcast(); 99 } else if (op.equals("profile")) { 100 runProfile(); 101 } else if (op.equals("dumpheap")) { 102 runDumpHeap(); 103 } else { 104 throw new IllegalArgumentException("Unknown command: " + op); 105 } 106 } 107 108 private Intent makeIntent() throws URISyntaxException { 109 Intent intent = new Intent(); 110 boolean hasIntentInfo = false; 111 112 mDebugOption = false; 113 mWaitOption = false; 114 Uri data = null; 115 String type = null; 116 117 String opt; 118 while ((opt=nextOption()) != null) { 119 if (opt.equals("-a")) { 120 intent.setAction(nextArgRequired()); 121 hasIntentInfo = true; 122 } else if (opt.equals("-d")) { 123 data = Uri.parse(nextArgRequired()); 124 hasIntentInfo = true; 125 } else if (opt.equals("-t")) { 126 type = nextArgRequired(); 127 hasIntentInfo = true; 128 } else if (opt.equals("-c")) { 129 intent.addCategory(nextArgRequired()); 130 hasIntentInfo = true; 131 } else if (opt.equals("-e") || opt.equals("--es")) { 132 String key = nextArgRequired(); 133 String value = nextArgRequired(); 134 intent.putExtra(key, value); 135 hasIntentInfo = true; 136 } else if (opt.equals("--esn")) { 137 String key = nextArgRequired(); 138 intent.putExtra(key, (String) null); 139 hasIntentInfo = true; 140 } else if (opt.equals("--ei")) { 141 String key = nextArgRequired(); 142 String value = nextArgRequired(); 143 intent.putExtra(key, Integer.valueOf(value)); 144 hasIntentInfo = true; 145 } else if (opt.equals("--el")) { 146 String key = nextArgRequired(); 147 String value = nextArgRequired(); 148 intent.putExtra(key, Long.valueOf(value)); 149 hasIntentInfo = true; 150 } else if (opt.equals("--ez")) { 151 String key = nextArgRequired(); 152 String value = nextArgRequired(); 153 intent.putExtra(key, Boolean.valueOf(value)); 154 hasIntentInfo = true; 155 } else if (opt.equals("-n")) { 156 String str = nextArgRequired(); 157 ComponentName cn = ComponentName.unflattenFromString(str); 158 if (cn == null) throw new IllegalArgumentException("Bad component name: " + str); 159 intent.setComponent(cn); 160 hasIntentInfo = true; 161 } else if (opt.equals("-f")) { 162 String str = nextArgRequired(); 163 intent.setFlags(Integer.decode(str).intValue()); 164 } else if (opt.equals("--grant-read-uri-permission")) { 165 intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); 166 } else if (opt.equals("--grant-write-uri-permission")) { 167 intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION); 168 } else if (opt.equals("--debug-log-resolution")) { 169 intent.addFlags(Intent.FLAG_DEBUG_LOG_RESOLUTION); 170 } else if (opt.equals("--activity-brought-to-front")) { 171 intent.addFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT); 172 } else if (opt.equals("--activity-clear-top")) { 173 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); 174 } else if (opt.equals("--activity-clear-when-task-reset")) { 175 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET); 176 } else if (opt.equals("--activity-exclude-from-recents")) { 177 intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); 178 } else if (opt.equals("--activity-launched-from-history")) { 179 intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY); 180 } else if (opt.equals("--activity-multiple-task")) { 181 intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK); 182 } else if (opt.equals("--activity-no-animation")) { 183 intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION); 184 } else if (opt.equals("--activity-no-history")) { 185 intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY); 186 } else if (opt.equals("--activity-no-user-action")) { 187 intent.addFlags(Intent.FLAG_ACTIVITY_NO_USER_ACTION); 188 } else if (opt.equals("--activity-previous-is-top")) { 189 intent.addFlags(Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP); 190 } else if (opt.equals("--activity-reorder-to-front")) { 191 intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT); 192 } else if (opt.equals("--activity-reset-task-if-needed")) { 193 intent.addFlags(Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); 194 } else if (opt.equals("--activity-single-top")) { 195 intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); 196 } else if (opt.equals("--receiver-registered-only")) { 197 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); 198 } else if (opt.equals("--receiver-replace-pending")) { 199 intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING); 200 } else if (opt.equals("-D")) { 201 mDebugOption = true; 202 } else if (opt.equals("-W")) { 203 mWaitOption = true; 204 } else { 205 System.err.println("Error: Unknown option: " + opt); 206 showUsage(); 207 return null; 208 } 209 } 210 intent.setDataAndType(data, type); 211 212 String uri = nextArg(); 213 if (uri != null) { 214 Intent oldIntent = intent; 215 intent = Intent.parseUri(uri, 0); 216 if (oldIntent.getAction() != null) { 217 intent.setAction(oldIntent.getAction()); 218 } 219 if (oldIntent.getData() != null || oldIntent.getType() != null) { 220 intent.setDataAndType(oldIntent.getData(), oldIntent.getType()); 221 } 222 Set cats = oldIntent.getCategories(); 223 if (cats != null) { 224 Iterator it = cats.iterator(); 225 while (it.hasNext()) { 226 intent.addCategory((String)it.next()); 227 } 228 } 229 hasIntentInfo = true; 230 } 231 232 if (!hasIntentInfo) throw new IllegalArgumentException("No intent supplied"); 233 return intent; 234 } 235 236 private void runStartService() throws Exception { 237 Intent intent = makeIntent(); 238 System.out.println("Starting service: " + intent); 239 ComponentName cn = mAm.startService(null, intent, intent.getType()); 240 if (cn == null) { 241 System.err.println("Error: Not found; no service started."); 242 } 243 } 244 245 private void runStart() throws Exception { 246 Intent intent = makeIntent(); 247 System.out.println("Starting: " + intent); 248 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 249 // XXX should do something to determine the MIME type. 250 IActivityManager.WaitResult result = null; 251 int res; 252 if (mWaitOption) { 253 result = mAm.startActivityAndWait(null, intent, intent.getType(), 254 null, 0, null, null, 0, false, mDebugOption); 255 res = result.result; 256 } else { 257 res = mAm.startActivity(null, intent, intent.getType(), 258 null, 0, null, null, 0, false, mDebugOption); 259 } 260 PrintStream out = mWaitOption ? System.out : System.err; 261 boolean launched = false; 262 switch (res) { 263 case IActivityManager.START_SUCCESS: 264 launched = true; 265 break; 266 case IActivityManager.START_SWITCHES_CANCELED: 267 launched = true; 268 out.println( 269 "Warning: Activity not started because the " 270 + " current activity is being kept for the user."); 271 break; 272 case IActivityManager.START_DELIVERED_TO_TOP: 273 launched = true; 274 out.println( 275 "Warning: Activity not started, intent has " 276 + "been delivered to currently running " 277 + "top-most instance."); 278 break; 279 case IActivityManager.START_RETURN_INTENT_TO_CALLER: 280 launched = true; 281 out.println( 282 "Warning: Activity not started because intent " 283 + "should be handled by the caller"); 284 break; 285 case IActivityManager.START_TASK_TO_FRONT: 286 launched = true; 287 out.println( 288 "Warning: Activity not started, its current " 289 + "task has been brought to the front"); 290 break; 291 case IActivityManager.START_INTENT_NOT_RESOLVED: 292 out.println( 293 "Error: Activity not started, unable to " 294 + "resolve " + intent.toString()); 295 break; 296 case IActivityManager.START_CLASS_NOT_FOUND: 297 out.println(NO_CLASS_ERROR_CODE); 298 out.println("Error: Activity class " + 299 intent.getComponent().toShortString() 300 + " does not exist."); 301 break; 302 case IActivityManager.START_FORWARD_AND_REQUEST_CONFLICT: 303 out.println( 304 "Error: Activity not started, you requested to " 305 + "both forward and receive its result"); 306 break; 307 case IActivityManager.START_PERMISSION_DENIED: 308 out.println( 309 "Error: Activity not started, you do not " 310 + "have permission to access it."); 311 break; 312 default: 313 out.println( 314 "Error: Activity not started, unknown error code " + res); 315 break; 316 } 317 if (mWaitOption && launched) { 318 if (result == null) { 319 result = new IActivityManager.WaitResult(); 320 result.who = intent.getComponent(); 321 } 322 System.out.println("Status: " + (result.timeout ? "timeout" : "ok")); 323 if (result.who != null) { 324 System.out.println("Activity: " + result.who.flattenToShortString()); 325 } 326 if (result.thisTime >= 0) { 327 System.out.println("ThisTime: " + result.thisTime); 328 } 329 if (result.totalTime >= 0) { 330 System.out.println("TotalTime: " + result.totalTime); 331 } 332 System.out.println("Complete"); 333 } 334 } 335 336 private void sendBroadcast() throws Exception { 337 Intent intent = makeIntent(); 338 IntentReceiver receiver = new IntentReceiver(); 339 System.out.println("Broadcasting: " + intent); 340 mAm.broadcastIntent(null, intent, null, receiver, 0, null, null, null, true, false); 341 receiver.waitForFinish(); 342 } 343 344 private void runInstrument() throws Exception { 345 String profileFile = null; 346 boolean wait = false; 347 boolean rawMode = false; 348 boolean no_window_animation = false; 349 Bundle args = new Bundle(); 350 String argKey = null, argValue = null; 351 IWindowManager wm = IWindowManager.Stub.asInterface(ServiceManager.getService("window")); 352 353 String opt; 354 while ((opt=nextOption()) != null) { 355 if (opt.equals("-p")) { 356 profileFile = nextArgRequired(); 357 } else if (opt.equals("-w")) { 358 wait = true; 359 } else if (opt.equals("-r")) { 360 rawMode = true; 361 } else if (opt.equals("-e")) { 362 argKey = nextArgRequired(); 363 argValue = nextArgRequired(); 364 args.putString(argKey, argValue); 365 } else if (opt.equals("--no_window_animation")) { 366 no_window_animation = true; 367 } else { 368 System.err.println("Error: Unknown option: " + opt); 369 showUsage(); 370 return; 371 } 372 } 373 374 String cnArg = nextArgRequired(); 375 ComponentName cn = ComponentName.unflattenFromString(cnArg); 376 if (cn == null) throw new IllegalArgumentException("Bad component name: " + cnArg); 377 378 InstrumentationWatcher watcher = null; 379 if (wait) { 380 watcher = new InstrumentationWatcher(); 381 watcher.setRawOutput(rawMode); 382 } 383 float[] oldAnims = null; 384 if (no_window_animation) { 385 oldAnims = wm.getAnimationScales(); 386 wm.setAnimationScale(0, 0.0f); 387 wm.setAnimationScale(1, 0.0f); 388 } 389 390 if (!mAm.startInstrumentation(cn, profileFile, 0, args, watcher)) { 391 throw new AndroidException("INSTRUMENTATION_FAILED: " + cn.flattenToString()); 392 } 393 394 if (watcher != null) { 395 if (!watcher.waitForFinish()) { 396 System.out.println("INSTRUMENTATION_ABORTED: System has crashed."); 397 } 398 } 399 400 if (oldAnims != null) { 401 wm.setAnimationScales(oldAnims); 402 } 403 } 404 405 private void runProfile() throws Exception { 406 String profileFile = null; 407 boolean start = false; 408 String process = nextArgRequired(); 409 ParcelFileDescriptor fd = null; 410 411 String cmd = nextArgRequired(); 412 if ("start".equals(cmd)) { 413 start = true; 414 profileFile = nextArgRequired(); 415 try { 416 fd = ParcelFileDescriptor.open( 417 new File(profileFile), 418 ParcelFileDescriptor.MODE_CREATE | 419 ParcelFileDescriptor.MODE_TRUNCATE | 420 ParcelFileDescriptor.MODE_READ_WRITE); 421 } catch (FileNotFoundException e) { 422 System.err.println("Error: Unable to open file: " + profileFile); 423 return; 424 } 425 } else if (!"stop".equals(cmd)) { 426 throw new IllegalArgumentException("Profile command " + cmd + " not valid"); 427 } 428 429 if (!mAm.profileControl(process, start, profileFile, fd)) { 430 throw new AndroidException("PROFILE FAILED on process " + process); 431 } 432 } 433 434 private void runDumpHeap() throws Exception { 435 boolean managed = !"-n".equals(nextOption()); 436 String process = nextArgRequired(); 437 String heapFile = nextArgRequired(); 438 ParcelFileDescriptor fd = null; 439 440 try { 441 fd = ParcelFileDescriptor.open( 442 new File(heapFile), 443 ParcelFileDescriptor.MODE_CREATE | 444 ParcelFileDescriptor.MODE_TRUNCATE | 445 ParcelFileDescriptor.MODE_READ_WRITE); 446 } catch (FileNotFoundException e) { 447 System.err.println("Error: Unable to open file: " + heapFile); 448 return; 449 } 450 451 if (!mAm.dumpHeap(process, managed, heapFile, fd)) { 452 throw new AndroidException("HEAP DUMP FAILED on process " + process); 453 } 454 } 455 456 private class IntentReceiver extends IIntentReceiver.Stub { 457 private boolean mFinished = false; 458 459 public synchronized void performReceive( 460 Intent intent, int rc, String data, Bundle ext, boolean ord, 461 boolean sticky) { 462 String line = "Broadcast completed: result=" + rc; 463 if (data != null) line = line + ", data=\"" + data + "\""; 464 if (ext != null) line = line + ", extras: " + ext; 465 System.out.println(line); 466 mFinished = true; 467 notifyAll(); 468 } 469 470 public synchronized void waitForFinish() { 471 try { 472 while (!mFinished) wait(); 473 } catch (InterruptedException e) { 474 throw new IllegalStateException(e); 475 } 476 } 477 } 478 479 private class InstrumentationWatcher extends IInstrumentationWatcher.Stub { 480 private boolean mFinished = false; 481 private boolean mRawMode = false; 482 483 /** 484 * Set or reset "raw mode". In "raw mode", all bundles are dumped. In "pretty mode", 485 * if a bundle includes Instrumentation.REPORT_KEY_STREAMRESULT, just print that. 486 * @param rawMode true for raw mode, false for pretty mode. 487 */ 488 public void setRawOutput(boolean rawMode) { 489 mRawMode = rawMode; 490 } 491 492 public void instrumentationStatus(ComponentName name, int resultCode, Bundle results) { 493 synchronized (this) { 494 // pretty printer mode? 495 String pretty = null; 496 if (!mRawMode && results != null) { 497 pretty = results.getString(Instrumentation.REPORT_KEY_STREAMRESULT); 498 } 499 if (pretty != null) { 500 System.out.print(pretty); 501 } else { 502 if (results != null) { 503 for (String key : results.keySet()) { 504 System.out.println( 505 "INSTRUMENTATION_STATUS: " + key + "=" + results.get(key)); 506 } 507 } 508 System.out.println("INSTRUMENTATION_STATUS_CODE: " + resultCode); 509 } 510 notifyAll(); 511 } 512 } 513 514 public void instrumentationFinished(ComponentName name, int resultCode, 515 Bundle results) { 516 synchronized (this) { 517 // pretty printer mode? 518 String pretty = null; 519 if (!mRawMode && results != null) { 520 pretty = results.getString(Instrumentation.REPORT_KEY_STREAMRESULT); 521 } 522 if (pretty != null) { 523 System.out.println(pretty); 524 } else { 525 if (results != null) { 526 for (String key : results.keySet()) { 527 System.out.println( 528 "INSTRUMENTATION_RESULT: " + key + "=" + results.get(key)); 529 } 530 } 531 System.out.println("INSTRUMENTATION_CODE: " + resultCode); 532 } 533 mFinished = true; 534 notifyAll(); 535 } 536 } 537 538 public boolean waitForFinish() { 539 synchronized (this) { 540 while (!mFinished) { 541 try { 542 if (!mAm.asBinder().pingBinder()) { 543 return false; 544 } 545 wait(1000); 546 } catch (InterruptedException e) { 547 throw new IllegalStateException(e); 548 } 549 } 550 } 551 return true; 552 } 553 } 554 555 private String nextOption() { 556 if (mCurArgData != null) { 557 String prev = mArgs[mNextArg - 1]; 558 throw new IllegalArgumentException("No argument expected after \"" + prev + "\""); 559 } 560 if (mNextArg >= mArgs.length) { 561 return null; 562 } 563 String arg = mArgs[mNextArg]; 564 if (!arg.startsWith("-")) { 565 return null; 566 } 567 mNextArg++; 568 if (arg.equals("--")) { 569 return null; 570 } 571 if (arg.length() > 1 && arg.charAt(1) != '-') { 572 if (arg.length() > 2) { 573 mCurArgData = arg.substring(2); 574 return arg.substring(0, 2); 575 } else { 576 mCurArgData = null; 577 return arg; 578 } 579 } 580 mCurArgData = null; 581 return arg; 582 } 583 584 private String nextArg() { 585 if (mCurArgData != null) { 586 String arg = mCurArgData; 587 mCurArgData = null; 588 return arg; 589 } else if (mNextArg < mArgs.length) { 590 return mArgs[mNextArg++]; 591 } else { 592 return null; 593 } 594 } 595 596 private String nextArgRequired() { 597 String arg = nextArg(); 598 if (arg == null) { 599 String prev = mArgs[mNextArg - 1]; 600 throw new IllegalArgumentException("Argument expected after \"" + prev + "\""); 601 } 602 return arg; 603 } 604 605 private static void showUsage() { 606 System.err.println( 607 "usage: am [subcommand] [options]\n" + 608 "\n" + 609 " start an Activity: am start [-D] [-W] <INTENT>\n" + 610 " -D: enable debugging\n" + 611 " -W: wait for launch to complete\n" + 612 "\n" + 613 " start a Service: am startservice <INTENT>\n" + 614 "\n" + 615 " send a broadcast Intent: am broadcast <INTENT>\n" + 616 "\n" + 617 " start an Instrumentation: am instrument [flags] <COMPONENT>\n" + 618 " -r: print raw results (otherwise decode REPORT_KEY_STREAMRESULT)\n" + 619 " -e <NAME> <VALUE>: set argument <NAME> to <VALUE>\n" + 620 " -p <FILE>: write profiling data to <FILE>\n" + 621 " -w: wait for instrumentation to finish before returning\n" + 622 "\n" + 623 " start profiling: am profile <PROCESS> start <FILE>\n" + 624 " stop profiling: am profile <PROCESS> stop\n" + 625 " dump heap: am dumpheap [flags] <PROCESS> <FILE>\n" + 626 " -n: dump native heap instead of managed heap\n" + 627 "\n" + 628 " <INTENT> specifications include these flags:\n" + 629 " [-a <ACTION>] [-d <DATA_URI>] [-t <MIME_TYPE>]\n" + 630 " [-c <CATEGORY> [-c <CATEGORY>] ...]\n" + 631 " [-e|--es <EXTRA_KEY> <EXTRA_STRING_VALUE> ...]\n" + 632 " [--esn <EXTRA_KEY> ...]\n" + 633 " [--ez <EXTRA_KEY> <EXTRA_BOOLEAN_VALUE> ...]\n" + 634 " [--ei <EXTRA_KEY> <EXTRA_INT_VALUE> ...]\n" + 635 " [--el <EXTRA_KEY> <EXTRA_LONG_VALUE> ...]\n" + 636 " [-n <COMPONENT>] [-f <FLAGS>]\n" + 637 " [--grant-read-uri-permission] [--grant-write-uri-permission]\n" + 638 " [--debug-log-resolution]\n" + 639 " [--activity-brought-to-front] [--activity-clear-top]\n" + 640 " [--activity-clear-when-task-reset] [--activity-exclude-from-recents]\n" + 641 " [--activity-launched-from-history] [--activity-multiple-task]\n" + 642 " [--activity-no-animation] [--activity-no-history]\n" + 643 " [--activity-no-user-action] [--activity-previous-is-top]\n" + 644 " [--activity-reorder-to-front] [--activity-reset-task-if-needed]\n" + 645 " [--activity-single-top]\n" + 646 " [--receiver-registered-only] [--receiver-replace-pending]\n" + 647 " [<URI>]\n" 648 ); 649 } 650} 651