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