Am.java revision 287952c35e148811c106bc0f5036eabf20f71562
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.IActivityController;
23import android.app.IActivityManager;
24import android.app.IInstrumentationWatcher;
25import android.app.Instrumentation;
26import android.content.ComponentName;
27import android.content.IIntentReceiver;
28import android.content.Intent;
29import android.net.Uri;
30import android.os.Bundle;
31import android.os.ParcelFileDescriptor;
32import android.os.RemoteException;
33import android.os.ServiceManager;
34import android.util.AndroidException;
35import android.view.IWindowManager;
36
37import java.io.BufferedReader;
38import java.io.DataInputStream;
39import java.io.File;
40import java.io.FileInputStream;
41import java.io.FileNotFoundException;
42import java.io.IOException;
43import java.io.InputStreamReader;
44import java.io.PrintStream;
45import java.net.URISyntaxException;
46import java.util.Iterator;
47import java.util.Set;
48
49public class Am {
50
51    private IActivityManager mAm;
52    private String[] mArgs;
53    private int mNextArg;
54    private String mCurArgData;
55
56    private boolean mDebugOption = false;
57    private boolean mWaitOption = false;
58
59    // These are magic strings understood by the Eclipse plugin.
60    private static final String FATAL_ERROR_CODE = "Error type 1";
61    private static final String NO_SYSTEM_ERROR_CODE = "Error type 2";
62    private static final String NO_CLASS_ERROR_CODE = "Error type 3";
63
64    /**
65     * Command-line entry point.
66     *
67     * @param args The command-line arguments
68     */
69    public static void main(String[] args) {
70        try {
71            (new Am()).run(args);
72        } catch (IllegalArgumentException e) {
73            showUsage();
74            System.err.println("Error: " + e.getMessage());
75        } catch (Exception e) {
76            System.err.println(e.toString());
77            System.exit(1);
78        }
79    }
80
81    private void run(String[] args) throws Exception {
82        if (args.length < 1) {
83            showUsage();
84            return;
85        }
86
87        mAm = ActivityManagerNative.getDefault();
88        if (mAm == null) {
89            System.err.println(NO_SYSTEM_ERROR_CODE);
90            throw new AndroidException("Can't connect to activity manager; is the system running?");
91        }
92
93        mArgs = args;
94        String op = args[0];
95        mNextArg = 1;
96
97        if (op.equals("start")) {
98            runStart();
99        } else if (op.equals("startservice")) {
100            runStartService();
101        } else if (op.equals("instrument")) {
102            runInstrument();
103        } else if (op.equals("broadcast")) {
104            sendBroadcast();
105        } else if (op.equals("profile")) {
106            runProfile();
107        } else if (op.equals("monitor")) {
108            runMonitor();
109        } else {
110            throw new IllegalArgumentException("Unknown command: " + op);
111        }
112    }
113
114    private Intent makeIntent() throws URISyntaxException {
115        Intent intent = new Intent();
116        boolean hasIntentInfo = false;
117
118        mDebugOption = false;
119        mWaitOption = false;
120        Uri data = null;
121        String type = null;
122
123        String opt;
124        while ((opt=nextOption()) != null) {
125            if (opt.equals("-a")) {
126                intent.setAction(nextArgRequired());
127                hasIntentInfo = true;
128            } else if (opt.equals("-d")) {
129                data = Uri.parse(nextArgRequired());
130                hasIntentInfo = true;
131            } else if (opt.equals("-t")) {
132                type = nextArgRequired();
133                hasIntentInfo = true;
134            } else if (opt.equals("-c")) {
135                intent.addCategory(nextArgRequired());
136                hasIntentInfo = true;
137            } else if (opt.equals("-e") || opt.equals("--es")) {
138                String key = nextArgRequired();
139                String value = nextArgRequired();
140                intent.putExtra(key, value);
141                hasIntentInfo = true;
142            } else if (opt.equals("--esn")) {
143                String key = nextArgRequired();
144                intent.putExtra(key, (String) null);
145                hasIntentInfo = true;
146            } else if (opt.equals("--ei")) {
147                String key = nextArgRequired();
148                String value = nextArgRequired();
149                intent.putExtra(key, Integer.valueOf(value));
150                hasIntentInfo = true;
151            } else if (opt.equals("--ez")) {
152                String key = nextArgRequired();
153                String value = nextArgRequired();
154                intent.putExtra(key, Boolean.valueOf(value));
155                hasIntentInfo = true;
156            } else if (opt.equals("-n")) {
157                String str = nextArgRequired();
158                ComponentName cn = ComponentName.unflattenFromString(str);
159                if (cn == null) throw new IllegalArgumentException("Bad component name: " + str);
160                intent.setComponent(cn);
161                hasIntentInfo = true;
162            } else if (opt.equals("-f")) {
163                String str = nextArgRequired();
164                intent.setFlags(Integer.decode(str).intValue());
165            } else if (opt.equals("--grant-read-uri-permission")) {
166                intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
167            } else if (opt.equals("--grant-write-uri-permission")) {
168                intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
169            } else if (opt.equals("--debug-log-resolution")) {
170                intent.addFlags(Intent.FLAG_DEBUG_LOG_RESOLUTION);
171            } else if (opt.equals("--activity-brought-to-front")) {
172                intent.addFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
173            } else if (opt.equals("--activity-clear-top")) {
174                intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
175            } else if (opt.equals("--activity-clear-when-task-reset")) {
176                intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
177            } else if (opt.equals("--activity-exclude-from-recents")) {
178                intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
179            } else if (opt.equals("--activity-launched-from-history")) {
180                intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY);
181            } else if (opt.equals("--activity-multiple-task")) {
182                intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
183            } else if (opt.equals("--activity-no-animation")) {
184                intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
185            } else if (opt.equals("--activity-no-history")) {
186                intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
187            } else if (opt.equals("--activity-no-user-action")) {
188                intent.addFlags(Intent.FLAG_ACTIVITY_NO_USER_ACTION);
189            } else if (opt.equals("--activity-previous-is-top")) {
190                intent.addFlags(Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP);
191            } else if (opt.equals("--activity-reorder-to-front")) {
192                intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
193            } else if (opt.equals("--activity-reset-task-if-needed")) {
194                intent.addFlags(Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
195            } else if (opt.equals("--activity-single-top")) {
196                intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
197            } else if (opt.equals("--receiver-registered-only")) {
198                intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
199            } else if (opt.equals("--receiver-replace-pending")) {
200                intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
201            } else if (opt.equals("-D")) {
202                mDebugOption = true;
203            } else if (opt.equals("-W")) {
204                mWaitOption = true;
205            } else {
206                System.err.println("Error: Unknown option: " + opt);
207                showUsage();
208                return null;
209            }
210        }
211        intent.setDataAndType(data, type);
212
213        String uri = nextArg();
214        if (uri != null) {
215            Intent oldIntent = intent;
216            intent = Intent.parseUri(uri, 0);
217            if (oldIntent.getAction() != null) {
218                intent.setAction(oldIntent.getAction());
219            }
220            if (oldIntent.getData() != null || oldIntent.getType() != null) {
221                intent.setDataAndType(oldIntent.getData(), oldIntent.getType());
222            }
223            Set cats = oldIntent.getCategories();
224            if (cats != null) {
225                Iterator it = cats.iterator();
226                while (it.hasNext()) {
227                    intent.addCategory((String)it.next());
228                }
229            }
230            hasIntentInfo = true;
231        }
232
233        if (!hasIntentInfo) throw new IllegalArgumentException("No intent supplied");
234        return intent;
235    }
236
237    private void runStartService() throws Exception {
238        Intent intent = makeIntent();
239        System.out.println("Starting service: " + intent);
240        ComponentName cn = mAm.startService(null, intent, intent.getType());
241        if (cn == null) {
242            System.err.println("Error: Not found; no service started.");
243        }
244    }
245
246    private void runStart() throws Exception {
247        Intent intent = makeIntent();
248        System.out.println("Starting: " + intent);
249        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
250        // XXX should do something to determine the MIME type.
251        IActivityManager.WaitResult result = null;
252        int res;
253        if (mWaitOption) {
254            result = mAm.startActivityAndWait(null, intent, intent.getType(),
255                        null, 0, null, null, 0, false, mDebugOption);
256            res = result.result;
257        } else {
258            res = mAm.startActivity(null, intent, intent.getType(),
259                    null, 0, null, null, 0, false, mDebugOption);
260        }
261        PrintStream out = mWaitOption ? System.out : System.err;
262        boolean launched = false;
263        switch (res) {
264            case IActivityManager.START_SUCCESS:
265                launched = true;
266                break;
267            case IActivityManager.START_SWITCHES_CANCELED:
268                launched = true;
269                out.println(
270                        "Warning: Activity not started because the "
271                        + " current activity is being kept for the user.");
272                break;
273            case IActivityManager.START_DELIVERED_TO_TOP:
274                launched = true;
275                out.println(
276                        "Warning: Activity not started, intent has "
277                        + "been delivered to currently running "
278                        + "top-most instance.");
279                break;
280            case IActivityManager.START_RETURN_INTENT_TO_CALLER:
281                launched = true;
282                out.println(
283                        "Warning: Activity not started because intent "
284                        + "should be handled by the caller");
285                break;
286            case IActivityManager.START_TASK_TO_FRONT:
287                launched = true;
288                out.println(
289                        "Warning: Activity not started, its current "
290                        + "task has been brought to the front");
291                break;
292            case IActivityManager.START_INTENT_NOT_RESOLVED:
293                out.println(
294                        "Error: Activity not started, unable to "
295                        + "resolve " + intent.toString());
296                break;
297            case IActivityManager.START_CLASS_NOT_FOUND:
298                out.println(NO_CLASS_ERROR_CODE);
299                out.println("Error: Activity class " +
300                        intent.getComponent().toShortString()
301                        + " does not exist.");
302                break;
303            case IActivityManager.START_FORWARD_AND_REQUEST_CONFLICT:
304                out.println(
305                        "Error: Activity not started, you requested to "
306                        + "both forward and receive its result");
307                break;
308            case IActivityManager.START_PERMISSION_DENIED:
309                out.println(
310                        "Error: Activity not started, you do not "
311                        + "have permission to access it.");
312                break;
313            default:
314                out.println(
315                        "Error: Activity not started, unknown error code " + res);
316                break;
317        }
318        if (mWaitOption && launched) {
319            if (result == null) {
320                result = new IActivityManager.WaitResult();
321                result.who = intent.getComponent();
322            }
323            System.out.println("Status: " + (result.timeout ? "timeout" : "ok"));
324            if (result.who != null) {
325                System.out.println("Activity: " + result.who.flattenToShortString());
326            }
327            if (result.thisTime >= 0) {
328                System.out.println("ThisTime: " + result.thisTime);
329            }
330            if (result.totalTime >= 0) {
331                System.out.println("TotalTime: " + result.totalTime);
332            }
333            System.out.println("Complete");
334        }
335    }
336
337    private void sendBroadcast() throws Exception {
338        Intent intent = makeIntent();
339        IntentReceiver receiver = new IntentReceiver();
340        System.out.println("Broadcasting: " + intent);
341        mAm.broadcastIntent(null, intent, null, receiver, 0, null, null, null, true, false);
342        receiver.waitForFinish();
343    }
344
345    private void runInstrument() throws Exception {
346        String profileFile = null;
347        boolean wait = false;
348        boolean rawMode = false;
349        boolean no_window_animation = false;
350        Bundle args = new Bundle();
351        String argKey = null, argValue = null;
352        IWindowManager wm = IWindowManager.Stub.asInterface(ServiceManager.getService("window"));
353
354        String opt;
355        while ((opt=nextOption()) != null) {
356            if (opt.equals("-p")) {
357                profileFile = nextArgRequired();
358            } else if (opt.equals("-w")) {
359                wait = true;
360            } else if (opt.equals("-r")) {
361                rawMode = true;
362            } else if (opt.equals("-e")) {
363                argKey = nextArgRequired();
364                argValue = nextArgRequired();
365                args.putString(argKey, argValue);
366            } else if (opt.equals("--no_window_animation")) {
367                no_window_animation = true;
368            } else {
369                System.err.println("Error: Unknown option: " + opt);
370                showUsage();
371                return;
372            }
373        }
374
375        String cnArg = nextArgRequired();
376        ComponentName cn = ComponentName.unflattenFromString(cnArg);
377        if (cn == null) throw new IllegalArgumentException("Bad component name: " + cnArg);
378
379        InstrumentationWatcher watcher = null;
380        if (wait) {
381            watcher = new InstrumentationWatcher();
382            watcher.setRawOutput(rawMode);
383        }
384        float[] oldAnims = null;
385        if (no_window_animation) {
386            oldAnims = wm.getAnimationScales();
387            wm.setAnimationScale(0, 0.0f);
388            wm.setAnimationScale(1, 0.0f);
389        }
390
391        if (!mAm.startInstrumentation(cn, profileFile, 0, args, watcher)) {
392            throw new AndroidException("INSTRUMENTATION_FAILED: " + cn.flattenToString());
393        }
394
395        if (watcher != null) {
396            if (!watcher.waitForFinish()) {
397                System.out.println("INSTRUMENTATION_ABORTED: System has crashed.");
398            }
399        }
400
401        if (oldAnims != null) {
402            wm.setAnimationScales(oldAnims);
403        }
404    }
405
406    private void runProfile() throws Exception {
407        String profileFile = null;
408        boolean start = false;
409        String process = nextArgRequired();
410        ParcelFileDescriptor fd = null;
411
412        String cmd = nextArgRequired();
413        if ("start".equals(cmd)) {
414            start = true;
415            profileFile = nextArgRequired();
416            try {
417                fd = ParcelFileDescriptor.open(
418                        new File(profileFile),
419                        ParcelFileDescriptor.MODE_CREATE |
420                        ParcelFileDescriptor.MODE_TRUNCATE |
421                        ParcelFileDescriptor.MODE_READ_WRITE);
422            } catch (FileNotFoundException e) {
423                System.err.println("Error: Unable to open file: " + profileFile);
424                return;
425            }
426        } else if (!"stop".equals(cmd)) {
427            throw new IllegalArgumentException("Profile command " + cmd + " not valid");
428        }
429
430        if (!mAm.profileControl(process, start, profileFile, fd)) {
431            throw new AndroidException("PROFILE FAILED on process " + process);
432        }
433    }
434
435    class MyActivityController extends IActivityController.Stub {
436        static final int STATE_NORMAL = 0;
437        static final int STATE_CRASHED = 1;
438        static final int STATE_EARLY_ANR = 2;
439        static final int STATE_ANR = 3;
440
441        int mState;
442
443        static final int RESULT_DEFAULT = 0;
444
445        static final int RESULT_CRASH_DIALOG = 0;
446        static final int RESULT_CRASH_KILL = 1;
447
448        static final int RESULT_EARLY_ANR_CONTINUE = 0;
449        static final int RESULT_EARLY_ANR_KILL = 1;
450
451        static final int RESULT_ANR_DIALOG = 0;
452        static final int RESULT_ANR_KILL = 1;
453        static final int RESULT_ANR_WAIT = 1;
454
455        int mResult;
456
457        @Override
458        public boolean activityResuming(String pkg) throws RemoteException {
459            synchronized (this) {
460                System.out.println("** Activity resuming: " + pkg);
461            }
462            return true;
463        }
464
465        @Override
466        public boolean activityStarting(Intent intent, String pkg) throws RemoteException {
467            synchronized (this) {
468                System.out.println("** Activity starting: " + pkg);
469            }
470            return true;
471        }
472
473        @Override
474        public boolean appCrashed(String processName, int pid, String shortMsg, String longMsg,
475                long timeMillis, String stackTrace) throws RemoteException {
476            synchronized (this) {
477                System.out.println("** ERROR: PROCESS CRASHED");
478                System.out.println("processName: " + processName);
479                System.out.println("processPid: " + pid);
480                System.out.println("shortMsg: " + shortMsg);
481                System.out.println("longMsg: " + longMsg);
482                System.out.println("timeMillis: " + timeMillis);
483                System.out.println("stack:");
484                System.out.print(stackTrace);
485                System.out.println("#");
486                int result = waitControllerLocked(STATE_CRASHED);
487                return result == RESULT_CRASH_KILL ? false : true;
488            }
489        }
490
491        @Override
492        public int appEarlyNotResponding(String processName, int pid, String annotation)
493                throws RemoteException {
494            synchronized (this) {
495                System.out.println("** ERROR: EARLY PROCESS NOT RESPONDING");
496                System.out.println("processName: " + processName);
497                System.out.println("processPid: " + pid);
498                System.out.println("annotation: " + annotation);
499                int result = waitControllerLocked(STATE_EARLY_ANR);
500                if (result == RESULT_EARLY_ANR_KILL) return -1;
501                return 0;
502            }
503        }
504
505        @Override
506        public int appNotResponding(String processName, int pid, String processStats)
507                throws RemoteException {
508            synchronized (this) {
509                System.out.println("** ERROR: PROCESS NOT RESPONDING");
510                System.out.println("processName: " + processName);
511                System.out.println("processPid: " + pid);
512                System.out.println("processStats:");
513                System.out.print(processStats);
514                System.out.println("#");
515                int result = waitControllerLocked(STATE_ANR);
516                if (result == RESULT_ANR_KILL) return -1;
517                if (result == RESULT_ANR_WAIT) return 1;
518                return 0;
519            }
520        }
521
522        int waitControllerLocked(int state) {
523            mState = state;
524            System.out.println("");
525            printMessageForState();
526
527            while (mState != STATE_NORMAL) {
528                try {
529                    wait();
530                } catch (InterruptedException e) {
531                }
532            }
533
534            return mResult;
535        }
536
537        void resumeController(int result) {
538            synchronized (this) {
539                mState = STATE_NORMAL;
540                mResult = result;
541                notifyAll();
542            }
543        }
544
545        void printMessageForState() {
546            switch (mState) {
547                case STATE_NORMAL:
548                    System.out.println("Monitoring activity manager...  available commands:");
549                    break;
550                case STATE_CRASHED:
551                    System.out.println("Waiting after crash...  available commands:");
552                    System.out.println("(c)ontinue: show crash dialog");
553                    System.out.println("(k)ill: immediately kill app");
554                    break;
555                case STATE_EARLY_ANR:
556                    System.out.println("Waiting after early ANR...  available commands:");
557                    System.out.println("(c)ontinue: standard ANR processing");
558                    System.out.println("(k)ill: immediately kill app");
559                    break;
560                case STATE_ANR:
561                    System.out.println("Waiting after ANR...  available commands:");
562                    System.out.println("(c)ontinue: show ANR dialog");
563                    System.out.println("(k)ill: immediately kill app");
564                    System.out.println("(w)ait: wait some more");
565                    break;
566            }
567            System.out.println("(q)uit: finish monitoring");
568        }
569
570        void run() throws RemoteException {
571            try {
572                printMessageForState();
573
574                mAm.setActivityController(this);
575                mState = STATE_NORMAL;
576
577                InputStreamReader converter = new InputStreamReader(System.in);
578                BufferedReader in = new BufferedReader(converter);
579                String line;
580
581                while ((line = in.readLine()) != null) {
582                    boolean addNewline = true;
583                    if (line.length() <= 0) {
584                        addNewline = false;
585                    } else if ("q".equals(line) || "quit".equals(line)) {
586                        resumeController(RESULT_DEFAULT);
587                        break;
588                    } else if (mState == STATE_CRASHED) {
589                        if ("c".equals(line) || "continue".equals(line)) {
590                            resumeController(RESULT_CRASH_DIALOG);
591                        } else if ("k".equals(line) || "kill".equals(line)) {
592                            resumeController(RESULT_CRASH_KILL);
593                        } else {
594                            System.out.println("Invalid command: " + line);
595                        }
596                    } else if (mState == STATE_ANR) {
597                        if ("c".equals(line) || "continue".equals(line)) {
598                            resumeController(RESULT_ANR_DIALOG);
599                        } else if ("k".equals(line) || "kill".equals(line)) {
600                            resumeController(RESULT_ANR_KILL);
601                        } else if ("w".equals(line) || "wait".equals(line)) {
602                            resumeController(RESULT_ANR_WAIT);
603                        } else {
604                            System.out.println("Invalid command: " + line);
605                        }
606                    } else if (mState == STATE_EARLY_ANR) {
607                        if ("c".equals(line) || "continue".equals(line)) {
608                            resumeController(RESULT_EARLY_ANR_CONTINUE);
609                        } else if ("k".equals(line) || "kill".equals(line)) {
610                            resumeController(RESULT_EARLY_ANR_KILL);
611                        } else {
612                            System.out.println("Invalid command: " + line);
613                        }
614                    } else {
615                        System.out.println("Invalid command: " + line);
616                    }
617
618                    synchronized (this) {
619                        if (addNewline) {
620                            System.out.println("");
621                        }
622                        printMessageForState();
623                    }
624                }
625
626            } catch (IOException e) {
627                e.printStackTrace();
628            } finally {
629                mAm.setActivityController(null);
630            }
631        }
632    }
633
634    private void runMonitor() throws Exception {
635        MyActivityController controller = new MyActivityController();
636        controller.run();
637    }
638
639    private class IntentReceiver extends IIntentReceiver.Stub {
640        private boolean mFinished = false;
641
642        public synchronized void performReceive(
643                Intent intent, int rc, String data, Bundle ext, boolean ord,
644                boolean sticky) {
645            String line = "Broadcast completed: result=" + rc;
646            if (data != null) line = line + ", data=\"" + data + "\"";
647            if (ext != null) line = line + ", extras: " + ext;
648            System.out.println(line);
649            mFinished = true;
650            notifyAll();
651        }
652
653        public synchronized void waitForFinish() {
654            try {
655                while (!mFinished) wait();
656            } catch (InterruptedException e) {
657                throw new IllegalStateException(e);
658            }
659        }
660    }
661
662    private class InstrumentationWatcher extends IInstrumentationWatcher.Stub {
663        private boolean mFinished = false;
664        private boolean mRawMode = false;
665
666        /**
667         * Set or reset "raw mode".  In "raw mode", all bundles are dumped.  In "pretty mode",
668         * if a bundle includes Instrumentation.REPORT_KEY_STREAMRESULT, just print that.
669         * @param rawMode true for raw mode, false for pretty mode.
670         */
671        public void setRawOutput(boolean rawMode) {
672            mRawMode = rawMode;
673        }
674
675        public void instrumentationStatus(ComponentName name, int resultCode, Bundle results) {
676            synchronized (this) {
677                // pretty printer mode?
678                String pretty = null;
679                if (!mRawMode && results != null) {
680                    pretty = results.getString(Instrumentation.REPORT_KEY_STREAMRESULT);
681                }
682                if (pretty != null) {
683                    System.out.print(pretty);
684                } else {
685                    if (results != null) {
686                        for (String key : results.keySet()) {
687                            System.out.println(
688                                    "INSTRUMENTATION_STATUS: " + key + "=" + results.get(key));
689                        }
690                    }
691                    System.out.println("INSTRUMENTATION_STATUS_CODE: " + resultCode);
692                }
693                notifyAll();
694            }
695        }
696
697        public void instrumentationFinished(ComponentName name, int resultCode,
698                Bundle results) {
699            synchronized (this) {
700                // pretty printer mode?
701                String pretty = null;
702                if (!mRawMode && results != null) {
703                    pretty = results.getString(Instrumentation.REPORT_KEY_STREAMRESULT);
704                }
705                if (pretty != null) {
706                    System.out.println(pretty);
707                } else {
708                    if (results != null) {
709                        for (String key : results.keySet()) {
710                            System.out.println(
711                                    "INSTRUMENTATION_RESULT: " + key + "=" + results.get(key));
712                        }
713                    }
714                    System.out.println("INSTRUMENTATION_CODE: " + resultCode);
715                }
716                mFinished = true;
717                notifyAll();
718            }
719        }
720
721        public boolean waitForFinish() {
722            synchronized (this) {
723                while (!mFinished) {
724                    try {
725                        if (!mAm.asBinder().pingBinder()) {
726                            return false;
727                        }
728                        wait(1000);
729                    } catch (InterruptedException e) {
730                        throw new IllegalStateException(e);
731                    }
732                }
733            }
734            return true;
735        }
736    }
737
738    private String nextOption() {
739        if (mCurArgData != null) {
740            String prev = mArgs[mNextArg - 1];
741            throw new IllegalArgumentException("No argument expected after \"" + prev + "\"");
742        }
743        if (mNextArg >= mArgs.length) {
744            return null;
745        }
746        String arg = mArgs[mNextArg];
747        if (!arg.startsWith("-")) {
748            return null;
749        }
750        mNextArg++;
751        if (arg.equals("--")) {
752            return null;
753        }
754        if (arg.length() > 1 && arg.charAt(1) != '-') {
755            if (arg.length() > 2) {
756                mCurArgData = arg.substring(2);
757                return arg.substring(0, 2);
758            } else {
759                mCurArgData = null;
760                return arg;
761            }
762        }
763        mCurArgData = null;
764        return arg;
765    }
766
767    private String nextArg() {
768        if (mCurArgData != null) {
769            String arg = mCurArgData;
770            mCurArgData = null;
771            return arg;
772        } else if (mNextArg < mArgs.length) {
773            return mArgs[mNextArg++];
774        } else {
775            return null;
776        }
777    }
778
779    private String nextArgRequired() {
780        String arg = nextArg();
781        if (arg == null) {
782            String prev = mArgs[mNextArg - 1];
783            throw new IllegalArgumentException("Argument expected after \"" + prev + "\"");
784        }
785        return arg;
786    }
787
788    private static void showUsage() {
789        System.err.println(
790                "usage: am [subcommand] [options]\n" +
791                "\n" +
792                "    start an Activity: am start [-D] [-W] <INTENT>\n" +
793                "        -D: enable debugging\n" +
794                "        -W: wait for launch to complete\n" +
795                "\n" +
796                "    start a Service: am startservice <INTENT>\n" +
797                "\n" +
798                "    send a broadcast Intent: am broadcast <INTENT>\n" +
799                "\n" +
800                "    start an Instrumentation: am instrument [flags] <COMPONENT>\n" +
801                "        -r: print raw results (otherwise decode REPORT_KEY_STREAMRESULT)\n" +
802                "        -e <NAME> <VALUE>: set argument <NAME> to <VALUE>\n" +
803                "        -p <FILE>: write profiling data to <FILE>\n" +
804                "        -w: wait for instrumentation to finish before returning\n" +
805                "\n" +
806                "    start profiling: am profile <PROCESS> start <FILE>\n" +
807                "    stop profiling: am profile <PROCESS> stop\n" +
808                "\n" +
809                "    start monitoring: am monitor\n" +
810                "\n" +
811                "    <INTENT> specifications include these flags:\n" +
812                "        [-a <ACTION>] [-d <DATA_URI>] [-t <MIME_TYPE>]\n" +
813                "        [-c <CATEGORY> [-c <CATEGORY>] ...]\n" +
814                "        [-e|--es <EXTRA_KEY> <EXTRA_STRING_VALUE> ...]\n" +
815                "        [--esn <EXTRA_KEY> ...]\n" +
816                "        [--ez <EXTRA_KEY> <EXTRA_BOOLEAN_VALUE> ...]\n" +
817                "        [-e|--ei <EXTRA_KEY> <EXTRA_INT_VALUE> ...]\n" +
818                "        [-n <COMPONENT>] [-f <FLAGS>]\n" +
819                "        [--grant-read-uri-permission] [--grant-write-uri-permission]\n" +
820                "        [--debug-log-resolution]\n" +
821                "        [--activity-brought-to-front] [--activity-clear-top]\n" +
822                "        [--activity-clear-when-task-reset] [--activity-exclude-from-recents]\n" +
823                "        [--activity-launched-from-history] [--activity-multiple-task]\n" +
824                "        [--activity-no-animation] [--activity-no-history]\n" +
825                "        [--activity-no-user-action] [--activity-previous-is-top]\n" +
826                "        [--activity-reorder-to-front] [--activity-reset-task-if-needed]\n" +
827                "        [--activity-single-top]\n" +
828                "        [--receiver-registered-only] [--receiver-replace-pending]\n" +
829                "        [<URI>]\n"
830                );
831    }
832}
833