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