Pm.java revision 23b4faf69437b732d681d9bee5c9a84379d5ccad
1/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.commands.pm;
18
19import android.content.ComponentName;
20import android.content.pm.ApplicationInfo;
21import android.content.pm.IPackageDeleteObserver;
22import android.content.pm.IPackageInstallObserver;
23import android.content.pm.IPackageManager;
24import android.content.pm.InstrumentationInfo;
25import android.content.pm.PackageInfo;
26import android.content.pm.PackageItemInfo;
27import android.content.pm.PackageManager;
28import android.content.pm.PermissionGroupInfo;
29import android.content.pm.PermissionInfo;
30import android.content.res.AssetManager;
31import android.content.res.Resources;
32import android.net.Uri;
33import android.os.RemoteException;
34import android.os.ServiceManager;
35
36import java.io.File;
37import java.lang.reflect.Field;
38import java.lang.reflect.Modifier;
39import java.util.ArrayList;
40import java.util.Collections;
41import java.util.Comparator;
42import java.util.List;
43import java.util.WeakHashMap;
44
45public final class Pm {
46    IPackageManager mPm;
47
48    private WeakHashMap<String, Resources> mResourceCache
49            = new WeakHashMap<String, Resources>();
50
51    private String[] mArgs;
52    private int mNextArg;
53    private String mCurArgData;
54
55    private static final String PM_NOT_RUNNING_ERR =
56        "Error: Could not access the Package Manager.  Is the system running?";
57
58    public static void main(String[] args) {
59        new Pm().run(args);
60    }
61
62    public void run(String[] args) {
63        boolean validCommand = false;
64        if (args.length < 1) {
65            showUsage();
66            return;
67        }
68
69        mPm = IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
70        if (mPm == null) {
71            System.err.println(PM_NOT_RUNNING_ERR);
72            return;
73        }
74
75        mArgs = args;
76        String op = args[0];
77        mNextArg = 1;
78
79        if ("list".equals(op)) {
80            runList();
81            return;
82        }
83
84        if ("path".equals(op)) {
85            runPath();
86            return;
87        }
88
89        if ("install".equals(op)) {
90            runInstall();
91            return;
92        }
93
94        if ("uninstall".equals(op)) {
95            runUninstall();
96            return;
97        }
98
99        if ("enable".equals(op)) {
100            runSetEnabledSetting(PackageManager.COMPONENT_ENABLED_STATE_ENABLED);
101            return;
102        }
103
104        if ("disable".equals(op)) {
105            runSetEnabledSetting(PackageManager.COMPONENT_ENABLED_STATE_DISABLED);
106            return;
107        }
108
109        try {
110            if (args.length == 1) {
111                if (args[0].equalsIgnoreCase("-l")) {
112                    validCommand = true;
113                    runListPackages(false);
114                } else if (args[0].equalsIgnoreCase("-lf")){
115                    validCommand = true;
116                    runListPackages(true);
117                }
118            } else if (args.length == 2) {
119                if (args[0].equalsIgnoreCase("-p")) {
120                    validCommand = true;
121                    displayPackageFilePath(args[1]);
122                }
123            }
124        } finally {
125            if (validCommand == false) {
126                if (op != null) {
127                    System.err.println("Error: unknown command '" + op + "'");
128                }
129                showUsage();
130            }
131        }
132    }
133
134    /**
135     * Execute the list sub-command.
136     *
137     * pm list [package | packages]
138     * pm list permission-groups
139     * pm list permissions
140     * pm list instrumentation
141     */
142    private void runList() {
143        String type = nextArg();
144        if (type == null) {
145            System.err.println("Error: didn't specify type of data to list");
146            showUsage();
147            return;
148        }
149        if ("package".equals(type) || "packages".equals(type)) {
150            runListPackages(false);
151        } else if ("permission-groups".equals(type)) {
152            runListPermissionGroups();
153        } else if ("permissions".equals(type)) {
154            runListPermissions();
155        } else if ("instrumentation".equals(type)) {
156            runListInstrumentation();
157        } else {
158            System.err.println("Error: unknown list type '" + type + "'");
159            showUsage();
160        }
161    }
162
163    /**
164     * Lists all the installed packages.
165     */
166    private void runListPackages(boolean showApplicationPackage) {
167        try {
168            String opt;
169            while ((opt=nextOption()) != null) {
170                if (opt.equals("-l")) {
171                    // old compat
172                } else if (opt.equals("-lf")) {
173                    showApplicationPackage = true;
174                } else if (opt.equals("-f")) {
175                    showApplicationPackage = true;
176                } else {
177                    System.err.println("Error: Unknown option: " + opt);
178                    showUsage();
179                    return;
180                }
181            }
182        } catch (RuntimeException ex) {
183            System.err.println("Error: " + ex.toString());
184            showUsage();
185            return;
186        }
187
188        try {
189            List<PackageInfo> packages = mPm.getInstalledPackages(0 /* all */);
190
191            int count = packages.size();
192            for (int p = 0 ; p < count ; p++) {
193                PackageInfo info = packages.get(p);
194                System.out.print("package:");
195                if (showApplicationPackage) {
196                    System.out.print(info.applicationInfo.sourceDir);
197                    System.out.print("=");
198                }
199                System.out.println(info.packageName);
200            }
201        } catch (RemoteException e) {
202            System.err.println(e.toString());
203            System.err.println(PM_NOT_RUNNING_ERR);
204        }
205    }
206
207    /**
208     * Lists all of the installed instrumentation, or all for a given package
209     *
210     * pm list instrumentation [package] [-f]
211     */
212    private void runListInstrumentation() {
213        int flags = 0;      // flags != 0 is only used to request meta-data
214        boolean showPackage = false;
215        String targetPackage = null;
216
217        try {
218            String opt;
219            while ((opt=nextArg()) != null) {
220                if (opt.equals("-f")) {
221                    showPackage = true;
222                } else if (opt.charAt(0) != '-') {
223                    targetPackage = opt;
224                } else {
225                    System.err.println("Error: Unknown option: " + opt);
226                    showUsage();
227                    return;
228                }
229            }
230        } catch (RuntimeException ex) {
231            System.err.println("Error: " + ex.toString());
232            showUsage();
233            return;
234        }
235
236        try {
237            List<InstrumentationInfo> list = mPm.queryInstrumentation(targetPackage, flags);
238
239            // Sort by target package
240            Collections.sort(list, new Comparator<InstrumentationInfo>() {
241                public int compare(InstrumentationInfo o1, InstrumentationInfo o2) {
242                    return o1.targetPackage.compareTo(o2.targetPackage);
243                }
244            });
245
246            int count = (list != null) ? list.size() : 0;
247            for (int p = 0; p < count; p++) {
248                InstrumentationInfo ii = list.get(p);
249                System.out.print("instrumentation:");
250                if (showPackage) {
251                    System.out.print(ii.sourceDir);
252                    System.out.print("=");
253                }
254                ComponentName cn = new ComponentName(ii.packageName, ii.name);
255                System.out.print(cn.flattenToShortString());
256                System.out.print(" (target=");
257                System.out.print(ii.targetPackage);
258                System.out.println(")");
259            }
260        } catch (RemoteException e) {
261            System.err.println(e.toString());
262            System.err.println(PM_NOT_RUNNING_ERR);
263        }
264    }
265
266    /**
267     * Lists all the known permission groups.
268     */
269    private void runListPermissionGroups() {
270        try {
271            List<PermissionGroupInfo> pgs = mPm.getAllPermissionGroups(0);
272
273            int count = pgs.size();
274            for (int p = 0 ; p < count ; p++) {
275                PermissionGroupInfo pgi = pgs.get(p);
276                System.out.print("permission group:");
277                System.out.println(pgi.name);
278            }
279        } catch (RemoteException e) {
280            System.err.println(e.toString());
281            System.err.println(PM_NOT_RUNNING_ERR);
282        }
283    }
284
285    private String loadText(PackageItemInfo pii, int res, CharSequence nonLocalized) {
286        if (nonLocalized != null) {
287            return nonLocalized.toString();
288        }
289        Resources r = getResources(pii);
290        if (r != null) {
291            return r.getString(res);
292        }
293        return null;
294    }
295
296    /**
297     * Lists all the permissions in a group.
298     */
299    private void runListPermissions() {
300        try {
301            boolean labels = false;
302            boolean groups = false;
303            boolean userOnly = false;
304            boolean summary = false;
305            boolean dangerousOnly = false;
306            String opt;
307            while ((opt=nextOption()) != null) {
308                if (opt.equals("-f")) {
309                    labels = true;
310                } else if (opt.equals("-g")) {
311                    groups = true;
312                } else if (opt.equals("-s")) {
313                    groups = true;
314                    labels = true;
315                    summary = true;
316                } else if (opt.equals("-u")) {
317                    userOnly = true;
318                } else if (opt.equals("-d")) {
319                    dangerousOnly = true;
320                } else {
321                    System.err.println("Error: Unknown option: " + opt);
322                    showUsage();
323                    return;
324                }
325            }
326
327            String grp = nextOption();
328            ArrayList<String> groupList = new ArrayList<String>();
329            if (groups) {
330                List<PermissionGroupInfo> infos =
331                        mPm.getAllPermissionGroups(0);
332                for (int i=0; i<infos.size(); i++) {
333                    groupList.add(infos.get(i).name);
334                }
335                groupList.add(null);
336            } else {
337                groupList.add(grp);
338            }
339
340            if (dangerousOnly) {
341                System.out.println("Dangerous Permissions:");
342                System.out.println("");
343                doListPermissions(groupList, groups, labels, summary,
344                        PermissionInfo.PROTECTION_DANGEROUS,
345                        PermissionInfo.PROTECTION_DANGEROUS);
346                if (userOnly) {
347                    System.out.println("Normal Permissions:");
348                    System.out.println("");
349                    doListPermissions(groupList, groups, labels, summary,
350                            PermissionInfo.PROTECTION_NORMAL,
351                            PermissionInfo.PROTECTION_NORMAL);
352                }
353            } else if (userOnly) {
354                System.out.println("Dangerous and Normal Permissions:");
355                System.out.println("");
356                doListPermissions(groupList, groups, labels, summary,
357                        PermissionInfo.PROTECTION_NORMAL,
358                        PermissionInfo.PROTECTION_DANGEROUS);
359            } else {
360                System.out.println("All Permissions:");
361                System.out.println("");
362                doListPermissions(groupList, groups, labels, summary,
363                        -10000, 10000);
364            }
365        } catch (RemoteException e) {
366            System.err.println(e.toString());
367            System.err.println(PM_NOT_RUNNING_ERR);
368        }
369    }
370
371    private void doListPermissions(ArrayList<String> groupList,
372            boolean groups, boolean labels, boolean summary,
373            int startProtectionLevel, int endProtectionLevel)
374            throws RemoteException {
375        for (int i=0; i<groupList.size(); i++) {
376            String groupName = groupList.get(i);
377            String prefix = "";
378            if (groups) {
379                if (i > 0) System.out.println("");
380                if (groupName != null) {
381                    PermissionGroupInfo pgi = mPm.getPermissionGroupInfo(
382                            groupName, 0);
383                    if (summary) {
384                        Resources res = getResources(pgi);
385                        if (res != null) {
386                            System.out.print(loadText(pgi, pgi.labelRes,
387                                    pgi.nonLocalizedLabel) + ": ");
388                        } else {
389                            System.out.print(pgi.name + ": ");
390
391                        }
392                    } else {
393                        System.out.println((labels ? "+ " : "")
394                                + "group:" + pgi.name);
395                        if (labels) {
396                            System.out.println("  package:" + pgi.packageName);
397                            Resources res = getResources(pgi);
398                            if (res != null) {
399                                System.out.println("  label:"
400                                        + loadText(pgi, pgi.labelRes,
401                                                pgi.nonLocalizedLabel));
402                                System.out.println("  description:"
403                                        + loadText(pgi, pgi.descriptionRes,
404                                                pgi.nonLocalizedDescription));
405                            }
406                        }
407                    }
408                } else {
409                    System.out.println(((labels && !summary)
410                            ? "+ " : "") + "ungrouped:");
411                }
412                prefix = "  ";
413            }
414            List<PermissionInfo> ps = mPm.queryPermissionsByGroup(
415                    groupList.get(i), 0);
416            int count = ps.size();
417            boolean first = true;
418            for (int p = 0 ; p < count ; p++) {
419                PermissionInfo pi = ps.get(p);
420                if (groups && groupName == null && pi.group != null) {
421                    continue;
422                }
423                if (pi.protectionLevel < startProtectionLevel
424                        || pi.protectionLevel > endProtectionLevel) {
425                    continue;
426                }
427                if (summary) {
428                    if (first) {
429                        first = false;
430                    } else {
431                        System.out.print(", ");
432                    }
433                    Resources res = getResources(pi);
434                    if (res != null) {
435                        System.out.print(loadText(pi, pi.labelRes,
436                                pi.nonLocalizedLabel));
437                    } else {
438                        System.out.print(pi.name);
439                    }
440                } else {
441                    System.out.println(prefix + (labels ? "+ " : "")
442                            + "permission:" + pi.name);
443                    if (labels) {
444                        System.out.println(prefix + "  package:" + pi.packageName);
445                        Resources res = getResources(pi);
446                        if (res != null) {
447                            System.out.println(prefix + "  label:"
448                                    + loadText(pi, pi.labelRes,
449                                            pi.nonLocalizedLabel));
450                            System.out.println(prefix + "  description:"
451                                    + loadText(pi, pi.descriptionRes,
452                                            pi.nonLocalizedDescription));
453                        }
454                        String protLevel = "unknown";
455                        switch(pi.protectionLevel) {
456                            case PermissionInfo.PROTECTION_DANGEROUS:
457                                protLevel = "dangerous";
458                                break;
459                            case PermissionInfo.PROTECTION_NORMAL:
460                                protLevel = "normal";
461                                break;
462                            case PermissionInfo.PROTECTION_SIGNATURE:
463                                protLevel = "signature";
464                                break;
465                            case PermissionInfo.PROTECTION_SIGNATURE_OR_SYSTEM:
466                                protLevel = "signatureOrSystem";
467                                break;
468                        }
469                        System.out.println(prefix + "  protectionLevel:" + protLevel);
470                    }
471                }
472            }
473
474            if (summary) {
475                System.out.println("");
476            }
477        }
478    }
479
480    private void runPath() {
481        String pkg = nextArg();
482        if (pkg == null) {
483            System.err.println("Error: no package specified");
484            showUsage();
485            return;
486        }
487        displayPackageFilePath(pkg);
488    }
489
490    class PackageInstallObserver extends IPackageInstallObserver.Stub {
491        boolean finished;
492        int result;
493
494        public void packageInstalled(String name, int status) {
495            synchronized( this) {
496                finished = true;
497                result = status;
498                notifyAll();
499            }
500        }
501    }
502
503    /**
504     * Converts a failure code into a string by using reflection to find a matching constant
505     * in PackageManager.
506     */
507    private String installFailureToString(int result) {
508        Field[] fields = PackageManager.class.getFields();
509        for (Field f: fields) {
510            if (f.getType() == int.class) {
511                int modifiers = f.getModifiers();
512                // only look at public final static fields.
513                if (((modifiers & Modifier.FINAL) != 0) &&
514                        ((modifiers & Modifier.PUBLIC) != 0) &&
515                        ((modifiers & Modifier.STATIC) != 0)) {
516                    String fieldName = f.getName();
517                    if (fieldName.startsWith("INSTALL_FAILED_") ||
518                            fieldName.startsWith("INSTALL_PARSE_FAILED_")) {
519                        // get the int value and compare it to result.
520                        try {
521                            if (result == f.getInt(null)) {
522                                return fieldName;
523                            }
524                        } catch (IllegalAccessException e) {
525                            // this shouldn't happen since we only look for public static fields.
526                        }
527                    }
528                }
529            }
530        }
531
532        // couldn't find a matching constant? return the value
533        return Integer.toString(result);
534    }
535
536    private void runInstall() {
537        int installFlags = 0;
538        String installerPackageName = null;
539
540        String opt;
541        while ((opt=nextOption()) != null) {
542            if (opt.equals("-l")) {
543                installFlags |= PackageManager.INSTALL_FORWARD_LOCK;
544            } else if (opt.equals("-r")) {
545                installFlags |= PackageManager.INSTALL_REPLACE_EXISTING;
546            } else if (opt.equals("-i")) {
547                installerPackageName = nextOptionData();
548                if (installerPackageName == null) {
549                    System.err.println("Error: no value specified for -i");
550                    showUsage();
551                    return;
552                }
553            } else if (opt.equals("-t")) {
554                installFlags |= PackageManager.INSTALL_ALLOW_TEST;
555            } else {
556                System.err.println("Error: Unknown option: " + opt);
557                showUsage();
558                return;
559            }
560        }
561
562        String apkFilePath = nextArg();
563        System.err.println("\tpkg: " + apkFilePath);
564        if (apkFilePath == null) {
565            System.err.println("Error: no package specified");
566            showUsage();
567            return;
568        }
569
570        PackageInstallObserver obs = new PackageInstallObserver();
571        try {
572            mPm.installPackage(Uri.fromFile(new File(apkFilePath)), obs, installFlags,
573                    installerPackageName);
574
575            synchronized (obs) {
576                while (!obs.finished) {
577                    try {
578                        obs.wait();
579                    } catch (InterruptedException e) {
580                    }
581                }
582                if (obs.result == PackageManager.INSTALL_SUCCEEDED) {
583                    System.out.println("Success");
584                } else {
585                    System.err.println("Failure ["
586                            + installFailureToString(obs.result)
587                            + "]");
588                }
589            }
590        } catch (RemoteException e) {
591            System.err.println(e.toString());
592            System.err.println(PM_NOT_RUNNING_ERR);
593        }
594    }
595
596    class PackageDeleteObserver extends IPackageDeleteObserver.Stub {
597        boolean finished;
598        boolean result;
599
600        public void packageDeleted(boolean succeeded) {
601            synchronized (this) {
602                finished = true;
603                result = succeeded;
604                notifyAll();
605            }
606        }
607    }
608
609    private void runUninstall() {
610        int unInstallFlags = 0;
611
612        String opt = nextOption();
613        if (opt != null && opt.equals("-k")) {
614            unInstallFlags = PackageManager.DONT_DELETE_DATA;
615        }
616
617        String pkg = nextArg();
618        if (pkg == null) {
619            System.err.println("Error: no package specified");
620            showUsage();
621            return;
622        }
623        boolean result = deletePackage(pkg, unInstallFlags);
624        if (result) {
625            System.out.println("Success");
626        } else {
627            System.out.println("Failure");
628        }
629    }
630
631    private boolean deletePackage(String pkg, int unInstallFlags) {
632        PackageDeleteObserver obs = new PackageDeleteObserver();
633        try {
634            mPm.deletePackage(pkg, obs, unInstallFlags);
635
636            synchronized (obs) {
637                while (!obs.finished) {
638                    try {
639                        obs.wait();
640                    } catch (InterruptedException e) {
641                    }
642                }
643            }
644        } catch (RemoteException e) {
645            System.err.println(e.toString());
646            System.err.println(PM_NOT_RUNNING_ERR);
647        }
648        return obs.result;
649    }
650
651    private static String enabledSettingToString(int state) {
652        switch (state) {
653            case PackageManager.COMPONENT_ENABLED_STATE_DEFAULT:
654                return "default";
655            case PackageManager.COMPONENT_ENABLED_STATE_ENABLED:
656                return "enabled";
657            case PackageManager.COMPONENT_ENABLED_STATE_DISABLED:
658                return "disabled";
659        }
660        return "unknown";
661    }
662
663    private void runSetEnabledSetting(int state) {
664        String pkg = nextArg();
665        if (pkg == null) {
666            System.err.println("Error: no package or component specified");
667            showUsage();
668            return;
669        }
670        ComponentName cn = ComponentName.unflattenFromString(pkg);
671        if (cn == null) {
672            try {
673                mPm.setApplicationEnabledSetting(pkg, state, 0);
674                System.err.println("Package " + pkg + " new state: "
675                        + enabledSettingToString(
676                                mPm.getApplicationEnabledSetting(pkg)));
677            } catch (RemoteException e) {
678                System.err.println(e.toString());
679                System.err.println(PM_NOT_RUNNING_ERR);
680            }
681        } else {
682            try {
683                mPm.setComponentEnabledSetting(cn, state, 0);
684                System.err.println("Component " + cn.toShortString() + " new state: "
685                        + enabledSettingToString(
686                                mPm.getComponentEnabledSetting(cn)));
687            } catch (RemoteException e) {
688                System.err.println(e.toString());
689                System.err.println(PM_NOT_RUNNING_ERR);
690            }
691        }
692    }
693
694    /**
695     * Displays the package file for a package.
696     * @param pckg
697     */
698    private void displayPackageFilePath(String pckg) {
699        try {
700            PackageInfo info = mPm.getPackageInfo(pckg, 0);
701            if (info != null && info.applicationInfo != null) {
702                System.out.print("package:");
703                System.out.println(info.applicationInfo.sourceDir);
704            }
705        } catch (RemoteException e) {
706            System.err.println(e.toString());
707            System.err.println(PM_NOT_RUNNING_ERR);
708        }
709    }
710
711    private Resources getResources(PackageItemInfo pii) {
712        Resources res = mResourceCache.get(pii.packageName);
713        if (res != null) return res;
714
715        try {
716            ApplicationInfo ai = mPm.getApplicationInfo(pii.packageName, 0);
717            AssetManager am = new AssetManager();
718            am.addAssetPath(ai.publicSourceDir);
719            res = new Resources(am, null, null);
720            mResourceCache.put(pii.packageName, res);
721            return res;
722        } catch (RemoteException e) {
723            System.err.println(e.toString());
724            System.err.println(PM_NOT_RUNNING_ERR);
725            return null;
726        }
727    }
728
729    private String nextOption() {
730        if (mNextArg >= mArgs.length) {
731            return null;
732        }
733        String arg = mArgs[mNextArg];
734        if (!arg.startsWith("-")) {
735            return null;
736        }
737        mNextArg++;
738        if (arg.equals("--")) {
739            return null;
740        }
741        if (arg.length() > 1 && arg.charAt(1) != '-') {
742            if (arg.length() > 2) {
743                mCurArgData = arg.substring(2);
744                return arg.substring(0, 2);
745            } else {
746                mCurArgData = null;
747                return arg;
748            }
749        }
750        mCurArgData = null;
751        return arg;
752    }
753
754    private String nextOptionData() {
755        if (mCurArgData != null) {
756            return mCurArgData;
757        }
758        if (mNextArg >= mArgs.length) {
759            return null;
760        }
761        String data = mArgs[mNextArg];
762        mNextArg++;
763        return data;
764    }
765
766    private String nextArg() {
767        if (mNextArg >= mArgs.length) {
768            return null;
769        }
770        String arg = mArgs[mNextArg];
771        mNextArg++;
772        return arg;
773    }
774
775    private static void showUsage() {
776        System.err.println("usage: pm [list|path|install|uninstall]");
777        System.err.println("       pm list packages [-f]");
778        System.err.println("       pm list permission-groups");
779        System.err.println("       pm list permissions [-g] [-f] [-d] [-u] [GROUP]");
780        System.err.println("       pm list instrumentation [-f] [TARGET-PACKAGE]");
781        System.err.println("       pm path PACKAGE");
782        System.err.println("       pm install [-l] [-r] [-t] [-i INSTALLER_PACKAGE_NAME] PATH");
783        System.err.println("       pm uninstall [-k] PACKAGE");
784        System.err.println("       pm enable PACKAGE_OR_COMPONENT");
785        System.err.println("       pm disable PACKAGE_OR_COMPONENT");
786        System.err.println("");
787        System.err.println("The list packages command prints all packages.  Options:");
788        System.err.println("  -f: see their associated file.");
789        System.err.println("");
790        System.err.println("The list permission-groups command prints all known");
791        System.err.println("permission groups.");
792        System.err.println("");
793        System.err.println("The list permissions command prints all known");
794        System.err.println("permissions, optionally only those in GROUP.  Options:");
795        System.err.println("  -g: organize by group.");
796        System.err.println("  -f: print all information.");
797        System.err.println("  -s: short summary.");
798        System.err.println("  -d: only list dangerous permissions.");
799        System.err.println("  -u: list only the permissions users will see.");
800        System.err.println("");
801        System.err.println("The list instrumentation command prints all instrumentations,");
802        System.err.println("or only those that target a specified package.  Options:");
803        System.err.println("  -f: see their associated file.");
804        System.err.println("");
805        System.err.println("The path command prints the path to the .apk of a package.");
806        System.err.println("");
807        System.err.println("The install command installs a package to the system.  Options:");
808        System.err.println("  -l: install the package with FORWARD_LOCK.");
809        System.err.println("  -r: reinstall an exisiting app, keeping its data.");
810        System.err.println("  -t: allow test .apks to be installed.");
811        System.err.println("  -i: specify the installer package name.");
812        System.err.println("");
813        System.err.println("The uninstall command removes a package from the system. Options:");
814        System.err.println("  -k: keep the data and cache directories around.");
815        System.err.println("after the package removal.");
816        System.err.println("");
817        System.err.println("The enable and disable commands change the enabled state of");
818        System.err.println("a given package or component (written as \"package/class\").");
819    }
820}
821