OSDN Git Service

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