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