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