Am.java revision 351f32270a489577e040bbeccc423b0bac1637dc
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.ActivityManager;
22import android.app.ActivityManagerNative;
23import android.app.IActivityController;
24import android.app.IActivityManager;
25import android.app.IInstrumentationWatcher;
26import android.app.Instrumentation;
27import android.content.ComponentName;
28import android.content.Context;
29import android.content.IIntentReceiver;
30import android.content.Intent;
31import android.net.Uri;
32import android.os.Bundle;
33import android.os.ParcelFileDescriptor;
34import android.os.RemoteException;
35import android.os.ServiceManager;
36import android.util.AndroidException;
37import android.view.IWindowManager;
38
39import java.io.BufferedReader;
40import java.io.File;
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("force-stop")) {
102            runForceStop();
103        } else if (op.equals("instrument")) {
104            runInstrument();
105        } else if (op.equals("broadcast")) {
106            sendBroadcast();
107        } else if (op.equals("profile")) {
108            runProfile();
109        } else if (op.equals("dumpheap")) {
110            runDumpHeap();
111        } else if (op.equals("monitor")) {
112            runMonitor();
113        } else if (op.equals("screen-compat")) {
114            runScreenCompat();
115        } else if (op.equals("display-size")) {
116            runDisplaySize();
117        } else {
118            throw new IllegalArgumentException("Unknown command: " + op);
119        }
120    }
121
122    private Intent makeIntent() throws URISyntaxException {
123        Intent intent = new Intent();
124        boolean hasIntentInfo = false;
125
126        mDebugOption = false;
127        mWaitOption = false;
128        Uri data = null;
129        String type = null;
130
131        String opt;
132        while ((opt=nextOption()) != null) {
133            if (opt.equals("-a")) {
134                intent.setAction(nextArgRequired());
135                hasIntentInfo = true;
136            } else if (opt.equals("-d")) {
137                data = Uri.parse(nextArgRequired());
138                hasIntentInfo = true;
139            } else if (opt.equals("-t")) {
140                type = nextArgRequired();
141                hasIntentInfo = true;
142            } else if (opt.equals("-c")) {
143                intent.addCategory(nextArgRequired());
144                hasIntentInfo = true;
145            } else if (opt.equals("-e") || opt.equals("--es")) {
146                String key = nextArgRequired();
147                String value = nextArgRequired();
148                intent.putExtra(key, value);
149                hasIntentInfo = true;
150            } else if (opt.equals("--esn")) {
151                String key = nextArgRequired();
152                intent.putExtra(key, (String) null);
153                hasIntentInfo = true;
154            } else if (opt.equals("--ei")) {
155                String key = nextArgRequired();
156                String value = nextArgRequired();
157                intent.putExtra(key, Integer.valueOf(value));
158                hasIntentInfo = true;
159            } else if (opt.equals("--eia")) {
160                String key = nextArgRequired();
161                String value = nextArgRequired();
162                String[] strings = value.split(",");
163                int[] list = new int[strings.length];
164                for (int i = 0; i < strings.length; i++) {
165                    list[i] = Integer.valueOf(strings[i]);
166                }
167                intent.putExtra(key, list);
168                hasIntentInfo = true;
169            } else if (opt.equals("--el")) {
170                String key = nextArgRequired();
171                String value = nextArgRequired();
172                intent.putExtra(key, Long.valueOf(value));
173                hasIntentInfo = true;
174            } else if (opt.equals("--ela")) {
175                String key = nextArgRequired();
176                String value = nextArgRequired();
177                String[] strings = value.split(",");
178                long[] list = new long[strings.length];
179                for (int i = 0; i < strings.length; i++) {
180                    list[i] = Long.valueOf(strings[i]);
181                }
182                intent.putExtra(key, list);
183                hasIntentInfo = true;
184            } else if (opt.equals("--ez")) {
185                String key = nextArgRequired();
186                String value = nextArgRequired();
187                intent.putExtra(key, Boolean.valueOf(value));
188                hasIntentInfo = true;
189            } else if (opt.equals("-n")) {
190                String str = nextArgRequired();
191                ComponentName cn = ComponentName.unflattenFromString(str);
192                if (cn == null) throw new IllegalArgumentException("Bad component name: " + str);
193                intent.setComponent(cn);
194                hasIntentInfo = true;
195            } else if (opt.equals("-f")) {
196                String str = nextArgRequired();
197                intent.setFlags(Integer.decode(str).intValue());
198            } else if (opt.equals("--grant-read-uri-permission")) {
199                intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
200            } else if (opt.equals("--grant-write-uri-permission")) {
201                intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
202            } else if (opt.equals("--debug-log-resolution")) {
203                intent.addFlags(Intent.FLAG_DEBUG_LOG_RESOLUTION);
204            } else if (opt.equals("--activity-brought-to-front")) {
205                intent.addFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
206            } else if (opt.equals("--activity-clear-top")) {
207                intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
208            } else if (opt.equals("--activity-clear-when-task-reset")) {
209                intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
210            } else if (opt.equals("--activity-exclude-from-recents")) {
211                intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
212            } else if (opt.equals("--activity-launched-from-history")) {
213                intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY);
214            } else if (opt.equals("--activity-multiple-task")) {
215                intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
216            } else if (opt.equals("--activity-no-animation")) {
217                intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
218            } else if (opt.equals("--activity-no-history")) {
219                intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
220            } else if (opt.equals("--activity-no-user-action")) {
221                intent.addFlags(Intent.FLAG_ACTIVITY_NO_USER_ACTION);
222            } else if (opt.equals("--activity-previous-is-top")) {
223                intent.addFlags(Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP);
224            } else if (opt.equals("--activity-reorder-to-front")) {
225                intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
226            } else if (opt.equals("--activity-reset-task-if-needed")) {
227                intent.addFlags(Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
228            } else if (opt.equals("--activity-single-top")) {
229                intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
230            } else if (opt.equals("--receiver-registered-only")) {
231                intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
232            } else if (opt.equals("--receiver-replace-pending")) {
233                intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
234            } else if (opt.equals("-D")) {
235                mDebugOption = true;
236            } else if (opt.equals("-W")) {
237                mWaitOption = true;
238            } else {
239                System.err.println("Error: Unknown option: " + opt);
240                showUsage();
241                return null;
242            }
243        }
244        intent.setDataAndType(data, type);
245
246        String uri = nextArg();
247        if (uri != null) {
248            Intent oldIntent = intent;
249            intent = Intent.parseUri(uri, 0);
250            if (oldIntent.getAction() != null) {
251                intent.setAction(oldIntent.getAction());
252            }
253            if (oldIntent.getData() != null || oldIntent.getType() != null) {
254                intent.setDataAndType(oldIntent.getData(), oldIntent.getType());
255            }
256            Set cats = oldIntent.getCategories();
257            if (cats != null) {
258                Iterator it = cats.iterator();
259                while (it.hasNext()) {
260                    intent.addCategory((String)it.next());
261                }
262            }
263            hasIntentInfo = true;
264        }
265
266        if (!hasIntentInfo) throw new IllegalArgumentException("No intent supplied");
267        return intent;
268    }
269
270    private void runStartService() throws Exception {
271        Intent intent = makeIntent();
272        System.out.println("Starting service: " + intent);
273        ComponentName cn = mAm.startService(null, intent, intent.getType());
274        if (cn == null) {
275            System.err.println("Error: Not found; no service started.");
276        }
277    }
278
279    private void runStart() throws Exception {
280        Intent intent = makeIntent();
281        System.out.println("Starting: " + intent);
282        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
283        // XXX should do something to determine the MIME type.
284        IActivityManager.WaitResult result = null;
285        int res;
286        if (mWaitOption) {
287            result = mAm.startActivityAndWait(null, intent, intent.getType(),
288                        null, 0, null, null, 0, false, mDebugOption);
289            res = result.result;
290        } else {
291            res = mAm.startActivity(null, intent, intent.getType(),
292                    null, 0, null, null, 0, false, mDebugOption);
293        }
294        PrintStream out = mWaitOption ? System.out : System.err;
295        boolean launched = false;
296        switch (res) {
297            case IActivityManager.START_SUCCESS:
298                launched = true;
299                break;
300            case IActivityManager.START_SWITCHES_CANCELED:
301                launched = true;
302                out.println(
303                        "Warning: Activity not started because the "
304                        + " current activity is being kept for the user.");
305                break;
306            case IActivityManager.START_DELIVERED_TO_TOP:
307                launched = true;
308                out.println(
309                        "Warning: Activity not started, intent has "
310                        + "been delivered to currently running "
311                        + "top-most instance.");
312                break;
313            case IActivityManager.START_RETURN_INTENT_TO_CALLER:
314                launched = true;
315                out.println(
316                        "Warning: Activity not started because intent "
317                        + "should be handled by the caller");
318                break;
319            case IActivityManager.START_TASK_TO_FRONT:
320                launched = true;
321                out.println(
322                        "Warning: Activity not started, its current "
323                        + "task has been brought to the front");
324                break;
325            case IActivityManager.START_INTENT_NOT_RESOLVED:
326                out.println(
327                        "Error: Activity not started, unable to "
328                        + "resolve " + intent.toString());
329                break;
330            case IActivityManager.START_CLASS_NOT_FOUND:
331                out.println(NO_CLASS_ERROR_CODE);
332                out.println("Error: Activity class " +
333                        intent.getComponent().toShortString()
334                        + " does not exist.");
335                break;
336            case IActivityManager.START_FORWARD_AND_REQUEST_CONFLICT:
337                out.println(
338                        "Error: Activity not started, you requested to "
339                        + "both forward and receive its result");
340                break;
341            case IActivityManager.START_PERMISSION_DENIED:
342                out.println(
343                        "Error: Activity not started, you do not "
344                        + "have permission to access it.");
345                break;
346            default:
347                out.println(
348                        "Error: Activity not started, unknown error code " + res);
349                break;
350        }
351        if (mWaitOption && launched) {
352            if (result == null) {
353                result = new IActivityManager.WaitResult();
354                result.who = intent.getComponent();
355            }
356            System.out.println("Status: " + (result.timeout ? "timeout" : "ok"));
357            if (result.who != null) {
358                System.out.println("Activity: " + result.who.flattenToShortString());
359            }
360            if (result.thisTime >= 0) {
361                System.out.println("ThisTime: " + result.thisTime);
362            }
363            if (result.totalTime >= 0) {
364                System.out.println("TotalTime: " + result.totalTime);
365            }
366            System.out.println("Complete");
367        }
368    }
369
370    private void runForceStop() throws Exception {
371        mAm.forceStopPackage(nextArgRequired());
372    }
373
374    private void sendBroadcast() throws Exception {
375        Intent intent = makeIntent();
376        IntentReceiver receiver = new IntentReceiver();
377        System.out.println("Broadcasting: " + intent);
378        mAm.broadcastIntent(null, intent, null, receiver, 0, null, null, null, true, false);
379        receiver.waitForFinish();
380    }
381
382    private void runInstrument() throws Exception {
383        String profileFile = null;
384        boolean wait = false;
385        boolean rawMode = false;
386        boolean no_window_animation = false;
387        Bundle args = new Bundle();
388        String argKey = null, argValue = null;
389        IWindowManager wm = IWindowManager.Stub.asInterface(ServiceManager.getService("window"));
390
391        String opt;
392        while ((opt=nextOption()) != null) {
393            if (opt.equals("-p")) {
394                profileFile = nextArgRequired();
395            } else if (opt.equals("-w")) {
396                wait = true;
397            } else if (opt.equals("-r")) {
398                rawMode = true;
399            } else if (opt.equals("-e")) {
400                argKey = nextArgRequired();
401                argValue = nextArgRequired();
402                args.putString(argKey, argValue);
403            } else if (opt.equals("--no_window_animation")) {
404                no_window_animation = true;
405            } else {
406                System.err.println("Error: Unknown option: " + opt);
407                showUsage();
408                return;
409            }
410        }
411
412        String cnArg = nextArgRequired();
413        ComponentName cn = ComponentName.unflattenFromString(cnArg);
414        if (cn == null) throw new IllegalArgumentException("Bad component name: " + cnArg);
415
416        InstrumentationWatcher watcher = null;
417        if (wait) {
418            watcher = new InstrumentationWatcher();
419            watcher.setRawOutput(rawMode);
420        }
421        float[] oldAnims = null;
422        if (no_window_animation) {
423            oldAnims = wm.getAnimationScales();
424            wm.setAnimationScale(0, 0.0f);
425            wm.setAnimationScale(1, 0.0f);
426        }
427
428        if (!mAm.startInstrumentation(cn, profileFile, 0, args, watcher)) {
429            throw new AndroidException("INSTRUMENTATION_FAILED: " + cn.flattenToString());
430        }
431
432        if (watcher != null) {
433            if (!watcher.waitForFinish()) {
434                System.out.println("INSTRUMENTATION_ABORTED: System has crashed.");
435            }
436        }
437
438        if (oldAnims != null) {
439            wm.setAnimationScales(oldAnims);
440        }
441    }
442
443    private void runProfile() throws Exception {
444        String profileFile = null;
445        boolean start = false;
446        String process = nextArgRequired();
447        ParcelFileDescriptor fd = null;
448
449        String cmd = nextArgRequired();
450        if ("start".equals(cmd)) {
451            start = true;
452            profileFile = nextArgRequired();
453            try {
454                fd = ParcelFileDescriptor.open(
455                        new File(profileFile),
456                        ParcelFileDescriptor.MODE_CREATE |
457                        ParcelFileDescriptor.MODE_TRUNCATE |
458                        ParcelFileDescriptor.MODE_READ_WRITE);
459            } catch (FileNotFoundException e) {
460                System.err.println("Error: Unable to open file: " + profileFile);
461                return;
462            }
463        } else if (!"stop".equals(cmd)) {
464            throw new IllegalArgumentException("Profile command " + cmd + " not valid");
465        }
466
467        if (!mAm.profileControl(process, start, profileFile, fd)) {
468            throw new AndroidException("PROFILE FAILED on process " + process);
469        }
470    }
471
472    private void runDumpHeap() throws Exception {
473        boolean managed = !"-n".equals(nextOption());
474        String process = nextArgRequired();
475        String heapFile = nextArgRequired();
476        ParcelFileDescriptor fd = null;
477
478        try {
479            fd = ParcelFileDescriptor.open(
480                    new File(heapFile),
481                    ParcelFileDescriptor.MODE_CREATE |
482                    ParcelFileDescriptor.MODE_TRUNCATE |
483                    ParcelFileDescriptor.MODE_READ_WRITE);
484        } catch (FileNotFoundException e) {
485            System.err.println("Error: Unable to open file: " + heapFile);
486            return;
487        }
488
489        if (!mAm.dumpHeap(process, managed, heapFile, fd)) {
490            throw new AndroidException("HEAP DUMP FAILED on process " + process);
491        }
492    }
493
494    class MyActivityController extends IActivityController.Stub {
495        final String mGdbPort;
496
497        static final int STATE_NORMAL = 0;
498        static final int STATE_CRASHED = 1;
499        static final int STATE_EARLY_ANR = 2;
500        static final int STATE_ANR = 3;
501
502        int mState;
503
504        static final int RESULT_DEFAULT = 0;
505
506        static final int RESULT_CRASH_DIALOG = 0;
507        static final int RESULT_CRASH_KILL = 1;
508
509        static final int RESULT_EARLY_ANR_CONTINUE = 0;
510        static final int RESULT_EARLY_ANR_KILL = 1;
511
512        static final int RESULT_ANR_DIALOG = 0;
513        static final int RESULT_ANR_KILL = 1;
514        static final int RESULT_ANR_WAIT = 1;
515
516        int mResult;
517
518        Process mGdbProcess;
519        Thread mGdbThread;
520        boolean mGotGdbPrint;
521
522        MyActivityController(String gdbPort) {
523            mGdbPort = gdbPort;
524        }
525
526        @Override
527        public boolean activityResuming(String pkg) throws RemoteException {
528            synchronized (this) {
529                System.out.println("** Activity resuming: " + pkg);
530            }
531            return true;
532        }
533
534        @Override
535        public boolean activityStarting(Intent intent, String pkg) throws RemoteException {
536            synchronized (this) {
537                System.out.println("** Activity starting: " + pkg);
538            }
539            return true;
540        }
541
542        @Override
543        public boolean appCrashed(String processName, int pid, String shortMsg, String longMsg,
544                long timeMillis, String stackTrace) throws RemoteException {
545            synchronized (this) {
546                System.out.println("** ERROR: PROCESS CRASHED");
547                System.out.println("processName: " + processName);
548                System.out.println("processPid: " + pid);
549                System.out.println("shortMsg: " + shortMsg);
550                System.out.println("longMsg: " + longMsg);
551                System.out.println("timeMillis: " + timeMillis);
552                System.out.println("stack:");
553                System.out.print(stackTrace);
554                System.out.println("#");
555                int result = waitControllerLocked(pid, STATE_CRASHED);
556                return result == RESULT_CRASH_KILL ? false : true;
557            }
558        }
559
560        @Override
561        public int appEarlyNotResponding(String processName, int pid, String annotation)
562                throws RemoteException {
563            synchronized (this) {
564                System.out.println("** ERROR: EARLY PROCESS NOT RESPONDING");
565                System.out.println("processName: " + processName);
566                System.out.println("processPid: " + pid);
567                System.out.println("annotation: " + annotation);
568                int result = waitControllerLocked(pid, STATE_EARLY_ANR);
569                if (result == RESULT_EARLY_ANR_KILL) return -1;
570                return 0;
571            }
572        }
573
574        @Override
575        public int appNotResponding(String processName, int pid, String processStats)
576                throws RemoteException {
577            synchronized (this) {
578                System.out.println("** ERROR: PROCESS NOT RESPONDING");
579                System.out.println("processName: " + processName);
580                System.out.println("processPid: " + pid);
581                System.out.println("processStats:");
582                System.out.print(processStats);
583                System.out.println("#");
584                int result = waitControllerLocked(pid, STATE_ANR);
585                if (result == RESULT_ANR_KILL) return -1;
586                if (result == RESULT_ANR_WAIT) return 1;
587                return 0;
588            }
589        }
590
591        void killGdbLocked() {
592            mGotGdbPrint = false;
593            if (mGdbProcess != null) {
594                System.out.println("Stopping gdbserver");
595                mGdbProcess.destroy();
596                mGdbProcess = null;
597            }
598            if (mGdbThread != null) {
599                mGdbThread.interrupt();
600                mGdbThread = null;
601            }
602        }
603
604        int waitControllerLocked(int pid, int state) {
605            if (mGdbPort != null) {
606                killGdbLocked();
607
608                try {
609                    System.out.println("Starting gdbserver on port " + mGdbPort);
610                    System.out.println("Do the following:");
611                    System.out.println("  adb forward tcp:" + mGdbPort + " tcp:" + mGdbPort);
612                    System.out.println("  gdbclient app_process :" + mGdbPort);
613
614                    mGdbProcess = Runtime.getRuntime().exec(new String[] {
615                            "gdbserver", ":" + mGdbPort, "--attach", Integer.toString(pid)
616                    });
617                    final InputStreamReader converter = new InputStreamReader(
618                            mGdbProcess.getInputStream());
619                    mGdbThread = new Thread() {
620                        @Override
621                        public void run() {
622                            BufferedReader in = new BufferedReader(converter);
623                            String line;
624                            int count = 0;
625                            while (true) {
626                                synchronized (MyActivityController.this) {
627                                    if (mGdbThread == null) {
628                                        return;
629                                    }
630                                    if (count == 2) {
631                                        mGotGdbPrint = true;
632                                        MyActivityController.this.notifyAll();
633                                    }
634                                }
635                                try {
636                                    line = in.readLine();
637                                    if (line == null) {
638                                        return;
639                                    }
640                                    System.out.println("GDB: " + line);
641                                    count++;
642                                } catch (IOException e) {
643                                    return;
644                                }
645                            }
646                        }
647                    };
648                    mGdbThread.start();
649
650                    // Stupid waiting for .5s.  Doesn't matter if we end early.
651                    try {
652                        this.wait(500);
653                    } catch (InterruptedException e) {
654                    }
655
656                } catch (IOException e) {
657                    System.err.println("Failure starting gdbserver: " + e);
658                    killGdbLocked();
659                }
660            }
661            mState = state;
662            System.out.println("");
663            printMessageForState();
664
665            while (mState != STATE_NORMAL) {
666                try {
667                    wait();
668                } catch (InterruptedException e) {
669                }
670            }
671
672            killGdbLocked();
673
674            return mResult;
675        }
676
677        void resumeController(int result) {
678            synchronized (this) {
679                mState = STATE_NORMAL;
680                mResult = result;
681                notifyAll();
682            }
683        }
684
685        void printMessageForState() {
686            switch (mState) {
687                case STATE_NORMAL:
688                    System.out.println("Monitoring activity manager...  available commands:");
689                    break;
690                case STATE_CRASHED:
691                    System.out.println("Waiting after crash...  available commands:");
692                    System.out.println("(c)ontinue: show crash dialog");
693                    System.out.println("(k)ill: immediately kill app");
694                    break;
695                case STATE_EARLY_ANR:
696                    System.out.println("Waiting after early ANR...  available commands:");
697                    System.out.println("(c)ontinue: standard ANR processing");
698                    System.out.println("(k)ill: immediately kill app");
699                    break;
700                case STATE_ANR:
701                    System.out.println("Waiting after ANR...  available commands:");
702                    System.out.println("(c)ontinue: show ANR dialog");
703                    System.out.println("(k)ill: immediately kill app");
704                    System.out.println("(w)ait: wait some more");
705                    break;
706            }
707            System.out.println("(q)uit: finish monitoring");
708        }
709
710        void run() throws RemoteException {
711            try {
712                printMessageForState();
713
714                mAm.setActivityController(this);
715                mState = STATE_NORMAL;
716
717                InputStreamReader converter = new InputStreamReader(System.in);
718                BufferedReader in = new BufferedReader(converter);
719                String line;
720
721                while ((line = in.readLine()) != null) {
722                    boolean addNewline = true;
723                    if (line.length() <= 0) {
724                        addNewline = false;
725                    } else if ("q".equals(line) || "quit".equals(line)) {
726                        resumeController(RESULT_DEFAULT);
727                        break;
728                    } else if (mState == STATE_CRASHED) {
729                        if ("c".equals(line) || "continue".equals(line)) {
730                            resumeController(RESULT_CRASH_DIALOG);
731                        } else if ("k".equals(line) || "kill".equals(line)) {
732                            resumeController(RESULT_CRASH_KILL);
733                        } else {
734                            System.out.println("Invalid command: " + line);
735                        }
736                    } else if (mState == STATE_ANR) {
737                        if ("c".equals(line) || "continue".equals(line)) {
738                            resumeController(RESULT_ANR_DIALOG);
739                        } else if ("k".equals(line) || "kill".equals(line)) {
740                            resumeController(RESULT_ANR_KILL);
741                        } else if ("w".equals(line) || "wait".equals(line)) {
742                            resumeController(RESULT_ANR_WAIT);
743                        } else {
744                            System.out.println("Invalid command: " + line);
745                        }
746                    } else if (mState == STATE_EARLY_ANR) {
747                        if ("c".equals(line) || "continue".equals(line)) {
748                            resumeController(RESULT_EARLY_ANR_CONTINUE);
749                        } else if ("k".equals(line) || "kill".equals(line)) {
750                            resumeController(RESULT_EARLY_ANR_KILL);
751                        } else {
752                            System.out.println("Invalid command: " + line);
753                        }
754                    } else {
755                        System.out.println("Invalid command: " + line);
756                    }
757
758                    synchronized (this) {
759                        if (addNewline) {
760                            System.out.println("");
761                        }
762                        printMessageForState();
763                    }
764                }
765
766            } catch (IOException e) {
767                e.printStackTrace();
768            } finally {
769                mAm.setActivityController(null);
770            }
771        }
772    }
773
774    private void runMonitor() throws Exception {
775        String opt;
776        String gdbPort = null;
777        while ((opt=nextOption()) != null) {
778            if (opt.equals("--gdb")) {
779                gdbPort = nextArgRequired();
780            } else {
781                System.err.println("Error: Unknown option: " + opt);
782                showUsage();
783                return;
784            }
785        }
786
787        MyActivityController controller = new MyActivityController(gdbPort);
788        controller.run();
789    }
790
791    private void runScreenCompat() throws Exception {
792        String mode = nextArgRequired();
793        boolean enabled;
794        if ("on".equals(mode)) {
795            enabled = true;
796        } else if ("off".equals(mode)) {
797            enabled = false;
798        } else {
799            System.err.println("Error: enabled mode must be 'on' or 'off' at " + mode);
800            showUsage();
801            return;
802        }
803
804        String packageName = nextArgRequired();
805        do {
806            try {
807                mAm.setPackageScreenCompatMode(packageName, enabled
808                        ? ActivityManager.COMPAT_MODE_ENABLED
809                        : ActivityManager.COMPAT_MODE_DISABLED);
810            } catch (RemoteException e) {
811            }
812            packageName = nextArg();
813        } while (packageName != null);
814    }
815
816    private void runDisplaySize() throws Exception {
817        String size = nextArgRequired();
818        int m, n;
819        if ("reset".equals(size)) {
820            m = n = -1;
821        } else {
822            int div = size.indexOf('x');
823            if (div <= 0 || div >= (size.length()-1)) {
824                System.err.println("Error: bad size " + size);
825                showUsage();
826                return;
827            }
828            String mstr = size.substring(0, div);
829            String nstr = size.substring(div+1);
830            try {
831                m = Integer.parseInt(mstr);
832                n = Integer.parseInt(nstr);
833            } catch (NumberFormatException e) {
834                System.err.println("Error: bad number " + e);
835                showUsage();
836                return;
837            }
838        }
839
840        if (m < n) {
841            int tmp = m;
842            m = n;
843            n = tmp;
844        }
845
846        IWindowManager wm = IWindowManager.Stub.asInterface(ServiceManager.checkService(
847                Context.WINDOW_SERVICE));
848        if (wm == null) {
849            System.err.println(NO_SYSTEM_ERROR_CODE);
850            throw new AndroidException("Can't connect to window manager; is the system running?");
851        }
852
853        try {
854            if (m >= 0 && n >= 0) {
855                wm.setForcedDisplaySize(m, n);
856            } else {
857                wm.clearForcedDisplaySize();
858            }
859        } catch (RemoteException e) {
860        }
861    }
862
863    private class IntentReceiver extends IIntentReceiver.Stub {
864        private boolean mFinished = false;
865
866        public synchronized void performReceive(
867                Intent intent, int rc, String data, Bundle ext, boolean ord,
868                boolean sticky) {
869            String line = "Broadcast completed: result=" + rc;
870            if (data != null) line = line + ", data=\"" + data + "\"";
871            if (ext != null) line = line + ", extras: " + ext;
872            System.out.println(line);
873            mFinished = true;
874            notifyAll();
875        }
876
877        public synchronized void waitForFinish() {
878            try {
879                while (!mFinished) wait();
880            } catch (InterruptedException e) {
881                throw new IllegalStateException(e);
882            }
883        }
884    }
885
886    private class InstrumentationWatcher extends IInstrumentationWatcher.Stub {
887        private boolean mFinished = false;
888        private boolean mRawMode = false;
889
890        /**
891         * Set or reset "raw mode".  In "raw mode", all bundles are dumped.  In "pretty mode",
892         * if a bundle includes Instrumentation.REPORT_KEY_STREAMRESULT, just print that.
893         * @param rawMode true for raw mode, false for pretty mode.
894         */
895        public void setRawOutput(boolean rawMode) {
896            mRawMode = rawMode;
897        }
898
899        public void instrumentationStatus(ComponentName name, int resultCode, Bundle results) {
900            synchronized (this) {
901                // pretty printer mode?
902                String pretty = null;
903                if (!mRawMode && results != null) {
904                    pretty = results.getString(Instrumentation.REPORT_KEY_STREAMRESULT);
905                }
906                if (pretty != null) {
907                    System.out.print(pretty);
908                } else {
909                    if (results != null) {
910                        for (String key : results.keySet()) {
911                            System.out.println(
912                                    "INSTRUMENTATION_STATUS: " + key + "=" + results.get(key));
913                        }
914                    }
915                    System.out.println("INSTRUMENTATION_STATUS_CODE: " + resultCode);
916                }
917                notifyAll();
918            }
919        }
920
921        public void instrumentationFinished(ComponentName name, int resultCode,
922                Bundle results) {
923            synchronized (this) {
924                // pretty printer mode?
925                String pretty = null;
926                if (!mRawMode && results != null) {
927                    pretty = results.getString(Instrumentation.REPORT_KEY_STREAMRESULT);
928                }
929                if (pretty != null) {
930                    System.out.println(pretty);
931                } else {
932                    if (results != null) {
933                        for (String key : results.keySet()) {
934                            System.out.println(
935                                    "INSTRUMENTATION_RESULT: " + key + "=" + results.get(key));
936                        }
937                    }
938                    System.out.println("INSTRUMENTATION_CODE: " + resultCode);
939                }
940                mFinished = true;
941                notifyAll();
942            }
943        }
944
945        public boolean waitForFinish() {
946            synchronized (this) {
947                while (!mFinished) {
948                    try {
949                        if (!mAm.asBinder().pingBinder()) {
950                            return false;
951                        }
952                        wait(1000);
953                    } catch (InterruptedException e) {
954                        throw new IllegalStateException(e);
955                    }
956                }
957            }
958            return true;
959        }
960    }
961
962    private String nextOption() {
963        if (mCurArgData != null) {
964            String prev = mArgs[mNextArg - 1];
965            throw new IllegalArgumentException("No argument expected after \"" + prev + "\"");
966        }
967        if (mNextArg >= mArgs.length) {
968            return null;
969        }
970        String arg = mArgs[mNextArg];
971        if (!arg.startsWith("-")) {
972            return null;
973        }
974        mNextArg++;
975        if (arg.equals("--")) {
976            return null;
977        }
978        if (arg.length() > 1 && arg.charAt(1) != '-') {
979            if (arg.length() > 2) {
980                mCurArgData = arg.substring(2);
981                return arg.substring(0, 2);
982            } else {
983                mCurArgData = null;
984                return arg;
985            }
986        }
987        mCurArgData = null;
988        return arg;
989    }
990
991    private String nextArg() {
992        if (mCurArgData != null) {
993            String arg = mCurArgData;
994            mCurArgData = null;
995            return arg;
996        } else if (mNextArg < mArgs.length) {
997            return mArgs[mNextArg++];
998        } else {
999            return null;
1000        }
1001    }
1002
1003    private String nextArgRequired() {
1004        String arg = nextArg();
1005        if (arg == null) {
1006            String prev = mArgs[mNextArg - 1];
1007            throw new IllegalArgumentException("Argument expected after \"" + prev + "\"");
1008        }
1009        return arg;
1010    }
1011
1012    private static void showUsage() {
1013        System.err.println(
1014                "usage: am [subcommand] [options]\n" +
1015                "\n" +
1016                "    start an Activity: am start [-D] [-W] <INTENT>\n" +
1017                "        -D: enable debugging\n" +
1018                "        -W: wait for launch to complete\n" +
1019                "\n" +
1020                "    start a Service: am startservice <INTENT>\n" +
1021                "\n" +
1022                "    force stop everything associated with a package: force-stop <package>\n" +
1023                "\n" +
1024                "    send a broadcast Intent: am broadcast <INTENT>\n" +
1025                "\n" +
1026                "    start an Instrumentation: am instrument [flags] <COMPONENT>\n" +
1027                "        -r: print raw results (otherwise decode REPORT_KEY_STREAMRESULT)\n" +
1028                "        -e <NAME> <VALUE>: set argument <NAME> to <VALUE>\n" +
1029                "        -p <FILE>: write profiling data to <FILE>\n" +
1030                "        -w: wait for instrumentation to finish before returning\n" +
1031                "\n" +
1032                "    run a test package against an application: am instrument [flags] <TEST_PACKAGE>/<RUNNER_CLASS>\n" +
1033                "        -e <testrunner_flag> <testrunner_value> [,<testrunner_value>]\n" +
1034                "        -w wait for the test to finish (required)\n" +
1035                "        -r use with -e perf true to generate raw output for performance measurements\n" +
1036                "\n" +
1037                "    start profiling: am profile <PROCESS> start <FILE>\n" +
1038                "    stop profiling: am profile <PROCESS> stop\n" +
1039                "    dump heap: am dumpheap [flags] <PROCESS> <FILE>\n" +
1040                "        -n: dump native heap instead of managed heap\n" +
1041                "\n" +
1042                "    start monitoring: am monitor [--gdb <port>]\n" +
1043                "        --gdb: start gdbserv on the given port at crash/ANR\n" +
1044                "\n" +
1045                "    control screen compatibility: am screen-compat [on|off] [package]\n" +
1046                "\n" +
1047                "    override display size: am display-size [reset|MxN]\n" +
1048                "\n" +
1049                "    <INTENT> specifications include these flags:\n" +
1050                "        [-a <ACTION>] [-d <DATA_URI>] [-t <MIME_TYPE>]\n" +
1051                "        [-c <CATEGORY> [-c <CATEGORY>] ...]\n" +
1052                "        [-e|--es <EXTRA_KEY> <EXTRA_STRING_VALUE> ...]\n" +
1053                "        [--esn <EXTRA_KEY> ...]\n" +
1054                "        [--ez <EXTRA_KEY> <EXTRA_BOOLEAN_VALUE> ...]\n" +
1055                "        [--ei <EXTRA_KEY> <EXTRA_INT_VALUE> ...]\n" +
1056                "        [--el <EXTRA_KEY> <EXTRA_LONG_VALUE> ...]\n" +
1057                "        [--eia <EXTRA_KEY> <EXTRA_INT_VALUE>[,<EXTRA_INT_VALUE...]]\n" +
1058                "        [--ela <EXTRA_KEY> <EXTRA_LONG_VALUE>[,<EXTRA_LONG_VALUE...]]\n" +
1059                "        [-n <COMPONENT>] [-f <FLAGS>]\n" +
1060                "        [--grant-read-uri-permission] [--grant-write-uri-permission]\n" +
1061                "        [--debug-log-resolution]\n" +
1062                "        [--activity-brought-to-front] [--activity-clear-top]\n" +
1063                "        [--activity-clear-when-task-reset] [--activity-exclude-from-recents]\n" +
1064                "        [--activity-launched-from-history] [--activity-multiple-task]\n" +
1065                "        [--activity-no-animation] [--activity-no-history]\n" +
1066                "        [--activity-no-user-action] [--activity-previous-is-top]\n" +
1067                "        [--activity-reorder-to-front] [--activity-reset-task-if-needed]\n" +
1068                "        [--activity-single-top]\n" +
1069                "        [--receiver-registered-only] [--receiver-replace-pending]\n" +
1070                "        [<URI>]\n"
1071                );
1072    }
1073}
1074