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