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