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