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