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.ActivityManager.StackInfo;
23import android.app.ActivityManagerNative;
24import android.app.IActivityContainer;
25import android.app.IActivityController;
26import android.app.IActivityManager;
27import android.app.IInstrumentationWatcher;
28import android.app.Instrumentation;
29import android.app.ProfilerInfo;
30import android.app.UiAutomationConnection;
31import android.app.usage.ConfigurationStats;
32import android.app.usage.IUsageStatsManager;
33import android.app.usage.UsageStatsManager;
34import android.content.ComponentName;
35import android.content.Context;
36import android.content.IIntentReceiver;
37import android.content.Intent;
38import android.content.pm.IPackageManager;
39import android.content.pm.ParceledListSlice;
40import android.content.pm.ResolveInfo;
41import android.content.res.Configuration;
42import android.graphics.Rect;
43import android.net.Uri;
44import android.os.Binder;
45import android.os.Build;
46import android.os.Bundle;
47import android.os.IBinder;
48import android.os.ParcelFileDescriptor;
49import android.os.RemoteException;
50import android.os.SELinux;
51import android.os.ServiceManager;
52import android.os.SystemClock;
53import android.os.SystemProperties;
54import android.os.UserHandle;
55import android.text.TextUtils;
56import android.util.AndroidException;
57import android.util.ArrayMap;
58import android.view.IWindowManager;
59
60import com.android.internal.os.BaseCommand;
61
62import java.io.BufferedReader;
63import java.io.File;
64import java.io.FileNotFoundException;
65import java.io.IOException;
66import java.io.InputStreamReader;
67import java.io.PrintStream;
68import java.net.URISyntaxException;
69import java.util.ArrayList;
70import java.util.Collections;
71import java.util.Comparator;
72import java.util.HashSet;
73import java.util.List;
74
75public class Am extends BaseCommand {
76
77    private IActivityManager mAm;
78
79    private int mStartFlags = 0;
80    private boolean mWaitOption = false;
81    private boolean mStopOption = false;
82
83    private int mRepeat = 0;
84    private int mUserId;
85    private String mReceiverPermission;
86
87    private String mProfileFile;
88    private int mSamplingInterval;
89    private boolean mAutoStop;
90
91    /**
92     * Command-line entry point.
93     *
94     * @param args The command-line arguments
95     */
96    public static void main(String[] args) {
97        (new Am()).run(args);
98    }
99
100    @Override
101    public void onShowUsage(PrintStream out) {
102        out.println(
103                "usage: am [subcommand] [options]\n" +
104                "usage: am start [-D] [-W] [-P <FILE>] [--start-profiler <FILE>]\n" +
105                "               [--sampling INTERVAL] [-R COUNT] [-S] [--opengl-trace]\n" +
106                "               [--user <USER_ID> | current] <INTENT>\n" +
107                "       am startservice [--user <USER_ID> | current] <INTENT>\n" +
108                "       am stopservice [--user <USER_ID> | current] <INTENT>\n" +
109                "       am force-stop [--user <USER_ID> | all | current] <PACKAGE>\n" +
110                "       am kill [--user <USER_ID> | all | current] <PACKAGE>\n" +
111                "       am kill-all\n" +
112                "       am broadcast [--user <USER_ID> | all | current] <INTENT>\n" +
113                "       am instrument [-r] [-e <NAME> <VALUE>] [-p <FILE>] [-w]\n" +
114                "               [--user <USER_ID> | current]\n" +
115                "               [--no-window-animation] [--abi <ABI>] <COMPONENT>\n" +
116                "       am profile start [--user <USER_ID> current] <PROCESS> <FILE>\n" +
117                "       am profile stop [--user <USER_ID> current] [<PROCESS>]\n" +
118                "       am dumpheap [--user <USER_ID> current] [-n] <PROCESS> <FILE>\n" +
119                "       am set-debug-app [-w] [--persistent] <PACKAGE>\n" +
120                "       am clear-debug-app\n" +
121                "       am monitor [--gdb <port>]\n" +
122                "       am hang [--allow-restart]\n" +
123                "       am restart\n" +
124                "       am idle-maintenance\n" +
125                "       am screen-compat [on|off] <PACKAGE>\n" +
126                "       am to-uri [INTENT]\n" +
127                "       am to-intent-uri [INTENT]\n" +
128                "       am to-app-uri [INTENT]\n" +
129                "       am switch-user <USER_ID>\n" +
130                "       am start-user <USER_ID>\n" +
131                "       am stop-user <USER_ID>\n" +
132                "       am stack start <DISPLAY_ID> <INTENT>\n" +
133                "       am stack movetask <TASK_ID> <STACK_ID> [true|false]\n" +
134                "       am stack resize <STACK_ID> <LEFT,TOP,RIGHT,BOTTOM>\n" +
135                "       am stack list\n" +
136                "       am stack info <STACK_ID>\n" +
137                "       am lock-task <TASK_ID>\n" +
138                "       am lock-task stop\n" +
139                "       am get-config\n" +
140                "\n" +
141                "am start: start an Activity.  Options are:\n" +
142                "    -D: enable debugging\n" +
143                "    -W: wait for launch to complete\n" +
144                "    --start-profiler <FILE>: start profiler and send results to <FILE>\n" +
145                "    --sampling INTERVAL: use sample profiling with INTERVAL microseconds\n" +
146                "        between samples (use with --start-profiler)\n" +
147                "    -P <FILE>: like above, but profiling stops when app goes idle\n" +
148                "    -R: repeat the activity launch <COUNT> times.  Prior to each repeat,\n" +
149                "        the top activity will be finished.\n" +
150                "    -S: force stop the target app before starting the activity\n" +
151                "    --opengl-trace: enable tracing of OpenGL functions\n" +
152                "    --user <USER_ID> | current: Specify which user to run as; if not\n" +
153                "        specified then run as the current user.\n" +
154                "\n" +
155                "am startservice: start a Service.  Options are:\n" +
156                "    --user <USER_ID> | current: Specify which user to run as; if not\n" +
157                "        specified then run as the current user.\n" +
158                "\n" +
159                "am stopservice: stop a Service.  Options are:\n" +
160                "    --user <USER_ID> | current: Specify which user to run as; if not\n" +
161                "        specified then run as the current user.\n" +
162                "\n" +
163                "am force-stop: force stop everything associated with <PACKAGE>.\n" +
164                "    --user <USER_ID> | all | current: Specify user to force stop;\n" +
165                "        all users if not specified.\n" +
166                "\n" +
167                "am kill: Kill all processes associated with <PACKAGE>.  Only kills.\n" +
168                "  processes that are safe to kill -- that is, will not impact the user\n" +
169                "  experience.\n" +
170                "    --user <USER_ID> | all | current: Specify user whose processes to kill;\n" +
171                "        all users if not specified.\n" +
172                "\n" +
173                "am kill-all: Kill all background processes.\n" +
174                "\n" +
175                "am broadcast: send a broadcast Intent.  Options are:\n" +
176                "    --user <USER_ID> | all | current: Specify which user to send to; if not\n" +
177                "        specified then send to all users.\n" +
178                "    --receiver-permission <PERMISSION>: Require receiver to hold permission.\n" +
179                "\n" +
180                "am instrument: start an Instrumentation.  Typically this target <COMPONENT>\n" +
181                "  is the form <TEST_PACKAGE>/<RUNNER_CLASS>.  Options are:\n" +
182                "    -r: print raw results (otherwise decode REPORT_KEY_STREAMRESULT).  Use with\n" +
183                "        [-e perf true] to generate raw output for performance measurements.\n" +
184                "    -e <NAME> <VALUE>: set argument <NAME> to <VALUE>.  For test runners a\n" +
185                "        common form is [-e <testrunner_flag> <value>[,<value>...]].\n" +
186                "    -p <FILE>: write profiling data to <FILE>\n" +
187                "    -w: wait for instrumentation to finish before returning.  Required for\n" +
188                "        test runners.\n" +
189                "    --user <USER_ID> | current: Specify user instrumentation runs in;\n" +
190                "        current user if not specified.\n" +
191                "    --no-window-animation: turn off window animations while running.\n" +
192                "    --abi <ABI>: Launch the instrumented process with the selected ABI.\n"  +
193                "        This assumes that the process supports the selected ABI.\n" +
194                "\n" +
195                "am profile: start and stop profiler on a process.  The given <PROCESS> argument\n" +
196                "  may be either a process name or pid.  Options are:\n" +
197                "    --user <USER_ID> | current: When supplying a process name,\n" +
198                "        specify user of process to profile; uses current user if not specified.\n" +
199                "\n" +
200                "am dumpheap: dump the heap of a process.  The given <PROCESS> argument may\n" +
201                "  be either a process name or pid.  Options are:\n" +
202                "    -n: dump native heap instead of managed heap\n" +
203                "    --user <USER_ID> | current: When supplying a process name,\n" +
204                "        specify user of process to dump; uses current user if not specified.\n" +
205                "\n" +
206                "am set-debug-app: set application <PACKAGE> to debug.  Options are:\n" +
207                "    -w: wait for debugger when application starts\n" +
208                "    --persistent: retain this value\n" +
209                "\n" +
210                "am clear-debug-app: clear the previously set-debug-app.\n" +
211                "\n" +
212                "am bug-report: request bug report generation; will launch UI\n" +
213                "    when done to select where it should be delivered.\n" +
214                "\n" +
215                "am monitor: start monitoring for crashes or ANRs.\n" +
216                "    --gdb: start gdbserv on the given port at crash/ANR\n" +
217                "\n" +
218                "am hang: hang the system.\n" +
219                "    --allow-restart: allow watchdog to perform normal system restart\n" +
220                "\n" +
221                "am restart: restart the user-space system.\n" +
222                "\n" +
223                "am idle-maintenance: perform idle maintenance now.\n" +
224                "\n" +
225                "am screen-compat: control screen compatibility mode of <PACKAGE>.\n" +
226                "\n" +
227                "am to-uri: print the given Intent specification as a URI.\n" +
228                "\n" +
229                "am to-intent-uri: print the given Intent specification as an intent: URI.\n" +
230                "\n" +
231                "am to-app-uri: print the given Intent specification as an android-app: URI.\n" +
232                "\n" +
233                "am switch-user: switch to put USER_ID in the foreground, starting\n" +
234                "  execution of that user if it is currently stopped.\n" +
235                "\n" +
236                "am start-user: start USER_ID in background if it is currently stopped,\n" +
237                "  use switch-user if you want to start the user in foreground.\n" +
238                "\n" +
239                "am stop-user: stop execution of USER_ID, not allowing it to run any\n" +
240                "  code until a later explicit start or switch to it.\n" +
241                "\n" +
242                "am stack start: start a new activity on <DISPLAY_ID> using <INTENT>.\n" +
243                "\n" +
244                "am stack movetask: move <TASK_ID> from its current stack to the top (true) or" +
245                "   bottom (false) of <STACK_ID>.\n" +
246                "\n" +
247                "am stack resize: change <STACK_ID> size and position to <LEFT,TOP,RIGHT,BOTTOM>.\n" +
248                "\n" +
249                "am stack list: list all of the activity stacks and their sizes.\n" +
250                "\n" +
251                "am stack info: display the information about activity stack <STACK_ID>.\n" +
252                "\n" +
253                "am lock-task: bring <TASK_ID> to the front and don't allow other tasks to run\n" +
254                "\n" +
255                "am get-config: retrieve the configuration and any recent configurations\n" +
256                "  of the device\n" +
257                "\n" +
258                "<INTENT> specifications include these flags and arguments:\n" +
259                "    [-a <ACTION>] [-d <DATA_URI>] [-t <MIME_TYPE>]\n" +
260                "    [-c <CATEGORY> [-c <CATEGORY>] ...]\n" +
261                "    [-e|--es <EXTRA_KEY> <EXTRA_STRING_VALUE> ...]\n" +
262                "    [--esn <EXTRA_KEY> ...]\n" +
263                "    [--ez <EXTRA_KEY> <EXTRA_BOOLEAN_VALUE> ...]\n" +
264                "    [--ei <EXTRA_KEY> <EXTRA_INT_VALUE> ...]\n" +
265                "    [--el <EXTRA_KEY> <EXTRA_LONG_VALUE> ...]\n" +
266                "    [--ef <EXTRA_KEY> <EXTRA_FLOAT_VALUE> ...]\n" +
267                "    [--eu <EXTRA_KEY> <EXTRA_URI_VALUE> ...]\n" +
268                "    [--ecn <EXTRA_KEY> <EXTRA_COMPONENT_NAME_VALUE>]\n" +
269                "    [--eia <EXTRA_KEY> <EXTRA_INT_VALUE>[,<EXTRA_INT_VALUE...]]\n" +
270                "    [--ela <EXTRA_KEY> <EXTRA_LONG_VALUE>[,<EXTRA_LONG_VALUE...]]\n" +
271                "    [--efa <EXTRA_KEY> <EXTRA_FLOAT_VALUE>[,<EXTRA_FLOAT_VALUE...]]\n" +
272                "    [--esa <EXTRA_KEY> <EXTRA_STRING_VALUE>[,<EXTRA_STRING_VALUE...]]\n" +
273                "        (to embed a comma into a string escape it using \"\\,\")\n" +
274                "    [-n <COMPONENT>] [-p <PACKAGE>] [-f <FLAGS>]\n" +
275                "    [--grant-read-uri-permission] [--grant-write-uri-permission]\n" +
276                "    [--grant-persistable-uri-permission] [--grant-prefix-uri-permission]\n" +
277                "    [--debug-log-resolution] [--exclude-stopped-packages]\n" +
278                "    [--include-stopped-packages]\n" +
279                "    [--activity-brought-to-front] [--activity-clear-top]\n" +
280                "    [--activity-clear-when-task-reset] [--activity-exclude-from-recents]\n" +
281                "    [--activity-launched-from-history] [--activity-multiple-task]\n" +
282                "    [--activity-no-animation] [--activity-no-history]\n" +
283                "    [--activity-no-user-action] [--activity-previous-is-top]\n" +
284                "    [--activity-reorder-to-front] [--activity-reset-task-if-needed]\n" +
285                "    [--activity-single-top] [--activity-clear-task]\n" +
286                "    [--activity-task-on-home]\n" +
287                "    [--receiver-registered-only] [--receiver-replace-pending]\n" +
288                "    [--selector]\n" +
289                "    [<URI> | <PACKAGE> | <COMPONENT>]\n"
290                );
291    }
292
293    @Override
294    public void onRun() throws Exception {
295
296        mAm = ActivityManagerNative.getDefault();
297        if (mAm == null) {
298            System.err.println(NO_SYSTEM_ERROR_CODE);
299            throw new AndroidException("Can't connect to activity manager; is the system running?");
300        }
301
302        String op = nextArgRequired();
303
304        if (op.equals("start")) {
305            runStart();
306        } else if (op.equals("startservice")) {
307            runStartService();
308        } else if (op.equals("stopservice")) {
309            runStopService();
310        } else if (op.equals("force-stop")) {
311            runForceStop();
312        } else if (op.equals("kill")) {
313            runKill();
314        } else if (op.equals("kill-all")) {
315            runKillAll();
316        } else if (op.equals("instrument")) {
317            runInstrument();
318        } else if (op.equals("broadcast")) {
319            sendBroadcast();
320        } else if (op.equals("profile")) {
321            runProfile();
322        } else if (op.equals("dumpheap")) {
323            runDumpHeap();
324        } else if (op.equals("set-debug-app")) {
325            runSetDebugApp();
326        } else if (op.equals("clear-debug-app")) {
327            runClearDebugApp();
328        } else if (op.equals("bug-report")) {
329            runBugReport();
330        } else if (op.equals("monitor")) {
331            runMonitor();
332        } else if (op.equals("hang")) {
333            runHang();
334        } else if (op.equals("restart")) {
335            runRestart();
336        } else if (op.equals("idle-maintenance")) {
337            runIdleMaintenance();
338        } else if (op.equals("screen-compat")) {
339            runScreenCompat();
340        } else if (op.equals("to-uri")) {
341            runToUri(0);
342        } else if (op.equals("to-intent-uri")) {
343            runToUri(Intent.URI_INTENT_SCHEME);
344        } else if (op.equals("to-app-uri")) {
345            runToUri(Intent.URI_ANDROID_APP_SCHEME);
346        } else if (op.equals("switch-user")) {
347            runSwitchUser();
348        } else if (op.equals("start-user")) {
349            runStartUserInBackground();
350        } else if (op.equals("stop-user")) {
351            runStopUser();
352        } else if (op.equals("stack")) {
353            runStack();
354        } else if (op.equals("lock-task")) {
355            runLockTask();
356        } else if (op.equals("get-config")) {
357            runGetConfig();
358        } else {
359            showError("Error: unknown command '" + op + "'");
360        }
361    }
362
363    int parseUserArg(String arg) {
364        int userId;
365        if ("all".equals(arg)) {
366            userId = UserHandle.USER_ALL;
367        } else if ("current".equals(arg) || "cur".equals(arg)) {
368            userId = UserHandle.USER_CURRENT;
369        } else {
370            userId = Integer.parseInt(arg);
371        }
372        return userId;
373    }
374
375    private Intent makeIntent(int defUser) throws URISyntaxException {
376        Intent intent = new Intent();
377        Intent baseIntent = intent;
378        boolean hasIntentInfo = false;
379
380        mStartFlags = 0;
381        mWaitOption = false;
382        mStopOption = false;
383        mRepeat = 0;
384        mProfileFile = null;
385        mSamplingInterval = 0;
386        mAutoStop = false;
387        mUserId = defUser;
388        Uri data = null;
389        String type = null;
390
391        String opt;
392        while ((opt=nextOption()) != null) {
393            if (opt.equals("-a")) {
394                intent.setAction(nextArgRequired());
395                if (intent == baseIntent) {
396                    hasIntentInfo = true;
397                }
398            } else if (opt.equals("-d")) {
399                data = Uri.parse(nextArgRequired());
400                if (intent == baseIntent) {
401                    hasIntentInfo = true;
402                }
403            } else if (opt.equals("-t")) {
404                type = nextArgRequired();
405                if (intent == baseIntent) {
406                    hasIntentInfo = true;
407                }
408            } else if (opt.equals("-c")) {
409                intent.addCategory(nextArgRequired());
410                if (intent == baseIntent) {
411                    hasIntentInfo = true;
412                }
413            } else if (opt.equals("-e") || opt.equals("--es")) {
414                String key = nextArgRequired();
415                String value = nextArgRequired();
416                intent.putExtra(key, value);
417            } else if (opt.equals("--esn")) {
418                String key = nextArgRequired();
419                intent.putExtra(key, (String) null);
420            } else if (opt.equals("--ei")) {
421                String key = nextArgRequired();
422                String value = nextArgRequired();
423                intent.putExtra(key, Integer.decode(value));
424            } else if (opt.equals("--eu")) {
425                String key = nextArgRequired();
426                String value = nextArgRequired();
427                intent.putExtra(key, Uri.parse(value));
428            } else if (opt.equals("--ecn")) {
429                String key = nextArgRequired();
430                String value = nextArgRequired();
431                ComponentName cn = ComponentName.unflattenFromString(value);
432                if (cn == null) throw new IllegalArgumentException("Bad component name: " + value);
433                intent.putExtra(key, cn);
434            } else if (opt.equals("--eia")) {
435                String key = nextArgRequired();
436                String value = nextArgRequired();
437                String[] strings = value.split(",");
438                int[] list = new int[strings.length];
439                for (int i = 0; i < strings.length; i++) {
440                    list[i] = Integer.decode(strings[i]);
441                }
442                intent.putExtra(key, list);
443            } else if (opt.equals("--el")) {
444                String key = nextArgRequired();
445                String value = nextArgRequired();
446                intent.putExtra(key, Long.valueOf(value));
447            } else if (opt.equals("--ela")) {
448                String key = nextArgRequired();
449                String value = nextArgRequired();
450                String[] strings = value.split(",");
451                long[] list = new long[strings.length];
452                for (int i = 0; i < strings.length; i++) {
453                    list[i] = Long.valueOf(strings[i]);
454                }
455                intent.putExtra(key, list);
456                hasIntentInfo = true;
457            } else if (opt.equals("--ef")) {
458                String key = nextArgRequired();
459                String value = nextArgRequired();
460                intent.putExtra(key, Float.valueOf(value));
461                hasIntentInfo = true;
462            } else if (opt.equals("--efa")) {
463                String key = nextArgRequired();
464                String value = nextArgRequired();
465                String[] strings = value.split(",");
466                float[] list = new float[strings.length];
467                for (int i = 0; i < strings.length; i++) {
468                    list[i] = Float.valueOf(strings[i]);
469                }
470                intent.putExtra(key, list);
471                hasIntentInfo = true;
472            } else if (opt.equals("--esa")) {
473                String key = nextArgRequired();
474                String value = nextArgRequired();
475                // Split on commas unless they are preceeded by an escape.
476                // The escape character must be escaped for the string and
477                // again for the regex, thus four escape characters become one.
478                String[] strings = value.split("(?<!\\\\),");
479                intent.putExtra(key, strings);
480                hasIntentInfo = true;
481            } else if (opt.equals("--ez")) {
482                String key = nextArgRequired();
483                String value = nextArgRequired().toLowerCase();
484                // Boolean.valueOf() results in false for anything that is not "true", which is
485                // error-prone in shell commands
486                boolean arg;
487                if ("true".equals(value) || "t".equals(value)) {
488                    arg = true;
489                } else if ("false".equals(value) || "f".equals(value)) {
490                    arg = false;
491                } else {
492                    try {
493                        arg = Integer.decode(value) != 0;
494                    } catch (NumberFormatException ex) {
495                        throw new IllegalArgumentException("Invalid boolean value: " + value);
496                    }
497                }
498
499                intent.putExtra(key, arg);
500            } else if (opt.equals("-n")) {
501                String str = nextArgRequired();
502                ComponentName cn = ComponentName.unflattenFromString(str);
503                if (cn == null) throw new IllegalArgumentException("Bad component name: " + str);
504                intent.setComponent(cn);
505                if (intent == baseIntent) {
506                    hasIntentInfo = true;
507                }
508            } else if (opt.equals("-p")) {
509                String str = nextArgRequired();
510                intent.setPackage(str);
511                if (intent == baseIntent) {
512                    hasIntentInfo = true;
513                }
514            } else if (opt.equals("-f")) {
515                String str = nextArgRequired();
516                intent.setFlags(Integer.decode(str).intValue());
517            } else if (opt.equals("--grant-read-uri-permission")) {
518                intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
519            } else if (opt.equals("--grant-write-uri-permission")) {
520                intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
521            } else if (opt.equals("--grant-persistable-uri-permission")) {
522                intent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
523            } else if (opt.equals("--grant-prefix-uri-permission")) {
524                intent.addFlags(Intent.FLAG_GRANT_PREFIX_URI_PERMISSION);
525            } else if (opt.equals("--exclude-stopped-packages")) {
526                intent.addFlags(Intent.FLAG_EXCLUDE_STOPPED_PACKAGES);
527            } else if (opt.equals("--include-stopped-packages")) {
528                intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
529            } else if (opt.equals("--debug-log-resolution")) {
530                intent.addFlags(Intent.FLAG_DEBUG_LOG_RESOLUTION);
531            } else if (opt.equals("--activity-brought-to-front")) {
532                intent.addFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
533            } else if (opt.equals("--activity-clear-top")) {
534                intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
535            } else if (opt.equals("--activity-clear-when-task-reset")) {
536                intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
537            } else if (opt.equals("--activity-exclude-from-recents")) {
538                intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
539            } else if (opt.equals("--activity-launched-from-history")) {
540                intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY);
541            } else if (opt.equals("--activity-multiple-task")) {
542                intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
543            } else if (opt.equals("--activity-no-animation")) {
544                intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
545            } else if (opt.equals("--activity-no-history")) {
546                intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
547            } else if (opt.equals("--activity-no-user-action")) {
548                intent.addFlags(Intent.FLAG_ACTIVITY_NO_USER_ACTION);
549            } else if (opt.equals("--activity-previous-is-top")) {
550                intent.addFlags(Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP);
551            } else if (opt.equals("--activity-reorder-to-front")) {
552                intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
553            } else if (opt.equals("--activity-reset-task-if-needed")) {
554                intent.addFlags(Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
555            } else if (opt.equals("--activity-single-top")) {
556                intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
557            } else if (opt.equals("--activity-clear-task")) {
558                intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
559            } else if (opt.equals("--activity-task-on-home")) {
560                intent.addFlags(Intent.FLAG_ACTIVITY_TASK_ON_HOME);
561            } else if (opt.equals("--receiver-registered-only")) {
562                intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
563            } else if (opt.equals("--receiver-replace-pending")) {
564                intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
565            } else if (opt.equals("--selector")) {
566                intent.setDataAndType(data, type);
567                intent = new Intent();
568            } else if (opt.equals("-D")) {
569                mStartFlags |= ActivityManager.START_FLAG_DEBUG;
570            } else if (opt.equals("-W")) {
571                mWaitOption = true;
572            } else if (opt.equals("-P")) {
573                mProfileFile = nextArgRequired();
574                mAutoStop = true;
575            } else if (opt.equals("--start-profiler")) {
576                mProfileFile = nextArgRequired();
577                mAutoStop = false;
578            } else if (opt.equals("--sampling")) {
579                mSamplingInterval = Integer.parseInt(nextArgRequired());
580            } else if (opt.equals("-R")) {
581                mRepeat = Integer.parseInt(nextArgRequired());
582            } else if (opt.equals("-S")) {
583                mStopOption = true;
584            } else if (opt.equals("--opengl-trace")) {
585                mStartFlags |= ActivityManager.START_FLAG_OPENGL_TRACES;
586            } else if (opt.equals("--user")) {
587                mUserId = parseUserArg(nextArgRequired());
588            } else if (opt.equals("--receiver-permission")) {
589                mReceiverPermission = nextArgRequired();
590            } else {
591                System.err.println("Error: Unknown option: " + opt);
592                return null;
593            }
594        }
595        intent.setDataAndType(data, type);
596
597        final boolean hasSelector = intent != baseIntent;
598        if (hasSelector) {
599            // A selector was specified; fix up.
600            baseIntent.setSelector(intent);
601            intent = baseIntent;
602        }
603
604        String arg = nextArg();
605        baseIntent = null;
606        if (arg == null) {
607            if (hasSelector) {
608                // If a selector has been specified, and no arguments
609                // have been supplied for the main Intent, then we can
610                // assume it is ACTION_MAIN CATEGORY_LAUNCHER; we don't
611                // need to have a component name specified yet, the
612                // selector will take care of that.
613                baseIntent = new Intent(Intent.ACTION_MAIN);
614                baseIntent.addCategory(Intent.CATEGORY_LAUNCHER);
615            }
616        } else if (arg.indexOf(':') >= 0) {
617            // The argument is a URI.  Fully parse it, and use that result
618            // to fill in any data not specified so far.
619            baseIntent = Intent.parseUri(arg, Intent.URI_INTENT_SCHEME
620                    | Intent.URI_ANDROID_APP_SCHEME | Intent.URI_ALLOW_UNSAFE);
621        } else if (arg.indexOf('/') >= 0) {
622            // The argument is a component name.  Build an Intent to launch
623            // it.
624            baseIntent = new Intent(Intent.ACTION_MAIN);
625            baseIntent.addCategory(Intent.CATEGORY_LAUNCHER);
626            baseIntent.setComponent(ComponentName.unflattenFromString(arg));
627        } else {
628            // Assume the argument is a package name.
629            baseIntent = new Intent(Intent.ACTION_MAIN);
630            baseIntent.addCategory(Intent.CATEGORY_LAUNCHER);
631            baseIntent.setPackage(arg);
632        }
633        if (baseIntent != null) {
634            Bundle extras = intent.getExtras();
635            intent.replaceExtras((Bundle)null);
636            Bundle uriExtras = baseIntent.getExtras();
637            baseIntent.replaceExtras((Bundle)null);
638            if (intent.getAction() != null && baseIntent.getCategories() != null) {
639                HashSet<String> cats = new HashSet<String>(baseIntent.getCategories());
640                for (String c : cats) {
641                    baseIntent.removeCategory(c);
642                }
643            }
644            intent.fillIn(baseIntent, Intent.FILL_IN_COMPONENT | Intent.FILL_IN_SELECTOR);
645            if (extras == null) {
646                extras = uriExtras;
647            } else if (uriExtras != null) {
648                uriExtras.putAll(extras);
649                extras = uriExtras;
650            }
651            intent.replaceExtras(extras);
652            hasIntentInfo = true;
653        }
654
655        if (!hasIntentInfo) throw new IllegalArgumentException("No intent supplied");
656        return intent;
657    }
658
659    private void runStartService() throws Exception {
660        Intent intent = makeIntent(UserHandle.USER_CURRENT);
661        if (mUserId == UserHandle.USER_ALL) {
662            System.err.println("Error: Can't start activity with user 'all'");
663            return;
664        }
665        System.out.println("Starting service: " + intent);
666        ComponentName cn = mAm.startService(null, intent, intent.getType(), mUserId);
667        if (cn == null) {
668            System.err.println("Error: Not found; no service started.");
669        } else if (cn.getPackageName().equals("!")) {
670            System.err.println("Error: Requires permission " + cn.getClassName());
671        } else if (cn.getPackageName().equals("!!")) {
672            System.err.println("Error: " + cn.getClassName());
673        }
674    }
675
676    private void runStopService() throws Exception {
677        Intent intent = makeIntent(UserHandle.USER_CURRENT);
678        if (mUserId == UserHandle.USER_ALL) {
679            System.err.println("Error: Can't stop activity with user 'all'");
680            return;
681        }
682        System.out.println("Stopping service: " + intent);
683        int result = mAm.stopService(null, intent, intent.getType(), mUserId);
684        if (result == 0) {
685            System.err.println("Service not stopped: was not running.");
686        } else if (result == 1) {
687            System.err.println("Service stopped");
688        } else if (result == -1) {
689            System.err.println("Error stopping service");
690        }
691    }
692
693    private void runStart() throws Exception {
694        Intent intent = makeIntent(UserHandle.USER_CURRENT);
695
696        if (mUserId == UserHandle.USER_ALL) {
697            System.err.println("Error: Can't start service with user 'all'");
698            return;
699        }
700
701        String mimeType = intent.getType();
702        if (mimeType == null && intent.getData() != null
703                && "content".equals(intent.getData().getScheme())) {
704            mimeType = mAm.getProviderMimeType(intent.getData(), mUserId);
705        }
706
707        do {
708            if (mStopOption) {
709                String packageName;
710                if (intent.getComponent() != null) {
711                    packageName = intent.getComponent().getPackageName();
712                } else {
713                    IPackageManager pm = IPackageManager.Stub.asInterface(
714                            ServiceManager.getService("package"));
715                    if (pm == null) {
716                        System.err.println("Error: Package manager not running; aborting");
717                        return;
718                    }
719                    List<ResolveInfo> activities = pm.queryIntentActivities(intent, mimeType, 0,
720                            mUserId);
721                    if (activities == null || activities.size() <= 0) {
722                        System.err.println("Error: Intent does not match any activities: "
723                                + intent);
724                        return;
725                    } else if (activities.size() > 1) {
726                        System.err.println("Error: Intent matches multiple activities; can't stop: "
727                                + intent);
728                        return;
729                    }
730                    packageName = activities.get(0).activityInfo.packageName;
731                }
732                System.out.println("Stopping: " + packageName);
733                mAm.forceStopPackage(packageName, mUserId);
734                Thread.sleep(250);
735            }
736
737            System.out.println("Starting: " + intent);
738            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
739
740            ParcelFileDescriptor fd = null;
741            ProfilerInfo profilerInfo = null;
742
743            if (mProfileFile != null) {
744                try {
745                    fd = openForSystemServer(
746                            new File(mProfileFile),
747                            ParcelFileDescriptor.MODE_CREATE |
748                            ParcelFileDescriptor.MODE_TRUNCATE |
749                            ParcelFileDescriptor.MODE_READ_WRITE);
750                } catch (FileNotFoundException e) {
751                    System.err.println("Error: Unable to open file: " + mProfileFile);
752                    System.err.println("Consider using a file under /data/local/tmp/");
753                    return;
754                }
755                profilerInfo = new ProfilerInfo(mProfileFile, fd, mSamplingInterval, mAutoStop);
756            }
757
758            IActivityManager.WaitResult result = null;
759            int res;
760            final long startTime = SystemClock.uptimeMillis();
761            if (mWaitOption) {
762                result = mAm.startActivityAndWait(null, null, intent, mimeType,
763                            null, null, 0, mStartFlags, profilerInfo, null, mUserId);
764                res = result.result;
765            } else {
766                res = mAm.startActivityAsUser(null, null, intent, mimeType,
767                        null, null, 0, mStartFlags, profilerInfo, null, mUserId);
768            }
769            final long endTime = SystemClock.uptimeMillis();
770            PrintStream out = mWaitOption ? System.out : System.err;
771            boolean launched = false;
772            switch (res) {
773                case ActivityManager.START_SUCCESS:
774                    launched = true;
775                    break;
776                case ActivityManager.START_SWITCHES_CANCELED:
777                    launched = true;
778                    out.println(
779                            "Warning: Activity not started because the "
780                            + " current activity is being kept for the user.");
781                    break;
782                case ActivityManager.START_DELIVERED_TO_TOP:
783                    launched = true;
784                    out.println(
785                            "Warning: Activity not started, intent has "
786                            + "been delivered to currently running "
787                            + "top-most instance.");
788                    break;
789                case ActivityManager.START_RETURN_INTENT_TO_CALLER:
790                    launched = true;
791                    out.println(
792                            "Warning: Activity not started because intent "
793                            + "should be handled by the caller");
794                    break;
795                case ActivityManager.START_TASK_TO_FRONT:
796                    launched = true;
797                    out.println(
798                            "Warning: Activity not started, its current "
799                            + "task has been brought to the front");
800                    break;
801                case ActivityManager.START_INTENT_NOT_RESOLVED:
802                    out.println(
803                            "Error: Activity not started, unable to "
804                            + "resolve " + intent.toString());
805                    break;
806                case ActivityManager.START_CLASS_NOT_FOUND:
807                    out.println(NO_CLASS_ERROR_CODE);
808                    out.println("Error: Activity class " +
809                            intent.getComponent().toShortString()
810                            + " does not exist.");
811                    break;
812                case ActivityManager.START_FORWARD_AND_REQUEST_CONFLICT:
813                    out.println(
814                            "Error: Activity not started, you requested to "
815                            + "both forward and receive its result");
816                    break;
817                case ActivityManager.START_PERMISSION_DENIED:
818                    out.println(
819                            "Error: Activity not started, you do not "
820                            + "have permission to access it.");
821                    break;
822                case ActivityManager.START_NOT_VOICE_COMPATIBLE:
823                    out.println(
824                            "Error: Activity not started, voice control not allowed for: "
825                                    + intent);
826                    break;
827                default:
828                    out.println(
829                            "Error: Activity not started, unknown error code " + res);
830                    break;
831            }
832            if (mWaitOption && launched) {
833                if (result == null) {
834                    result = new IActivityManager.WaitResult();
835                    result.who = intent.getComponent();
836                }
837                System.out.println("Status: " + (result.timeout ? "timeout" : "ok"));
838                if (result.who != null) {
839                    System.out.println("Activity: " + result.who.flattenToShortString());
840                }
841                if (result.thisTime >= 0) {
842                    System.out.println("ThisTime: " + result.thisTime);
843                }
844                if (result.totalTime >= 0) {
845                    System.out.println("TotalTime: " + result.totalTime);
846                }
847                System.out.println("WaitTime: " + (endTime-startTime));
848                System.out.println("Complete");
849            }
850            mRepeat--;
851            if (mRepeat > 1) {
852                mAm.unhandledBack();
853            }
854        } while (mRepeat > 1);
855    }
856
857    private void runForceStop() throws Exception {
858        int userId = UserHandle.USER_ALL;
859
860        String opt;
861        while ((opt=nextOption()) != null) {
862            if (opt.equals("--user")) {
863                userId = parseUserArg(nextArgRequired());
864            } else {
865                System.err.println("Error: Unknown option: " + opt);
866                return;
867            }
868        }
869        mAm.forceStopPackage(nextArgRequired(), userId);
870    }
871
872    private void runKill() throws Exception {
873        int userId = UserHandle.USER_ALL;
874
875        String opt;
876        while ((opt=nextOption()) != null) {
877            if (opt.equals("--user")) {
878                userId = parseUserArg(nextArgRequired());
879            } else {
880                System.err.println("Error: Unknown option: " + opt);
881                return;
882            }
883        }
884        mAm.killBackgroundProcesses(nextArgRequired(), userId);
885    }
886
887    private void runKillAll() throws Exception {
888        mAm.killAllBackgroundProcesses();
889    }
890
891    private void sendBroadcast() throws Exception {
892        Intent intent = makeIntent(UserHandle.USER_CURRENT);
893        IntentReceiver receiver = new IntentReceiver();
894        System.out.println("Broadcasting: " + intent);
895        mAm.broadcastIntent(null, intent, null, receiver, 0, null, null, mReceiverPermission,
896                android.app.AppOpsManager.OP_NONE, true, false, mUserId);
897        receiver.waitForFinish();
898    }
899
900    private void runInstrument() throws Exception {
901        String profileFile = null;
902        boolean wait = false;
903        boolean rawMode = false;
904        boolean no_window_animation = false;
905        int userId = UserHandle.USER_CURRENT;
906        Bundle args = new Bundle();
907        String argKey = null, argValue = null;
908        IWindowManager wm = IWindowManager.Stub.asInterface(ServiceManager.getService("window"));
909        String abi = null;
910
911        String opt;
912        while ((opt=nextOption()) != null) {
913            if (opt.equals("-p")) {
914                profileFile = nextArgRequired();
915            } else if (opt.equals("-w")) {
916                wait = true;
917            } else if (opt.equals("-r")) {
918                rawMode = true;
919            } else if (opt.equals("-e")) {
920                argKey = nextArgRequired();
921                argValue = nextArgRequired();
922                args.putString(argKey, argValue);
923            } else if (opt.equals("--no_window_animation")
924                    || opt.equals("--no-window-animation")) {
925                no_window_animation = true;
926            } else if (opt.equals("--user")) {
927                userId = parseUserArg(nextArgRequired());
928            } else if (opt.equals("--abi")) {
929                abi = nextArgRequired();
930            } else {
931                System.err.println("Error: Unknown option: " + opt);
932                return;
933            }
934        }
935
936        if (userId == UserHandle.USER_ALL) {
937            System.err.println("Error: Can't start instrumentation with user 'all'");
938            return;
939        }
940
941        String cnArg = nextArgRequired();
942        ComponentName cn = ComponentName.unflattenFromString(cnArg);
943        if (cn == null) throw new IllegalArgumentException("Bad component name: " + cnArg);
944
945        InstrumentationWatcher watcher = null;
946        UiAutomationConnection connection = null;
947        if (wait) {
948            watcher = new InstrumentationWatcher();
949            watcher.setRawOutput(rawMode);
950            connection = new UiAutomationConnection();
951        }
952
953        float[] oldAnims = null;
954        if (no_window_animation) {
955            oldAnims = wm.getAnimationScales();
956            wm.setAnimationScale(0, 0.0f);
957            wm.setAnimationScale(1, 0.0f);
958        }
959
960        if (abi != null) {
961            final String[] supportedAbis = Build.SUPPORTED_ABIS;
962            boolean matched = false;
963            for (String supportedAbi : supportedAbis) {
964                if (supportedAbi.equals(abi)) {
965                    matched = true;
966                    break;
967                }
968            }
969
970            if (!matched) {
971                throw new AndroidException(
972                        "INSTRUMENTATION_FAILED: Unsupported instruction set " + abi);
973            }
974        }
975
976        if (!mAm.startInstrumentation(cn, profileFile, 0, args, watcher, connection, userId, abi)) {
977            throw new AndroidException("INSTRUMENTATION_FAILED: " + cn.flattenToString());
978        }
979
980        if (watcher != null) {
981            if (!watcher.waitForFinish()) {
982                System.out.println("INSTRUMENTATION_ABORTED: System has crashed.");
983            }
984        }
985
986        if (oldAnims != null) {
987            wm.setAnimationScales(oldAnims);
988        }
989    }
990
991    static void removeWallOption() {
992        String props = SystemProperties.get("dalvik.vm.extra-opts");
993        if (props != null && props.contains("-Xprofile:wallclock")) {
994            props = props.replace("-Xprofile:wallclock", "");
995            props = props.trim();
996            SystemProperties.set("dalvik.vm.extra-opts", props);
997        }
998    }
999
1000    private void runProfile() throws Exception {
1001        String profileFile = null;
1002        boolean start = false;
1003        boolean wall = false;
1004        int userId = UserHandle.USER_CURRENT;
1005        int profileType = 0;
1006
1007        String process = null;
1008
1009        String cmd = nextArgRequired();
1010
1011        if ("start".equals(cmd)) {
1012            start = true;
1013            String opt;
1014            while ((opt=nextOption()) != null) {
1015                if (opt.equals("--user")) {
1016                    userId = parseUserArg(nextArgRequired());
1017                } else if (opt.equals("--wall")) {
1018                    wall = true;
1019                } else {
1020                    System.err.println("Error: Unknown option: " + opt);
1021                    return;
1022                }
1023            }
1024            process = nextArgRequired();
1025        } else if ("stop".equals(cmd)) {
1026            String opt;
1027            while ((opt=nextOption()) != null) {
1028                if (opt.equals("--user")) {
1029                    userId = parseUserArg(nextArgRequired());
1030                } else {
1031                    System.err.println("Error: Unknown option: " + opt);
1032                    return;
1033                }
1034            }
1035            process = nextArg();
1036        } else {
1037            // Compatibility with old syntax: process is specified first.
1038            process = cmd;
1039            cmd = nextArgRequired();
1040            if ("start".equals(cmd)) {
1041                start = true;
1042            } else if (!"stop".equals(cmd)) {
1043                throw new IllegalArgumentException("Profile command " + process + " not valid");
1044            }
1045        }
1046
1047        if (userId == UserHandle.USER_ALL) {
1048            System.err.println("Error: Can't profile with user 'all'");
1049            return;
1050        }
1051
1052        ParcelFileDescriptor fd = null;
1053        ProfilerInfo profilerInfo = null;
1054
1055        if (start) {
1056            profileFile = nextArgRequired();
1057            try {
1058                fd = openForSystemServer(
1059                        new File(profileFile),
1060                        ParcelFileDescriptor.MODE_CREATE |
1061                        ParcelFileDescriptor.MODE_TRUNCATE |
1062                        ParcelFileDescriptor.MODE_READ_WRITE);
1063            } catch (FileNotFoundException e) {
1064                System.err.println("Error: Unable to open file: " + profileFile);
1065                System.err.println("Consider using a file under /data/local/tmp/");
1066                return;
1067            }
1068            profilerInfo = new ProfilerInfo(profileFile, fd, 0, false);
1069        }
1070
1071        try {
1072            if (wall) {
1073                // XXX doesn't work -- this needs to be set before booting.
1074                String props = SystemProperties.get("dalvik.vm.extra-opts");
1075                if (props == null || !props.contains("-Xprofile:wallclock")) {
1076                    props = props + " -Xprofile:wallclock";
1077                    //SystemProperties.set("dalvik.vm.extra-opts", props);
1078                }
1079            } else if (start) {
1080                //removeWallOption();
1081            }
1082            if (!mAm.profileControl(process, userId, start, profilerInfo, profileType)) {
1083                wall = false;
1084                throw new AndroidException("PROFILE FAILED on process " + process);
1085            }
1086        } finally {
1087            if (!wall) {
1088                //removeWallOption();
1089            }
1090        }
1091    }
1092
1093    private void runDumpHeap() throws Exception {
1094        boolean managed = true;
1095        int userId = UserHandle.USER_CURRENT;
1096
1097        String opt;
1098        while ((opt=nextOption()) != null) {
1099            if (opt.equals("--user")) {
1100                userId = parseUserArg(nextArgRequired());
1101                if (userId == UserHandle.USER_ALL) {
1102                    System.err.println("Error: Can't dump heap with user 'all'");
1103                    return;
1104                }
1105            } else if (opt.equals("-n")) {
1106                managed = false;
1107            } else {
1108                System.err.println("Error: Unknown option: " + opt);
1109                return;
1110            }
1111        }
1112        String process = nextArgRequired();
1113        String heapFile = nextArgRequired();
1114        ParcelFileDescriptor fd = null;
1115
1116        try {
1117            File file = new File(heapFile);
1118            file.delete();
1119            fd = openForSystemServer(file,
1120                    ParcelFileDescriptor.MODE_CREATE |
1121                    ParcelFileDescriptor.MODE_TRUNCATE |
1122                    ParcelFileDescriptor.MODE_READ_WRITE);
1123        } catch (FileNotFoundException e) {
1124            System.err.println("Error: Unable to open file: " + heapFile);
1125            System.err.println("Consider using a file under /data/local/tmp/");
1126            return;
1127        }
1128
1129        if (!mAm.dumpHeap(process, userId, managed, heapFile, fd)) {
1130            throw new AndroidException("HEAP DUMP FAILED on process " + process);
1131        }
1132    }
1133
1134    private void runSetDebugApp() throws Exception {
1135        boolean wait = false;
1136        boolean persistent = false;
1137
1138        String opt;
1139        while ((opt=nextOption()) != null) {
1140            if (opt.equals("-w")) {
1141                wait = true;
1142            } else if (opt.equals("--persistent")) {
1143                persistent = true;
1144            } else {
1145                System.err.println("Error: Unknown option: " + opt);
1146                return;
1147            }
1148        }
1149
1150        String pkg = nextArgRequired();
1151        mAm.setDebugApp(pkg, wait, persistent);
1152    }
1153
1154    private void runClearDebugApp() throws Exception {
1155        mAm.setDebugApp(null, false, true);
1156    }
1157
1158    private void runBugReport() throws Exception {
1159        mAm.requestBugReport();
1160        System.out.println("Your lovely bug report is being created; please be patient.");
1161    }
1162
1163    private void runSwitchUser() throws Exception {
1164        String user = nextArgRequired();
1165        mAm.switchUser(Integer.parseInt(user));
1166    }
1167
1168    private void runStartUserInBackground() throws Exception {
1169        String user = nextArgRequired();
1170        boolean success = mAm.startUserInBackground(Integer.parseInt(user));
1171        if (success) {
1172            System.out.println("Success: user started");
1173        } else {
1174            System.err.println("Error: could not start user");
1175        }
1176    }
1177
1178    private void runStopUser() throws Exception {
1179        String user = nextArgRequired();
1180        int res = mAm.stopUser(Integer.parseInt(user), null);
1181        if (res != ActivityManager.USER_OP_SUCCESS) {
1182            String txt = "";
1183            switch (res) {
1184                case ActivityManager.USER_OP_IS_CURRENT:
1185                    txt = " (Can't stop current user)";
1186                    break;
1187                case ActivityManager.USER_OP_UNKNOWN_USER:
1188                    txt = " (Unknown user " + user + ")";
1189                    break;
1190            }
1191            System.err.println("Switch failed: " + res + txt);
1192        }
1193    }
1194
1195    class MyActivityController extends IActivityController.Stub {
1196        final String mGdbPort;
1197
1198        static final int STATE_NORMAL = 0;
1199        static final int STATE_CRASHED = 1;
1200        static final int STATE_EARLY_ANR = 2;
1201        static final int STATE_ANR = 3;
1202
1203        int mState;
1204
1205        static final int RESULT_DEFAULT = 0;
1206
1207        static final int RESULT_CRASH_DIALOG = 0;
1208        static final int RESULT_CRASH_KILL = 1;
1209
1210        static final int RESULT_EARLY_ANR_CONTINUE = 0;
1211        static final int RESULT_EARLY_ANR_KILL = 1;
1212
1213        static final int RESULT_ANR_DIALOG = 0;
1214        static final int RESULT_ANR_KILL = 1;
1215        static final int RESULT_ANR_WAIT = 1;
1216
1217        int mResult;
1218
1219        Process mGdbProcess;
1220        Thread mGdbThread;
1221        boolean mGotGdbPrint;
1222
1223        MyActivityController(String gdbPort) {
1224            mGdbPort = gdbPort;
1225        }
1226
1227        @Override
1228        public boolean activityResuming(String pkg) {
1229            synchronized (this) {
1230                System.out.println("** Activity resuming: " + pkg);
1231            }
1232            return true;
1233        }
1234
1235        @Override
1236        public boolean activityStarting(Intent intent, String pkg) {
1237            synchronized (this) {
1238                System.out.println("** Activity starting: " + pkg);
1239            }
1240            return true;
1241        }
1242
1243        @Override
1244        public boolean appCrashed(String processName, int pid, String shortMsg, String longMsg,
1245                long timeMillis, String stackTrace) {
1246            synchronized (this) {
1247                System.out.println("** ERROR: PROCESS CRASHED");
1248                System.out.println("processName: " + processName);
1249                System.out.println("processPid: " + pid);
1250                System.out.println("shortMsg: " + shortMsg);
1251                System.out.println("longMsg: " + longMsg);
1252                System.out.println("timeMillis: " + timeMillis);
1253                System.out.println("stack:");
1254                System.out.print(stackTrace);
1255                System.out.println("#");
1256                int result = waitControllerLocked(pid, STATE_CRASHED);
1257                return result == RESULT_CRASH_KILL ? false : true;
1258            }
1259        }
1260
1261        @Override
1262        public int appEarlyNotResponding(String processName, int pid, String annotation) {
1263            synchronized (this) {
1264                System.out.println("** ERROR: EARLY PROCESS NOT RESPONDING");
1265                System.out.println("processName: " + processName);
1266                System.out.println("processPid: " + pid);
1267                System.out.println("annotation: " + annotation);
1268                int result = waitControllerLocked(pid, STATE_EARLY_ANR);
1269                if (result == RESULT_EARLY_ANR_KILL) return -1;
1270                return 0;
1271            }
1272        }
1273
1274        @Override
1275        public int appNotResponding(String processName, int pid, String processStats) {
1276            synchronized (this) {
1277                System.out.println("** ERROR: PROCESS NOT RESPONDING");
1278                System.out.println("processName: " + processName);
1279                System.out.println("processPid: " + pid);
1280                System.out.println("processStats:");
1281                System.out.print(processStats);
1282                System.out.println("#");
1283                int result = waitControllerLocked(pid, STATE_ANR);
1284                if (result == RESULT_ANR_KILL) return -1;
1285                if (result == RESULT_ANR_WAIT) return 1;
1286                return 0;
1287            }
1288        }
1289
1290        @Override
1291        public int systemNotResponding(String message) {
1292            synchronized (this) {
1293                System.out.println("** ERROR: PROCESS NOT RESPONDING");
1294                System.out.println("message: " + message);
1295                System.out.println("#");
1296                System.out.println("Allowing system to die.");
1297                return -1;
1298            }
1299        }
1300
1301        void killGdbLocked() {
1302            mGotGdbPrint = false;
1303            if (mGdbProcess != null) {
1304                System.out.println("Stopping gdbserver");
1305                mGdbProcess.destroy();
1306                mGdbProcess = null;
1307            }
1308            if (mGdbThread != null) {
1309                mGdbThread.interrupt();
1310                mGdbThread = null;
1311            }
1312        }
1313
1314        int waitControllerLocked(int pid, int state) {
1315            if (mGdbPort != null) {
1316                killGdbLocked();
1317
1318                try {
1319                    System.out.println("Starting gdbserver on port " + mGdbPort);
1320                    System.out.println("Do the following:");
1321                    System.out.println("  adb forward tcp:" + mGdbPort + " tcp:" + mGdbPort);
1322                    System.out.println("  gdbclient app_process :" + mGdbPort);
1323
1324                    mGdbProcess = Runtime.getRuntime().exec(new String[] {
1325                            "gdbserver", ":" + mGdbPort, "--attach", Integer.toString(pid)
1326                    });
1327                    final InputStreamReader converter = new InputStreamReader(
1328                            mGdbProcess.getInputStream());
1329                    mGdbThread = new Thread() {
1330                        @Override
1331                        public void run() {
1332                            BufferedReader in = new BufferedReader(converter);
1333                            String line;
1334                            int count = 0;
1335                            while (true) {
1336                                synchronized (MyActivityController.this) {
1337                                    if (mGdbThread == null) {
1338                                        return;
1339                                    }
1340                                    if (count == 2) {
1341                                        mGotGdbPrint = true;
1342                                        MyActivityController.this.notifyAll();
1343                                    }
1344                                }
1345                                try {
1346                                    line = in.readLine();
1347                                    if (line == null) {
1348                                        return;
1349                                    }
1350                                    System.out.println("GDB: " + line);
1351                                    count++;
1352                                } catch (IOException e) {
1353                                    return;
1354                                }
1355                            }
1356                        }
1357                    };
1358                    mGdbThread.start();
1359
1360                    // Stupid waiting for .5s.  Doesn't matter if we end early.
1361                    try {
1362                        this.wait(500);
1363                    } catch (InterruptedException e) {
1364                    }
1365
1366                } catch (IOException e) {
1367                    System.err.println("Failure starting gdbserver: " + e);
1368                    killGdbLocked();
1369                }
1370            }
1371            mState = state;
1372            System.out.println("");
1373            printMessageForState();
1374
1375            while (mState != STATE_NORMAL) {
1376                try {
1377                    wait();
1378                } catch (InterruptedException e) {
1379                }
1380            }
1381
1382            killGdbLocked();
1383
1384            return mResult;
1385        }
1386
1387        void resumeController(int result) {
1388            synchronized (this) {
1389                mState = STATE_NORMAL;
1390                mResult = result;
1391                notifyAll();
1392            }
1393        }
1394
1395        void printMessageForState() {
1396            switch (mState) {
1397                case STATE_NORMAL:
1398                    System.out.println("Monitoring activity manager...  available commands:");
1399                    break;
1400                case STATE_CRASHED:
1401                    System.out.println("Waiting after crash...  available commands:");
1402                    System.out.println("(c)ontinue: show crash dialog");
1403                    System.out.println("(k)ill: immediately kill app");
1404                    break;
1405                case STATE_EARLY_ANR:
1406                    System.out.println("Waiting after early ANR...  available commands:");
1407                    System.out.println("(c)ontinue: standard ANR processing");
1408                    System.out.println("(k)ill: immediately kill app");
1409                    break;
1410                case STATE_ANR:
1411                    System.out.println("Waiting after ANR...  available commands:");
1412                    System.out.println("(c)ontinue: show ANR dialog");
1413                    System.out.println("(k)ill: immediately kill app");
1414                    System.out.println("(w)ait: wait some more");
1415                    break;
1416            }
1417            System.out.println("(q)uit: finish monitoring");
1418        }
1419
1420        void run() throws RemoteException {
1421            try {
1422                printMessageForState();
1423
1424                mAm.setActivityController(this);
1425                mState = STATE_NORMAL;
1426
1427                InputStreamReader converter = new InputStreamReader(System.in);
1428                BufferedReader in = new BufferedReader(converter);
1429                String line;
1430
1431                while ((line = in.readLine()) != null) {
1432                    boolean addNewline = true;
1433                    if (line.length() <= 0) {
1434                        addNewline = false;
1435                    } else if ("q".equals(line) || "quit".equals(line)) {
1436                        resumeController(RESULT_DEFAULT);
1437                        break;
1438                    } else if (mState == STATE_CRASHED) {
1439                        if ("c".equals(line) || "continue".equals(line)) {
1440                            resumeController(RESULT_CRASH_DIALOG);
1441                        } else if ("k".equals(line) || "kill".equals(line)) {
1442                            resumeController(RESULT_CRASH_KILL);
1443                        } else {
1444                            System.out.println("Invalid command: " + line);
1445                        }
1446                    } else if (mState == STATE_ANR) {
1447                        if ("c".equals(line) || "continue".equals(line)) {
1448                            resumeController(RESULT_ANR_DIALOG);
1449                        } else if ("k".equals(line) || "kill".equals(line)) {
1450                            resumeController(RESULT_ANR_KILL);
1451                        } else if ("w".equals(line) || "wait".equals(line)) {
1452                            resumeController(RESULT_ANR_WAIT);
1453                        } else {
1454                            System.out.println("Invalid command: " + line);
1455                        }
1456                    } else if (mState == STATE_EARLY_ANR) {
1457                        if ("c".equals(line) || "continue".equals(line)) {
1458                            resumeController(RESULT_EARLY_ANR_CONTINUE);
1459                        } else if ("k".equals(line) || "kill".equals(line)) {
1460                            resumeController(RESULT_EARLY_ANR_KILL);
1461                        } else {
1462                            System.out.println("Invalid command: " + line);
1463                        }
1464                    } else {
1465                        System.out.println("Invalid command: " + line);
1466                    }
1467
1468                    synchronized (this) {
1469                        if (addNewline) {
1470                            System.out.println("");
1471                        }
1472                        printMessageForState();
1473                    }
1474                }
1475
1476            } catch (IOException e) {
1477                e.printStackTrace();
1478            } finally {
1479                mAm.setActivityController(null);
1480            }
1481        }
1482    }
1483
1484    private void runMonitor() throws Exception {
1485        String opt;
1486        String gdbPort = null;
1487        while ((opt=nextOption()) != null) {
1488            if (opt.equals("--gdb")) {
1489                gdbPort = nextArgRequired();
1490            } else {
1491                System.err.println("Error: Unknown option: " + opt);
1492                return;
1493            }
1494        }
1495
1496        MyActivityController controller = new MyActivityController(gdbPort);
1497        controller.run();
1498    }
1499
1500    private void runHang() throws Exception {
1501        String opt;
1502        boolean allowRestart = false;
1503        while ((opt=nextOption()) != null) {
1504            if (opt.equals("--allow-restart")) {
1505                allowRestart = true;
1506            } else {
1507                System.err.println("Error: Unknown option: " + opt);
1508                return;
1509            }
1510        }
1511
1512        System.out.println("Hanging the system...");
1513        mAm.hang(new Binder(), allowRestart);
1514    }
1515
1516    private void runRestart() throws Exception {
1517        String opt;
1518        while ((opt=nextOption()) != null) {
1519            System.err.println("Error: Unknown option: " + opt);
1520            return;
1521        }
1522
1523        System.out.println("Restart the system...");
1524        mAm.restart();
1525    }
1526
1527    private void runIdleMaintenance() throws Exception {
1528        String opt;
1529        while ((opt=nextOption()) != null) {
1530            System.err.println("Error: Unknown option: " + opt);
1531            return;
1532        }
1533
1534        System.out.println("Performing idle maintenance...");
1535        Intent intent = new Intent(
1536                "com.android.server.task.controllers.IdleController.ACTION_TRIGGER_IDLE");
1537        mAm.broadcastIntent(null, intent, null, null, 0, null, null, null,
1538                android.app.AppOpsManager.OP_NONE, true, false, UserHandle.USER_ALL);
1539    }
1540
1541    private void runScreenCompat() throws Exception {
1542        String mode = nextArgRequired();
1543        boolean enabled;
1544        if ("on".equals(mode)) {
1545            enabled = true;
1546        } else if ("off".equals(mode)) {
1547            enabled = false;
1548        } else {
1549            System.err.println("Error: enabled mode must be 'on' or 'off' at " + mode);
1550            return;
1551        }
1552
1553        String packageName = nextArgRequired();
1554        do {
1555            try {
1556                mAm.setPackageScreenCompatMode(packageName, enabled
1557                        ? ActivityManager.COMPAT_MODE_ENABLED
1558                        : ActivityManager.COMPAT_MODE_DISABLED);
1559            } catch (RemoteException e) {
1560            }
1561            packageName = nextArg();
1562        } while (packageName != null);
1563    }
1564
1565    private void runToUri(int flags) throws Exception {
1566        Intent intent = makeIntent(UserHandle.USER_CURRENT);
1567        System.out.println(intent.toUri(flags));
1568    }
1569
1570    private class IntentReceiver extends IIntentReceiver.Stub {
1571        private boolean mFinished = false;
1572
1573        @Override
1574        public void performReceive(Intent intent, int resultCode, String data, Bundle extras,
1575                boolean ordered, boolean sticky, int sendingUser) {
1576            String line = "Broadcast completed: result=" + resultCode;
1577            if (data != null) line = line + ", data=\"" + data + "\"";
1578            if (extras != null) line = line + ", extras: " + extras;
1579            System.out.println(line);
1580            synchronized (this) {
1581              mFinished = true;
1582              notifyAll();
1583            }
1584        }
1585
1586        public synchronized void waitForFinish() {
1587            try {
1588                while (!mFinished) wait();
1589            } catch (InterruptedException e) {
1590                throw new IllegalStateException(e);
1591            }
1592        }
1593    }
1594
1595    private class InstrumentationWatcher extends IInstrumentationWatcher.Stub {
1596        private boolean mFinished = false;
1597        private boolean mRawMode = false;
1598
1599        /**
1600         * Set or reset "raw mode".  In "raw mode", all bundles are dumped.  In "pretty mode",
1601         * if a bundle includes Instrumentation.REPORT_KEY_STREAMRESULT, just print that.
1602         * @param rawMode true for raw mode, false for pretty mode.
1603         */
1604        public void setRawOutput(boolean rawMode) {
1605            mRawMode = rawMode;
1606        }
1607
1608        @Override
1609        public void instrumentationStatus(ComponentName name, int resultCode, Bundle results) {
1610            synchronized (this) {
1611                // pretty printer mode?
1612                String pretty = null;
1613                if (!mRawMode && results != null) {
1614                    pretty = results.getString(Instrumentation.REPORT_KEY_STREAMRESULT);
1615                }
1616                if (pretty != null) {
1617                    System.out.print(pretty);
1618                } else {
1619                    if (results != null) {
1620                        for (String key : results.keySet()) {
1621                            System.out.println(
1622                                    "INSTRUMENTATION_STATUS: " + key + "=" + results.get(key));
1623                        }
1624                    }
1625                    System.out.println("INSTRUMENTATION_STATUS_CODE: " + resultCode);
1626                }
1627                notifyAll();
1628            }
1629        }
1630
1631        @Override
1632        public void instrumentationFinished(ComponentName name, int resultCode,
1633                Bundle results) {
1634            synchronized (this) {
1635                // pretty printer mode?
1636                String pretty = null;
1637                if (!mRawMode && results != null) {
1638                    pretty = results.getString(Instrumentation.REPORT_KEY_STREAMRESULT);
1639                }
1640                if (pretty != null) {
1641                    System.out.println(pretty);
1642                } else {
1643                    if (results != null) {
1644                        for (String key : results.keySet()) {
1645                            System.out.println(
1646                                    "INSTRUMENTATION_RESULT: " + key + "=" + results.get(key));
1647                        }
1648                    }
1649                    System.out.println("INSTRUMENTATION_CODE: " + resultCode);
1650                }
1651                mFinished = true;
1652                notifyAll();
1653            }
1654        }
1655
1656        public boolean waitForFinish() {
1657            synchronized (this) {
1658                while (!mFinished) {
1659                    try {
1660                        if (!mAm.asBinder().pingBinder()) {
1661                            return false;
1662                        }
1663                        wait(1000);
1664                    } catch (InterruptedException e) {
1665                        throw new IllegalStateException(e);
1666                    }
1667                }
1668            }
1669            return true;
1670        }
1671    }
1672
1673    private void runStack() throws Exception {
1674        String op = nextArgRequired();
1675        if (op.equals("start")) {
1676            runStackStart();
1677        } else if (op.equals("movetask")) {
1678            runStackMoveTask();
1679        } else if (op.equals("resize")) {
1680            runStackResize();
1681        } else if (op.equals("list")) {
1682            runStackList();
1683        } else if (op.equals("info")) {
1684            runStackInfo();
1685        } else {
1686            showError("Error: unknown command '" + op + "'");
1687            return;
1688        }
1689    }
1690
1691    private void runStackStart() throws Exception {
1692        String displayIdStr = nextArgRequired();
1693        int displayId = Integer.valueOf(displayIdStr);
1694        Intent intent = makeIntent(UserHandle.USER_CURRENT);
1695
1696        try {
1697            IBinder homeActivityToken = mAm.getHomeActivityToken();
1698            IActivityContainer container = mAm.createActivityContainer(homeActivityToken, null);
1699            container.attachToDisplay(displayId);
1700            container.startActivity(intent);
1701        } catch (RemoteException e) {
1702        }
1703    }
1704
1705    private void runStackMoveTask() throws Exception {
1706        String taskIdStr = nextArgRequired();
1707        int taskId = Integer.valueOf(taskIdStr);
1708        String stackIdStr = nextArgRequired();
1709        int stackId = Integer.valueOf(stackIdStr);
1710        String toTopStr = nextArgRequired();
1711        final boolean toTop;
1712        if ("true".equals(toTopStr)) {
1713            toTop = true;
1714        } else if ("false".equals(toTopStr)) {
1715            toTop = false;
1716        } else {
1717            System.err.println("Error: bad toTop arg: " + toTopStr);
1718            return;
1719        }
1720
1721        try {
1722            mAm.moveTaskToStack(taskId, stackId, toTop);
1723        } catch (RemoteException e) {
1724        }
1725    }
1726
1727    private void runStackResize() throws Exception {
1728        String stackIdStr = nextArgRequired();
1729        int stackId = Integer.valueOf(stackIdStr);
1730        String leftStr = nextArgRequired();
1731        int left = Integer.valueOf(leftStr);
1732        String topStr = nextArgRequired();
1733        int top = Integer.valueOf(topStr);
1734        String rightStr = nextArgRequired();
1735        int right = Integer.valueOf(rightStr);
1736        String bottomStr = nextArgRequired();
1737        int bottom = Integer.valueOf(bottomStr);
1738
1739        try {
1740            mAm.resizeStack(stackId, new Rect(left, top, right, bottom));
1741        } catch (RemoteException e) {
1742        }
1743    }
1744
1745    private void runStackList() throws Exception {
1746        try {
1747            List<StackInfo> stacks = mAm.getAllStackInfos();
1748            for (StackInfo info : stacks) {
1749                System.out.println(info);
1750            }
1751        } catch (RemoteException e) {
1752        }
1753    }
1754
1755    private void runStackInfo() throws Exception {
1756        try {
1757            String stackIdStr = nextArgRequired();
1758            int stackId = Integer.valueOf(stackIdStr);
1759            StackInfo info = mAm.getStackInfo(stackId);
1760            System.out.println(info);
1761        } catch (RemoteException e) {
1762        }
1763    }
1764
1765    private void runLockTask() throws Exception {
1766        String taskIdStr = nextArgRequired();
1767        try {
1768            if (taskIdStr.equals("stop")) {
1769                mAm.stopLockTaskMode();
1770            } else {
1771                int taskId = Integer.valueOf(taskIdStr);
1772                mAm.startLockTaskMode(taskId);
1773            }
1774            System.err.println("Activity manager is " + (mAm.isInLockTaskMode() ? "" : "not ") +
1775                    "in lockTaskMode");
1776        } catch (RemoteException e) {
1777        }
1778    }
1779
1780    private List<Configuration> getRecentConfigurations(int days) {
1781        IUsageStatsManager usm = IUsageStatsManager.Stub.asInterface(ServiceManager.getService(
1782                    Context.USAGE_STATS_SERVICE));
1783        final long now = System.currentTimeMillis();
1784        final long nDaysAgo = now - (days * 24 * 60 * 60 * 1000);
1785        try {
1786            @SuppressWarnings("unchecked")
1787            ParceledListSlice<ConfigurationStats> configStatsSlice = usm.queryConfigurationStats(
1788                    UsageStatsManager.INTERVAL_BEST, nDaysAgo, now, "com.android.shell");
1789            if (configStatsSlice == null) {
1790                return Collections.emptyList();
1791            }
1792
1793            final ArrayMap<Configuration, Integer> recentConfigs = new ArrayMap<>();
1794            final List<ConfigurationStats> configStatsList = configStatsSlice.getList();
1795            final int configStatsListSize = configStatsList.size();
1796            for (int i = 0; i < configStatsListSize; i++) {
1797                final ConfigurationStats stats = configStatsList.get(i);
1798                final int indexOfKey = recentConfigs.indexOfKey(stats.getConfiguration());
1799                if (indexOfKey < 0) {
1800                    recentConfigs.put(stats.getConfiguration(), stats.getActivationCount());
1801                } else {
1802                    recentConfigs.setValueAt(indexOfKey,
1803                            recentConfigs.valueAt(indexOfKey) + stats.getActivationCount());
1804                }
1805            }
1806
1807            final Comparator<Configuration> comparator = new Comparator<Configuration>() {
1808                @Override
1809                public int compare(Configuration a, Configuration b) {
1810                    return recentConfigs.get(b).compareTo(recentConfigs.get(a));
1811                }
1812            };
1813
1814            ArrayList<Configuration> configs = new ArrayList<>(recentConfigs.size());
1815            configs.addAll(recentConfigs.keySet());
1816            Collections.sort(configs, comparator);
1817            return configs;
1818
1819        } catch (RemoteException e) {
1820            return Collections.emptyList();
1821        }
1822    }
1823
1824    private void runGetConfig() throws Exception {
1825        int days = 14;
1826        String option = nextOption();
1827        if (option != null) {
1828            if (!option.equals("--days")) {
1829                throw new IllegalArgumentException("unrecognized option " + option);
1830            }
1831
1832            days = Integer.parseInt(nextArgRequired());
1833            if (days <= 0) {
1834                throw new IllegalArgumentException("--days must be a positive integer");
1835            }
1836        }
1837
1838        try {
1839            Configuration config = mAm.getConfiguration();
1840            if (config == null) {
1841                System.err.println("Activity manager has no configuration");
1842                return;
1843            }
1844
1845            System.out.println("config: " + Configuration.resourceQualifierString(config));
1846            System.out.println("abi: " + TextUtils.join(",", Build.SUPPORTED_ABIS));
1847
1848            final List<Configuration> recentConfigs = getRecentConfigurations(days);
1849            final int recentConfigSize = recentConfigs.size();
1850            if (recentConfigSize > 0) {
1851                System.out.println("recentConfigs:");
1852            }
1853
1854            for (int i = 0; i < recentConfigSize; i++) {
1855                System.out.println("  config: " + Configuration.resourceQualifierString(
1856                        recentConfigs.get(i)));
1857            }
1858
1859        } catch (RemoteException e) {
1860        }
1861    }
1862
1863    /**
1864     * Open the given file for sending into the system process. This verifies
1865     * with SELinux that the system will have access to the file.
1866     */
1867    private static ParcelFileDescriptor openForSystemServer(File file, int mode)
1868            throws FileNotFoundException {
1869        final ParcelFileDescriptor fd = ParcelFileDescriptor.open(file, mode);
1870        final String tcon = SELinux.getFileContext(file.getAbsolutePath());
1871        if (!SELinux.checkSELinuxAccess("u:r:system_server:s0", tcon, "file", "read")) {
1872            throw new FileNotFoundException("System server has no access to file context " + tcon);
1873        }
1874        return fd;
1875    }
1876}
1877