Am.java revision 9c8dd55a9d829c29a3feee9469d8c2f27a9f5516
1/*
2**
3** Copyright 2007, The Android Open Source Project
4**
5** Licensed under the Apache License, Version 2.0 (the "License");
6** you may not use this file except in compliance with the License.
7** You may obtain a copy of the License at
8**
9**     http://www.apache.org/licenses/LICENSE-2.0
10**
11** Unless required by applicable law or agreed to in writing, software
12** distributed under the License is distributed on an "AS IS" BASIS,
13** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14** See the License for the specific language governing permissions and
15** limitations under the License.
16*/
17
18
19package com.android.commands.am;
20
21import android.app.ActivityManagerNative;
22import android.app.IActivityManager;
23import android.app.IInstrumentationWatcher;
24import android.app.Instrumentation;
25import android.content.ComponentName;
26import android.content.Intent;
27import android.net.Uri;
28import android.os.Bundle;
29import android.os.ParcelFileDescriptor;
30import android.os.RemoteException;
31import android.os.ServiceManager;
32import android.view.IWindowManager;
33
34import java.io.File;
35import java.io.FileNotFoundException;
36import java.util.Iterator;
37import java.util.Set;
38
39public class Am {
40
41    private IActivityManager mAm;
42    private String[] mArgs;
43    private int mNextArg;
44    private String mCurArgData;
45
46    private boolean mDebugOption = false;
47
48    /**
49     * Command-line entry point.
50     *
51     * @param args The command-line arguments
52     */
53    public static void main(String[] args) {
54        (new Am()).run(args);
55    }
56
57    private void run(String[] args) {
58        if (args.length < 1) {
59            showUsage();
60            return;
61        }
62
63        mAm = ActivityManagerNative.getDefault();
64        if (mAm == null) {
65            System.err.println("Error type 2");
66            System.err.println("Error: Unable to connect to activity manager; is the system running?");
67            showUsage();
68            return;
69        }
70
71        mArgs = args;
72
73        String op = args[0];
74        mNextArg = 1;
75        if (op.equals("start")) {
76            runStart();
77        } else if (op.equals("instrument")) {
78            runInstrument();
79        } else if (op.equals("broadcast")) {
80            sendBroadcast();
81        } else if (op.equals("profile")) {
82            runProfile();
83        } else {
84            System.err.println("Error: Unknown command: " + op);
85            showUsage();
86            return;
87        }
88    }
89
90    private Intent makeIntent() {
91        Intent intent = new Intent();
92        boolean hasIntentInfo = false;
93
94        mDebugOption = false;
95        Uri data = null;
96        String type = null;
97
98        try {
99            String opt;
100            while ((opt=nextOption()) != null) {
101                if (opt.equals("-a")) {
102                    intent.setAction(nextOptionData());
103                    hasIntentInfo = true;
104                } else if (opt.equals("-d")) {
105                    data = Uri.parse(nextOptionData());
106                    hasIntentInfo = true;
107                } else if (opt.equals("-t")) {
108                    type = nextOptionData();
109                    hasIntentInfo = true;
110                } else if (opt.equals("-c")) {
111                    intent.addCategory(nextOptionData());
112                    hasIntentInfo = true;
113                } else if (opt.equals("-e") || opt.equals("--es")) {
114                    String key = nextOptionData();
115                    String value = nextOptionData();
116                    intent.putExtra(key, value);
117                    hasIntentInfo = true;
118                } else if (opt.equals("--ei")) {
119                    String key = nextOptionData();
120                    String value = nextOptionData();
121                    intent.putExtra(key, Integer.valueOf(value));
122                    hasIntentInfo = true;
123                } else if (opt.equals("--ez")) {
124                    String key = nextOptionData();
125                    String value = nextOptionData();
126                    intent.putExtra(key, Boolean.valueOf(value));
127                    hasIntentInfo = true;
128                } else if (opt.equals("-n")) {
129                    String str = nextOptionData();
130                    ComponentName cn = ComponentName.unflattenFromString(str);
131                    if (cn == null) {
132                        System.err.println("Error: Bad component name: " + str);
133                        showUsage();
134                        return null;
135                    }
136                    intent.setComponent(cn);
137                    hasIntentInfo = true;
138                } else if (opt.equals("-f")) {
139                    String str = nextOptionData();
140                    intent.setFlags(Integer.decode(str).intValue());
141                } else if (opt.equals("-D")) {
142                    mDebugOption = true;
143                } else {
144                    System.err.println("Error: Unknown option: " + opt);
145                    showUsage();
146                    return null;
147                }
148            }
149        } catch (RuntimeException ex) {
150            System.err.println("Error: " + ex.toString());
151            showUsage();
152            return null;
153        }
154        intent.setDataAndType(data, type);
155
156        String uri = nextArg();
157        if (uri != null) {
158            try {
159                Intent oldIntent = intent;
160                try {
161                    intent = Intent.getIntent(uri);
162                } catch (java.net.URISyntaxException ex) {
163                    System.err.println("Bad URI: " + uri);
164                    showUsage();
165                    return null;
166                }
167                if (oldIntent.getAction() != null) {
168                    intent.setAction(oldIntent.getAction());
169                }
170                if (oldIntent.getData() != null || oldIntent.getType() != null) {
171                    intent.setDataAndType(oldIntent.getData(), oldIntent.getType());
172                }
173                Set cats = oldIntent.getCategories();
174                if (cats != null) {
175                    Iterator it = cats.iterator();
176                    while (it.hasNext()) {
177                        intent.addCategory((String)it.next());
178                    }
179                }
180            } catch (RuntimeException ex) {
181                System.err.println("Error creating from URI: " + ex.toString());
182                showUsage();
183                return null;
184            }
185        } else if (!hasIntentInfo) {
186            System.err.println("Error: No intent supplied");
187            showUsage();
188            return null;
189        }
190
191        return intent;
192    }
193
194    private void runStart() {
195        Intent intent = makeIntent();
196
197        if (intent != null) {
198            System.out.println("Starting: " + intent);
199            try {
200                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
201                // XXX should do something to determine the MIME type.
202                int res = mAm.startActivity(null, intent, intent.getType(),
203                        null, 0, null, null, 0, false, mDebugOption);
204                switch (res) {
205                    case IActivityManager.START_SUCCESS:
206                        break;
207                    case IActivityManager.START_SWITCHES_CANCELED:
208                        System.err.println(
209                                "Warning: Activity not started because the "
210                                + " current activity is being kept for the user.");
211                        break;
212                    case IActivityManager.START_DELIVERED_TO_TOP:
213                        System.err.println(
214                                "Warning: Activity not started, intent has "
215                                + "been delivered to currently running "
216                                + "top-most instance.");
217                        break;
218                    case IActivityManager.START_RETURN_INTENT_TO_CALLER:
219                        System.err.println(
220                                "Warning: Activity not started because intent "
221                                + "should be handled by the caller");
222                        break;
223                    case IActivityManager.START_TASK_TO_FRONT:
224                        System.err.println(
225                                "Warning: Activity not started, its current "
226                                + "task has been brought to the front");
227                        break;
228                    case IActivityManager.START_INTENT_NOT_RESOLVED:
229                        System.err.println(
230                                "Error: Activity not started, unable to "
231                                + "resolve " + intent.toString());
232                        break;
233                    case IActivityManager.START_CLASS_NOT_FOUND:
234                        System.err.println("Error type 3");
235                        System.err.println("Error: Activity class " +
236                                intent.getComponent().toShortString()
237                                + " does not exist.");
238                        break;
239                    case IActivityManager.START_FORWARD_AND_REQUEST_CONFLICT:
240                        System.err.println(
241                                "Error: Activity not started, you requested to "
242                                + "both forward and receive its result");
243                        break;
244                    case IActivityManager.START_PERMISSION_DENIED:
245                        System.err.println(
246                                "Error: Activity not started, you do not "
247                                + "have permission to access it.");
248                        break;
249                    default:
250                        System.err.println(
251                                "Error: Activity not started, unknown error "
252                                + "code " + res);
253                        break;
254                }
255            } catch (RemoteException e) {
256                System.err.println("Error type 1");
257                System.err.println(
258                        "Error: Activity not started, unable to "
259                        + "call on to activity manager service");
260            }
261        }
262    }
263
264    private void sendBroadcast() {
265        Intent intent = makeIntent();
266
267        if (intent != null) {
268            System.out.println("Broadcasting: " + intent);
269            try {
270                mAm.broadcastIntent(null, intent, null, null, 0, null, null,
271                        null, true, false);
272            } catch (RemoteException e) {
273            }
274        }
275    }
276
277    private void runInstrument() {
278        String profileFile = null;
279        boolean wait = false;
280        boolean rawMode = false;
281        boolean no_window_animation = false;
282        Bundle args = new Bundle();
283        String argKey = null, argValue = null;
284        IWindowManager wm = IWindowManager.Stub.asInterface(ServiceManager.getService("window"));
285
286        try {
287            String opt;
288            while ((opt=nextOption()) != null) {
289                if (opt.equals("-p")) {
290                    profileFile = nextOptionData();
291                } else if (opt.equals("-w")) {
292                    wait = true;
293                } else if (opt.equals("-r")) {
294                    rawMode = true;
295                } else if (opt.equals("-e")) {
296                    argKey = nextOptionData();
297                    argValue = nextOptionData();
298                    args.putString(argKey, argValue);
299                } else if (opt.equals("--no_window_animation")) {
300                    no_window_animation = true;
301                } else {
302                    System.err.println("Error: Unknown option: " + opt);
303                    showUsage();
304                    return;
305                }
306            }
307        } catch (RuntimeException ex) {
308            System.err.println("Error: " + ex.toString());
309            showUsage();
310            return;
311        }
312
313        String cnArg = nextArg();
314        if (cnArg == null) {
315            System.err.println("Error: No instrumentation component supplied");
316            showUsage();
317            return;
318        }
319
320        ComponentName cn = ComponentName.unflattenFromString(cnArg);
321        if (cn == null) {
322            System.err.println("Error: Bad component name: " + cnArg);
323            showUsage();
324            return;
325        }
326
327        InstrumentationWatcher watcher = null;
328        if (wait) {
329            watcher = new InstrumentationWatcher();
330            watcher.setRawOutput(rawMode);
331        }
332        float[] oldAnims = null;
333        if (no_window_animation) {
334            try {
335                oldAnims = wm.getAnimationScales();
336                wm.setAnimationScale(0, 0.0f);
337                wm.setAnimationScale(1, 0.0f);
338            } catch (RemoteException e) {
339            }
340        }
341
342        try {
343            if (!mAm.startInstrumentation(cn, profileFile, 0, args, watcher)) {
344                System.out.println("INSTRUMENTATION_FAILED: " +
345                        cn.flattenToString());
346                showUsage();
347                return;
348            }
349        } catch (RemoteException e) {
350        }
351
352        if (watcher != null) {
353            if (!watcher.waitForFinish()) {
354                System.out.println("INSTRUMENTATION_ABORTED: System has crashed.");
355            }
356        }
357
358        if (oldAnims != null) {
359            try {
360                wm.setAnimationScales(oldAnims);
361            } catch (RemoteException e) {
362            }
363        }
364    }
365
366    private class InstrumentationWatcher extends IInstrumentationWatcher.Stub {
367        private boolean mFinished = false;
368        private boolean mRawMode = false;
369
370        /**
371         * Set or reset "raw mode".  In "raw mode", all bundles are dumped.  In "pretty mode",
372         * if a bundle includes Instrumentation.REPORT_KEY_STREAMRESULT, just print that.
373         * @param rawMode true for raw mode, false for pretty mode.
374         */
375        public void setRawOutput(boolean rawMode) {
376            mRawMode = rawMode;
377        }
378
379        public void instrumentationStatus(ComponentName name, int resultCode, Bundle results) {
380            synchronized (this) {
381                // pretty printer mode?
382                String pretty = null;
383                if (!mRawMode && results != null) {
384                    pretty = results.getString(Instrumentation.REPORT_KEY_STREAMRESULT);
385                }
386                if (pretty != null) {
387                    System.out.print(pretty);
388                } else {
389                    if (results != null) {
390                        for (String key : results.keySet()) {
391                            System.out.println(
392                                    "INSTRUMENTATION_STATUS: " + key + "=" + results.get(key));
393                        }
394                    }
395                    System.out.println("INSTRUMENTATION_STATUS_CODE: " + resultCode);
396                }
397                notifyAll();
398            }
399        }
400
401        public void instrumentationFinished(ComponentName name, int resultCode,
402                Bundle results) {
403            synchronized (this) {
404                // pretty printer mode?
405                String pretty = null;
406                if (!mRawMode && results != null) {
407                    pretty = results.getString(Instrumentation.REPORT_KEY_STREAMRESULT);
408                }
409                if (pretty != null) {
410                    System.out.println(pretty);
411                } else {
412                    if (results != null) {
413                        for (String key : results.keySet()) {
414                            System.out.println(
415                                    "INSTRUMENTATION_RESULT: " + key + "=" + results.get(key));
416                        }
417                    }
418                    System.out.println("INSTRUMENTATION_CODE: " + resultCode);
419                }
420                mFinished = true;
421                notifyAll();
422            }
423        }
424
425        public boolean waitForFinish() {
426            synchronized (this) {
427                while (!mFinished) {
428                    try {
429                        if (!mAm.asBinder().pingBinder()) {
430                            return false;
431                        }
432                        wait(1000);
433                    } catch (InterruptedException e) {
434                    }
435                }
436            }
437            return true;
438        }
439    }
440
441    private void runProfile() {
442        String profileFile = null;
443        boolean start = false;
444
445        String process = nextArg();
446        if (process == null) {
447            System.err.println("Error: No profile process supplied");
448            showUsage();
449            return;
450        }
451
452        ParcelFileDescriptor fd = null;
453
454        String cmd = nextArg();
455        if ("start".equals(cmd)) {
456            start = true;
457            profileFile = nextArg();
458            if (profileFile == null) {
459                System.err.println("Error: No profile file path supplied");
460                showUsage();
461                return;
462            }
463            try {
464                fd = ParcelFileDescriptor.open(
465                        new File(profileFile),
466                        ParcelFileDescriptor.MODE_CREATE |
467                        ParcelFileDescriptor.MODE_TRUNCATE |
468                        ParcelFileDescriptor.MODE_READ_WRITE);
469            } catch (FileNotFoundException e) {
470                System.err.println("Error: Unable to open file: " + profileFile);
471                return;
472            }
473        } else if (!"stop".equals(cmd)) {
474            System.err.println("Error: Profile command " + cmd + " not valid");
475            showUsage();
476            return;
477        }
478
479        try {
480            if (!mAm.profileControl(process, start, profileFile, fd)) {
481                System.err.println("PROFILE FAILED on process " + process);
482                return;
483            }
484        } catch (IllegalArgumentException e) {
485            System.out.println("PROFILE FAILED: " + e.getMessage());
486            return;
487        } catch (IllegalStateException e) {
488            System.out.println("PROFILE FAILED: " + e.getMessage());
489            return;
490        } catch (RemoteException e) {
491            System.out.println("PROFILE FAILED: activity manager gone");
492            return;
493        }
494    }
495
496    private String nextOption() {
497        if (mNextArg >= mArgs.length) {
498            return null;
499        }
500        String arg = mArgs[mNextArg];
501        if (!arg.startsWith("-")) {
502            return null;
503        }
504        mNextArg++;
505        if (arg.equals("--")) {
506            return null;
507        }
508        if (arg.length() > 1 && arg.charAt(1) != '-') {
509            if (arg.length() > 2) {
510                mCurArgData = arg.substring(2);
511                return arg.substring(0, 2);
512            } else {
513                mCurArgData = null;
514                return arg;
515            }
516        }
517        mCurArgData = null;
518        return arg;
519    }
520
521    private String nextOptionData() {
522        if (mCurArgData != null) {
523            return mCurArgData;
524        }
525        if (mNextArg >= mArgs.length) {
526            return null;
527        }
528        String data = mArgs[mNextArg];
529        mNextArg++;
530        return data;
531    }
532
533    private String nextArg() {
534        if (mNextArg >= mArgs.length) {
535            return null;
536        }
537        String arg = mArgs[mNextArg];
538        mNextArg++;
539        return arg;
540    }
541
542    private void showUsage() {
543        System.err.println("usage: am [start|broadcast|instrument|profile]");
544        System.err.println("       am start [-D] INTENT");
545        System.err.println("       am broadcast INTENT");
546        System.err.println("       am instrument [-r] [-e <ARG_NAME> <ARG_VALUE>] [-p <PROF_FILE>]");
547        System.err.println("                [-w] <COMPONENT> ");
548        System.err.println("       am profile <PROCESS> [start <PROF_FILE>|stop]");
549        System.err.println("");
550        System.err.println("       INTENT is described with:");
551        System.err.println("                [-a <ACTION>] [-d <DATA_URI>] [-t <MIME_TYPE>]");
552        System.err.println("                [-c <CATEGORY> [-c <CATEGORY>] ...]");
553        System.err.println("                [-e|--es <EXTRA_KEY> <EXTRA_STRING_VALUE> ...]");
554        System.err.println("                [--ez <EXTRA_KEY> <EXTRA_BOOLEAN_VALUE> ...]");
555        System.err.println("                [-e|--ei <EXTRA_KEY> <EXTRA_INT_VALUE> ...]");
556        System.err.println("                [-n <COMPONENT>] [-f <FLAGS>] [<URI>]");
557    }
558}
559