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