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