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