Pm.java revision 5e03e2ca7d25b899b129baad2dd5eca6bf99d88a
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 com.android.internal.content.PackageHelper;
20
21import android.app.ActivityManagerNative;
22import android.content.ComponentName;
23import android.content.pm.ApplicationInfo;
24import android.content.pm.ContainerEncryptionParams;
25import android.content.pm.FeatureInfo;
26import android.content.pm.IPackageDataObserver;
27import android.content.pm.IPackageDeleteObserver;
28import android.content.pm.IPackageInstallObserver;
29import android.content.pm.IPackageManager;
30import android.content.pm.InstrumentationInfo;
31import android.content.pm.PackageInfo;
32import android.content.pm.PackageItemInfo;
33import android.content.pm.PackageManager;
34import android.content.pm.ParceledListSlice;
35import android.content.pm.PermissionGroupInfo;
36import android.content.pm.PermissionInfo;
37import android.content.pm.UserInfo;
38import android.content.pm.VerificationParams;
39import android.content.res.AssetManager;
40import android.content.res.Resources;
41import android.net.Uri;
42import android.os.IUserManager;
43import android.os.RemoteException;
44import android.os.ServiceManager;
45
46import java.io.File;
47import java.lang.reflect.Field;
48import java.lang.reflect.Modifier;
49import java.security.InvalidAlgorithmParameterException;
50import java.util.ArrayList;
51import java.util.Collections;
52import java.util.Comparator;
53import java.util.List;
54import java.util.WeakHashMap;
55
56import javax.crypto.SecretKey;
57import javax.crypto.spec.IvParameterSpec;
58import javax.crypto.spec.SecretKeySpec;
59
60public final class Pm {
61    IPackageManager mPm;
62    IUserManager mUm;
63
64    private WeakHashMap<String, Resources> mResourceCache
65            = new WeakHashMap<String, Resources>();
66
67    private String[] mArgs;
68    private int mNextArg;
69    private String mCurArgData;
70
71    private static final String PM_NOT_RUNNING_ERR =
72        "Error: Could not access the Package Manager.  Is the system running?";
73
74    public static void main(String[] args) {
75        new Pm().run(args);
76    }
77
78    public void run(String[] args) {
79        boolean validCommand = false;
80        if (args.length < 1) {
81            showUsage();
82            return;
83        }
84
85        mUm = IUserManager.Stub.asInterface(ServiceManager.getService("user"));
86        mPm = IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
87        if (mPm == null) {
88            System.err.println(PM_NOT_RUNNING_ERR);
89            return;
90        }
91
92        mArgs = args;
93        String op = args[0];
94        mNextArg = 1;
95
96        if ("list".equals(op)) {
97            runList();
98            return;
99        }
100
101        if ("path".equals(op)) {
102            runPath();
103            return;
104        }
105
106        if ("install".equals(op)) {
107            runInstall();
108            return;
109        }
110
111        if ("uninstall".equals(op)) {
112            runUninstall();
113            return;
114        }
115
116        if ("clear".equals(op)) {
117            runClear();
118            return;
119        }
120
121        if ("enable".equals(op)) {
122            runSetEnabledSetting(PackageManager.COMPONENT_ENABLED_STATE_ENABLED);
123            return;
124        }
125
126        if ("disable".equals(op)) {
127            runSetEnabledSetting(PackageManager.COMPONENT_ENABLED_STATE_DISABLED);
128            return;
129        }
130
131        if ("disable-user".equals(op)) {
132            runSetEnabledSetting(PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER);
133            return;
134        }
135
136        if ("grant".equals(op)) {
137            runGrantRevokePermission(true);
138            return;
139        }
140
141        if ("revoke".equals(op)) {
142            runGrantRevokePermission(false);
143            return;
144        }
145
146        if ("set-permission-enforced".equals(op)) {
147            runSetPermissionEnforced();
148            return;
149        }
150
151        if ("set-install-location".equals(op)) {
152            runSetInstallLocation();
153            return;
154        }
155
156        if ("get-install-location".equals(op)) {
157            runGetInstallLocation();
158            return;
159        }
160
161        if ("trim-caches".equals(op)) {
162            runTrimCaches();
163            return;
164        }
165
166        if ("create-user".equals(op)) {
167            runCreateUser();
168            return;
169        }
170
171        if ("remove-user".equals(op)) {
172            runRemoveUser();
173            return;
174        }
175
176        try {
177            if (args.length == 1) {
178                if (args[0].equalsIgnoreCase("-l")) {
179                    validCommand = true;
180                    runListPackages(false);
181                } else if (args[0].equalsIgnoreCase("-lf")){
182                    validCommand = true;
183                    runListPackages(true);
184                }
185            } else if (args.length == 2) {
186                if (args[0].equalsIgnoreCase("-p")) {
187                    validCommand = true;
188                    displayPackageFilePath(args[1]);
189                }
190            }
191        } finally {
192            if (validCommand == false) {
193                if (op != null) {
194                    System.err.println("Error: unknown command '" + op + "'");
195                }
196                showUsage();
197            }
198        }
199    }
200
201    /**
202     * Execute the list sub-command.
203     *
204     * pm list [package | packages]
205     * pm list permission-groups
206     * pm list permissions
207     * pm list features
208     * pm list libraries
209     * pm list instrumentation
210     */
211    private void runList() {
212        String type = nextArg();
213        if (type == null) {
214            System.err.println("Error: didn't specify type of data to list");
215            return;
216        }
217        if ("package".equals(type) || "packages".equals(type)) {
218            runListPackages(false);
219        } else if ("permission-groups".equals(type)) {
220            runListPermissionGroups();
221        } else if ("permissions".equals(type)) {
222            runListPermissions();
223        } else if ("features".equals(type)) {
224            runListFeatures();
225        } else if ("libraries".equals(type)) {
226            runListLibraries();
227        } else if ("instrumentation".equals(type)) {
228            runListInstrumentation();
229        } else if ("users".equals(type)) {
230            runListUsers();
231        } else {
232            System.err.println("Error: unknown list type '" + type + "'");
233        }
234    }
235
236    /**
237     * Lists all the installed packages.
238     */
239    private void runListPackages(boolean showApplicationPackage) {
240        int getFlags = 0;
241        boolean listDisabled = false, listEnabled = false;
242        boolean listSystem = false, listThirdParty = false;
243        boolean listInstaller = false;
244        try {
245            String opt;
246            while ((opt=nextOption()) != null) {
247                if (opt.equals("-l")) {
248                    // old compat
249                } else if (opt.equals("-lf")) {
250                    showApplicationPackage = true;
251                } else if (opt.equals("-f")) {
252                    showApplicationPackage = true;
253                } else if (opt.equals("-d")) {
254                    listDisabled = true;
255                } else if (opt.equals("-e")) {
256                    listEnabled = true;
257                } else if (opt.equals("-s")) {
258                    listSystem = true;
259                } else if (opt.equals("-3")) {
260                    listThirdParty = true;
261                } else if (opt.equals("-i")) {
262                    listInstaller = true;
263                } else if (opt.equals("-u")) {
264                    getFlags |= PackageManager.GET_UNINSTALLED_PACKAGES;
265                } else {
266                    System.err.println("Error: Unknown option: " + opt);
267                    return;
268                }
269            }
270        } catch (RuntimeException ex) {
271            System.err.println("Error: " + ex.toString());
272            return;
273        }
274
275        String filter = nextArg();
276
277        try {
278            final List<PackageInfo> packages = getInstalledPackages(mPm, getFlags);
279
280            int count = packages.size();
281            for (int p = 0 ; p < count ; p++) {
282                PackageInfo info = packages.get(p);
283                if (filter != null && !info.packageName.contains(filter)) {
284                    continue;
285                }
286                final boolean isSystem =
287                        (info.applicationInfo.flags&ApplicationInfo.FLAG_SYSTEM) != 0;
288                if ((!listDisabled || !info.applicationInfo.enabled) &&
289                        (!listEnabled || info.applicationInfo.enabled) &&
290                        (!listSystem || isSystem) &&
291                        (!listThirdParty || !isSystem)) {
292                    System.out.print("package:");
293                    if (showApplicationPackage) {
294                        System.out.print(info.applicationInfo.sourceDir);
295                        System.out.print("=");
296                    }
297                    System.out.print(info.packageName);
298                    if (listInstaller) {
299                        System.out.print("  installer=");
300                        System.out.print(mPm.getInstallerPackageName(info.packageName));
301                    }
302                    System.out.println();
303                }
304            }
305        } catch (RemoteException e) {
306            System.err.println(e.toString());
307            System.err.println(PM_NOT_RUNNING_ERR);
308        }
309    }
310
311    @SuppressWarnings("unchecked")
312    private List<PackageInfo> getInstalledPackages(IPackageManager pm, int flags)
313            throws RemoteException {
314        final List<PackageInfo> packageInfos = new ArrayList<PackageInfo>();
315        PackageInfo lastItem = null;
316        ParceledListSlice<PackageInfo> slice;
317
318        do {
319            final String lastKey = lastItem != null ? lastItem.packageName : null;
320            slice = pm.getInstalledPackages(flags, lastKey);
321            lastItem = slice.populateList(packageInfos, PackageInfo.CREATOR);
322        } while (!slice.isLastSlice());
323
324        return packageInfos;
325    }
326
327    /**
328     * Lists all of the features supported by the current device.
329     *
330     * pm list features
331     */
332    private void runListFeatures() {
333        try {
334            List<FeatureInfo> list = new ArrayList<FeatureInfo>();
335            FeatureInfo[] rawList = mPm.getSystemAvailableFeatures();
336            for (int i=0; i<rawList.length; i++) {
337                list.add(rawList[i]);
338            }
339
340
341            // Sort by name
342            Collections.sort(list, new Comparator<FeatureInfo>() {
343                public int compare(FeatureInfo o1, FeatureInfo o2) {
344                    if (o1.name == o2.name) return 0;
345                    if (o1.name == null) return -1;
346                    if (o2.name == null) return 1;
347                    return o1.name.compareTo(o2.name);
348                }
349            });
350
351            int count = (list != null) ? list.size() : 0;
352            for (int p = 0; p < count; p++) {
353                FeatureInfo fi = list.get(p);
354                System.out.print("feature:");
355                if (fi.name != null) System.out.println(fi.name);
356                else System.out.println("reqGlEsVersion=0x"
357                        + Integer.toHexString(fi.reqGlEsVersion));
358            }
359        } catch (RemoteException e) {
360            System.err.println(e.toString());
361            System.err.println(PM_NOT_RUNNING_ERR);
362        }
363    }
364
365    /**
366     * Lists all of the libraries supported by the current device.
367     *
368     * pm list libraries
369     */
370    private void runListLibraries() {
371        try {
372            List<String> list = new ArrayList<String>();
373            String[] rawList = mPm.getSystemSharedLibraryNames();
374            for (int i=0; i<rawList.length; i++) {
375                list.add(rawList[i]);
376            }
377
378
379            // Sort by name
380            Collections.sort(list, new Comparator<String>() {
381                public int compare(String o1, String o2) {
382                    if (o1 == o2) return 0;
383                    if (o1 == null) return -1;
384                    if (o2 == null) return 1;
385                    return o1.compareTo(o2);
386                }
387            });
388
389            int count = (list != null) ? list.size() : 0;
390            for (int p = 0; p < count; p++) {
391                String lib = list.get(p);
392                System.out.print("library:");
393                System.out.println(lib);
394            }
395        } catch (RemoteException e) {
396            System.err.println(e.toString());
397            System.err.println(PM_NOT_RUNNING_ERR);
398        }
399    }
400
401    /**
402     * Lists all of the installed instrumentation, or all for a given package
403     *
404     * pm list instrumentation [package] [-f]
405     */
406    private void runListInstrumentation() {
407        int flags = 0;      // flags != 0 is only used to request meta-data
408        boolean showPackage = false;
409        String targetPackage = null;
410
411        try {
412            String opt;
413            while ((opt=nextArg()) != null) {
414                if (opt.equals("-f")) {
415                    showPackage = true;
416                } else if (opt.charAt(0) != '-') {
417                    targetPackage = opt;
418                } else {
419                    System.err.println("Error: Unknown option: " + opt);
420                    return;
421                }
422            }
423        } catch (RuntimeException ex) {
424            System.err.println("Error: " + ex.toString());
425            return;
426        }
427
428        try {
429            List<InstrumentationInfo> list = mPm.queryInstrumentation(targetPackage, flags);
430
431            // Sort by target package
432            Collections.sort(list, new Comparator<InstrumentationInfo>() {
433                public int compare(InstrumentationInfo o1, InstrumentationInfo o2) {
434                    return o1.targetPackage.compareTo(o2.targetPackage);
435                }
436            });
437
438            int count = (list != null) ? list.size() : 0;
439            for (int p = 0; p < count; p++) {
440                InstrumentationInfo ii = list.get(p);
441                System.out.print("instrumentation:");
442                if (showPackage) {
443                    System.out.print(ii.sourceDir);
444                    System.out.print("=");
445                }
446                ComponentName cn = new ComponentName(ii.packageName, ii.name);
447                System.out.print(cn.flattenToShortString());
448                System.out.print(" (target=");
449                System.out.print(ii.targetPackage);
450                System.out.println(")");
451            }
452        } catch (RemoteException e) {
453            System.err.println(e.toString());
454            System.err.println(PM_NOT_RUNNING_ERR);
455        }
456    }
457
458    /**
459     * Lists all the known permission groups.
460     */
461    private void runListPermissionGroups() {
462        try {
463            List<PermissionGroupInfo> pgs = mPm.getAllPermissionGroups(0);
464
465            int count = pgs.size();
466            for (int p = 0 ; p < count ; p++) {
467                PermissionGroupInfo pgi = pgs.get(p);
468                System.out.print("permission group:");
469                System.out.println(pgi.name);
470            }
471        } catch (RemoteException e) {
472            System.err.println(e.toString());
473            System.err.println(PM_NOT_RUNNING_ERR);
474        }
475    }
476
477    private String loadText(PackageItemInfo pii, int res, CharSequence nonLocalized) {
478        if (nonLocalized != null) {
479            return nonLocalized.toString();
480        }
481        if (res != 0) {
482            Resources r = getResources(pii);
483            if (r != null) {
484                return r.getString(res);
485            }
486        }
487        return null;
488    }
489
490    /**
491     * Lists all the permissions in a group.
492     */
493    private void runListPermissions() {
494        try {
495            boolean labels = false;
496            boolean groups = false;
497            boolean userOnly = false;
498            boolean summary = false;
499            boolean dangerousOnly = false;
500            String opt;
501            while ((opt=nextOption()) != null) {
502                if (opt.equals("-f")) {
503                    labels = true;
504                } else if (opt.equals("-g")) {
505                    groups = true;
506                } else if (opt.equals("-s")) {
507                    groups = true;
508                    labels = true;
509                    summary = true;
510                } else if (opt.equals("-u")) {
511                    userOnly = true;
512                } else if (opt.equals("-d")) {
513                    dangerousOnly = true;
514                } else {
515                    System.err.println("Error: Unknown option: " + opt);
516                    return;
517                }
518            }
519
520            String grp = nextOption();
521            ArrayList<String> groupList = new ArrayList<String>();
522            if (groups) {
523                List<PermissionGroupInfo> infos =
524                        mPm.getAllPermissionGroups(0);
525                for (int i=0; i<infos.size(); i++) {
526                    groupList.add(infos.get(i).name);
527                }
528                groupList.add(null);
529            } else {
530                groupList.add(grp);
531            }
532
533            if (dangerousOnly) {
534                System.out.println("Dangerous Permissions:");
535                System.out.println("");
536                doListPermissions(groupList, groups, labels, summary,
537                        PermissionInfo.PROTECTION_DANGEROUS,
538                        PermissionInfo.PROTECTION_DANGEROUS);
539                if (userOnly) {
540                    System.out.println("Normal Permissions:");
541                    System.out.println("");
542                    doListPermissions(groupList, groups, labels, summary,
543                            PermissionInfo.PROTECTION_NORMAL,
544                            PermissionInfo.PROTECTION_NORMAL);
545                }
546            } else if (userOnly) {
547                System.out.println("Dangerous and Normal Permissions:");
548                System.out.println("");
549                doListPermissions(groupList, groups, labels, summary,
550                        PermissionInfo.PROTECTION_NORMAL,
551                        PermissionInfo.PROTECTION_DANGEROUS);
552            } else {
553                System.out.println("All Permissions:");
554                System.out.println("");
555                doListPermissions(groupList, groups, labels, summary,
556                        -10000, 10000);
557            }
558        } catch (RemoteException e) {
559            System.err.println(e.toString());
560            System.err.println(PM_NOT_RUNNING_ERR);
561        }
562    }
563
564    private void doListPermissions(ArrayList<String> groupList,
565            boolean groups, boolean labels, boolean summary,
566            int startProtectionLevel, int endProtectionLevel)
567            throws RemoteException {
568        for (int i=0; i<groupList.size(); i++) {
569            String groupName = groupList.get(i);
570            String prefix = "";
571            if (groups) {
572                if (i > 0) System.out.println("");
573                if (groupName != null) {
574                    PermissionGroupInfo pgi = mPm.getPermissionGroupInfo(
575                            groupName, 0);
576                    if (summary) {
577                        Resources res = getResources(pgi);
578                        if (res != null) {
579                            System.out.print(loadText(pgi, pgi.labelRes,
580                                    pgi.nonLocalizedLabel) + ": ");
581                        } else {
582                            System.out.print(pgi.name + ": ");
583
584                        }
585                    } else {
586                        System.out.println((labels ? "+ " : "")
587                                + "group:" + pgi.name);
588                        if (labels) {
589                            System.out.println("  package:" + pgi.packageName);
590                            Resources res = getResources(pgi);
591                            if (res != null) {
592                                System.out.println("  label:"
593                                        + loadText(pgi, pgi.labelRes,
594                                                pgi.nonLocalizedLabel));
595                                System.out.println("  description:"
596                                        + loadText(pgi, pgi.descriptionRes,
597                                                pgi.nonLocalizedDescription));
598                            }
599                        }
600                    }
601                } else {
602                    System.out.println(((labels && !summary)
603                            ? "+ " : "") + "ungrouped:");
604                }
605                prefix = "  ";
606            }
607            List<PermissionInfo> ps = mPm.queryPermissionsByGroup(
608                    groupList.get(i), 0);
609            int count = ps.size();
610            boolean first = true;
611            for (int p = 0 ; p < count ; p++) {
612                PermissionInfo pi = ps.get(p);
613                if (groups && groupName == null && pi.group != null) {
614                    continue;
615                }
616                final int base = pi.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE;
617                if (base < startProtectionLevel
618                        || base > endProtectionLevel) {
619                    continue;
620                }
621                if (summary) {
622                    if (first) {
623                        first = false;
624                    } else {
625                        System.out.print(", ");
626                    }
627                    Resources res = getResources(pi);
628                    if (res != null) {
629                        System.out.print(loadText(pi, pi.labelRes,
630                                pi.nonLocalizedLabel));
631                    } else {
632                        System.out.print(pi.name);
633                    }
634                } else {
635                    System.out.println(prefix + (labels ? "+ " : "")
636                            + "permission:" + pi.name);
637                    if (labels) {
638                        System.out.println(prefix + "  package:" + pi.packageName);
639                        Resources res = getResources(pi);
640                        if (res != null) {
641                            System.out.println(prefix + "  label:"
642                                    + loadText(pi, pi.labelRes,
643                                            pi.nonLocalizedLabel));
644                            System.out.println(prefix + "  description:"
645                                    + loadText(pi, pi.descriptionRes,
646                                            pi.nonLocalizedDescription));
647                        }
648                        System.out.println(prefix + "  protectionLevel:"
649                                + PermissionInfo.protectionToString(pi.protectionLevel));
650                    }
651                }
652            }
653
654            if (summary) {
655                System.out.println("");
656            }
657        }
658    }
659
660    private void runPath() {
661        String pkg = nextArg();
662        if (pkg == null) {
663            System.err.println("Error: no package specified");
664            return;
665        }
666        displayPackageFilePath(pkg);
667    }
668
669    class PackageInstallObserver extends IPackageInstallObserver.Stub {
670        boolean finished;
671        int result;
672
673        public void packageInstalled(String name, int status) {
674            synchronized( this) {
675                finished = true;
676                result = status;
677                notifyAll();
678            }
679        }
680    }
681
682    /**
683     * Converts a failure code into a string by using reflection to find a matching constant
684     * in PackageManager.
685     */
686    private String installFailureToString(int result) {
687        Field[] fields = PackageManager.class.getFields();
688        for (Field f: fields) {
689            if (f.getType() == int.class) {
690                int modifiers = f.getModifiers();
691                // only look at public final static fields.
692                if (((modifiers & Modifier.FINAL) != 0) &&
693                        ((modifiers & Modifier.PUBLIC) != 0) &&
694                        ((modifiers & Modifier.STATIC) != 0)) {
695                    String fieldName = f.getName();
696                    if (fieldName.startsWith("INSTALL_FAILED_") ||
697                            fieldName.startsWith("INSTALL_PARSE_FAILED_")) {
698                        // get the int value and compare it to result.
699                        try {
700                            if (result == f.getInt(null)) {
701                                return fieldName;
702                            }
703                        } catch (IllegalAccessException e) {
704                            // this shouldn't happen since we only look for public static fields.
705                        }
706                    }
707                }
708            }
709        }
710
711        // couldn't find a matching constant? return the value
712        return Integer.toString(result);
713    }
714
715    private void runSetInstallLocation() {
716        int loc;
717
718        String arg = nextArg();
719        if (arg == null) {
720            System.err.println("Error: no install location specified.");
721            return;
722        }
723        try {
724            loc = Integer.parseInt(arg);
725        } catch (NumberFormatException e) {
726            System.err.println("Error: install location has to be a number.");
727            return;
728        }
729        try {
730            if (!mPm.setInstallLocation(loc)) {
731                System.err.println("Error: install location has to be a number.");
732            }
733        } catch (RemoteException e) {
734            System.err.println(e.toString());
735            System.err.println(PM_NOT_RUNNING_ERR);
736        }
737    }
738
739    private void runGetInstallLocation() {
740        try {
741            int loc = mPm.getInstallLocation();
742            String locStr = "invalid";
743            if (loc == PackageHelper.APP_INSTALL_AUTO) {
744                locStr = "auto";
745            } else if (loc == PackageHelper.APP_INSTALL_INTERNAL) {
746                locStr = "internal";
747            } else if (loc == PackageHelper.APP_INSTALL_EXTERNAL) {
748                locStr = "external";
749            }
750            System.out.println(loc + "[" + locStr + "]");
751        } catch (RemoteException e) {
752            System.err.println(e.toString());
753            System.err.println(PM_NOT_RUNNING_ERR);
754        }
755    }
756
757    private void runInstall() {
758        int installFlags = PackageManager.INSTALL_ALL_USERS;
759        String installerPackageName = null;
760
761        String opt;
762
763        String algo = null;
764        byte[] iv = null;
765        byte[] key = null;
766
767        String macAlgo = null;
768        byte[] macKey = null;
769        byte[] tag = null;
770        String originatingUriString = null;
771        String referrer = null;
772
773        while ((opt=nextOption()) != null) {
774            if (opt.equals("-l")) {
775                installFlags |= PackageManager.INSTALL_FORWARD_LOCK;
776            } else if (opt.equals("-r")) {
777                installFlags |= PackageManager.INSTALL_REPLACE_EXISTING;
778            } else if (opt.equals("-i")) {
779                installerPackageName = nextOptionData();
780                if (installerPackageName == null) {
781                    System.err.println("Error: no value specified for -i");
782                    return;
783                }
784            } else if (opt.equals("-t")) {
785                installFlags |= PackageManager.INSTALL_ALLOW_TEST;
786            } else if (opt.equals("-s")) {
787                // Override if -s option is specified.
788                installFlags |= PackageManager.INSTALL_EXTERNAL;
789            } else if (opt.equals("-f")) {
790                // Override if -s option is specified.
791                installFlags |= PackageManager.INSTALL_INTERNAL;
792            } else if (opt.equals("-d")) {
793                installFlags |= PackageManager.INSTALL_ALLOW_DOWNGRADE;
794            } else if (opt.equals("--algo")) {
795                algo = nextOptionData();
796                if (algo == null) {
797                    System.err.println("Error: must supply argument for --algo");
798                    return;
799                }
800            } else if (opt.equals("--iv")) {
801                iv = hexToBytes(nextOptionData());
802                if (iv == null) {
803                    System.err.println("Error: must supply argument for --iv");
804                    return;
805                }
806            } else if (opt.equals("--key")) {
807                key = hexToBytes(nextOptionData());
808                if (key == null) {
809                    System.err.println("Error: must supply argument for --key");
810                    return;
811                }
812            } else if (opt.equals("--macalgo")) {
813                macAlgo = nextOptionData();
814                if (macAlgo == null) {
815                    System.err.println("Error: must supply argument for --macalgo");
816                    return;
817                }
818            } else if (opt.equals("--mackey")) {
819                macKey = hexToBytes(nextOptionData());
820                if (macKey == null) {
821                    System.err.println("Error: must supply argument for --mackey");
822                    return;
823                }
824            } else if (opt.equals("--tag")) {
825                tag = hexToBytes(nextOptionData());
826                if (tag == null) {
827                    System.err.println("Error: must supply argument for --tag");
828                    return;
829                }
830            } else if (opt.equals("--originating-uri")) {
831                originatingUriString = nextOptionData();
832                if (originatingUriString == null) {
833                    System.err.println("Error: must supply argument for --originating-uri");
834                    return;
835                }
836            } else if (opt.equals("--referrer")) {
837                referrer = nextOptionData();
838                if (referrer == null) {
839                    System.err.println("Error: must supply argument for --referrer");
840                    return;
841                }
842            } else {
843                System.err.println("Error: Unknown option: " + opt);
844                return;
845            }
846        }
847
848        final ContainerEncryptionParams encryptionParams;
849        if (algo != null || iv != null || key != null || macAlgo != null || macKey != null
850                || tag != null) {
851            if (algo == null || iv == null || key == null) {
852                System.err.println("Error: all of --algo, --iv, and --key must be specified");
853                return;
854            }
855
856            if (macAlgo != null || macKey != null || tag != null) {
857                if (macAlgo == null || macKey == null || tag == null) {
858                    System.err.println("Error: all of --macalgo, --mackey, and --tag must "
859                            + "be specified");
860                    return;
861                }
862            }
863
864            try {
865                final SecretKey encKey = new SecretKeySpec(key, "RAW");
866
867                final SecretKey macSecretKey;
868                if (macKey == null || macKey.length == 0) {
869                    macSecretKey = null;
870                } else {
871                    macSecretKey = new SecretKeySpec(macKey, "RAW");
872                }
873
874                encryptionParams = new ContainerEncryptionParams(algo, new IvParameterSpec(iv),
875                        encKey, macAlgo, null, macSecretKey, tag, -1, -1, -1);
876            } catch (InvalidAlgorithmParameterException e) {
877                e.printStackTrace();
878                return;
879            }
880        } else {
881            encryptionParams = null;
882        }
883
884        final Uri apkURI;
885        final Uri verificationURI;
886        final Uri originatingURI;
887        final Uri referrerURI;
888
889        if (originatingUriString != null) {
890            originatingURI = Uri.parse(originatingUriString);
891        } else {
892            originatingURI = null;
893        }
894
895        if (referrer != null) {
896            referrerURI = Uri.parse(referrer);
897        } else {
898            referrerURI = null;
899        }
900
901        // Populate apkURI, must be present
902        final String apkFilePath = nextArg();
903        System.err.println("\tpkg: " + apkFilePath);
904        if (apkFilePath != null) {
905            apkURI = Uri.fromFile(new File(apkFilePath));
906        } else {
907            System.err.println("Error: no package specified");
908            return;
909        }
910
911        // Populate verificationURI, optionally present
912        final String verificationFilePath = nextArg();
913        if (verificationFilePath != null) {
914            System.err.println("\tver: " + verificationFilePath);
915            verificationURI = Uri.fromFile(new File(verificationFilePath));
916        } else {
917            verificationURI = null;
918        }
919
920        PackageInstallObserver obs = new PackageInstallObserver();
921        try {
922            VerificationParams verificationParams = new VerificationParams(verificationURI,
923                    originatingURI, referrerURI, null);
924
925            mPm.installPackageWithVerificationAndEncryption(apkURI, obs, installFlags,
926                    installerPackageName, verificationParams, encryptionParams);
927
928            synchronized (obs) {
929                while (!obs.finished) {
930                    try {
931                        obs.wait();
932                    } catch (InterruptedException e) {
933                    }
934                }
935                if (obs.result == PackageManager.INSTALL_SUCCEEDED) {
936                    System.out.println("Success");
937                } else {
938                    System.err.println("Failure ["
939                            + installFailureToString(obs.result)
940                            + "]");
941                }
942            }
943        } catch (RemoteException e) {
944            System.err.println(e.toString());
945            System.err.println(PM_NOT_RUNNING_ERR);
946        }
947    }
948
949    /**
950     * Convert a string containing hex-encoded bytes to a byte array.
951     *
952     * @param input String containing hex-encoded bytes
953     * @return input as an array of bytes
954     */
955    private byte[] hexToBytes(String input) {
956        if (input == null) {
957            return null;
958        }
959
960        final int inputLength = input.length();
961        if ((inputLength % 2) != 0) {
962            System.err.print("Invalid length; must be multiple of 2");
963            return null;
964        }
965
966        final int byteLength = inputLength / 2;
967        final byte[] output = new byte[byteLength];
968
969        int inputIndex = 0;
970        int byteIndex = 0;
971        while (inputIndex < inputLength) {
972            output[byteIndex++] = (byte) Integer.parseInt(
973                    input.substring(inputIndex, inputIndex + 2), 16);
974            inputIndex += 2;
975        }
976
977        return output;
978    }
979
980    public void runCreateUser() {
981        String name;
982        String arg = nextArg();
983        if (arg == null) {
984            System.err.println("Error: no user name specified.");
985            return;
986        }
987        name = arg;
988        try {
989            if (mUm.createUser(name, 0) == null) {
990                System.err.println("Error: couldn't create User.");
991            }
992        } catch (RemoteException e) {
993            System.err.println(e.toString());
994            System.err.println(PM_NOT_RUNNING_ERR);
995        }
996
997    }
998
999    public void runRemoveUser() {
1000        int userId;
1001        String arg = nextArg();
1002        if (arg == null) {
1003            System.err.println("Error: no user id specified.");
1004            return;
1005        }
1006        try {
1007            userId = Integer.parseInt(arg);
1008        } catch (NumberFormatException e) {
1009            System.err.println("Error: user id '" + arg + "' is not a number.");
1010            return;
1011        }
1012        try {
1013            if (!mUm.removeUser(userId)) {
1014                System.err.println("Error: couldn't remove user #" + userId + ".");
1015            }
1016        } catch (RemoteException e) {
1017            System.err.println(e.toString());
1018            System.err.println(PM_NOT_RUNNING_ERR);
1019        }
1020    }
1021
1022    public void runListUsers() {
1023        try {
1024            List<UserInfo> users = mUm.getUsers();
1025            if (users == null) {
1026                System.err.println("Error: couldn't get users");
1027            } else {
1028                System.out.println("Users:");
1029                for (int i = 0; i < users.size(); i++) {
1030                    System.out.println("\t" + users.get(i).toString());
1031                }
1032            }
1033        } catch (RemoteException e) {
1034            System.err.println(e.toString());
1035            System.err.println(PM_NOT_RUNNING_ERR);
1036        }
1037    }
1038    class PackageDeleteObserver extends IPackageDeleteObserver.Stub {
1039        boolean finished;
1040        boolean result;
1041
1042        public void packageDeleted(String packageName, int returnCode) {
1043            synchronized (this) {
1044                finished = true;
1045                result = returnCode == PackageManager.DELETE_SUCCEEDED;
1046                notifyAll();
1047            }
1048        }
1049    }
1050
1051    private void runUninstall() {
1052        int unInstallFlags = PackageManager.DELETE_ALL_USERS;
1053
1054        String opt;
1055        while ((opt=nextOption()) != null) {
1056            if (opt.equals("-k")) {
1057                unInstallFlags |= PackageManager.DELETE_KEEP_DATA;
1058            } else {
1059                System.err.println("Error: Unknown option: " + opt);
1060                return;
1061            }
1062        }
1063
1064        String pkg = nextArg();
1065        if (pkg == null) {
1066            System.err.println("Error: no package specified");
1067            showUsage();
1068            return;
1069        }
1070        boolean result = deletePackage(pkg, unInstallFlags);
1071        if (result) {
1072            System.out.println("Success");
1073        } else {
1074            System.out.println("Failure");
1075        }
1076    }
1077
1078    private boolean deletePackage(String pkg, int unInstallFlags) {
1079        PackageDeleteObserver obs = new PackageDeleteObserver();
1080        try {
1081            mPm.deletePackage(pkg, obs, unInstallFlags);
1082
1083            synchronized (obs) {
1084                while (!obs.finished) {
1085                    try {
1086                        obs.wait();
1087                    } catch (InterruptedException e) {
1088                    }
1089                }
1090            }
1091        } catch (RemoteException e) {
1092            System.err.println(e.toString());
1093            System.err.println(PM_NOT_RUNNING_ERR);
1094        }
1095        return obs.result;
1096    }
1097
1098    static class ClearDataObserver extends IPackageDataObserver.Stub {
1099        boolean finished;
1100        boolean result;
1101
1102        @Override
1103        public void onRemoveCompleted(String packageName, boolean succeeded) throws RemoteException {
1104            synchronized (this) {
1105                finished = true;
1106                result = succeeded;
1107                notifyAll();
1108            }
1109        }
1110
1111    }
1112
1113    private void runClear() {
1114        String pkg = nextArg();
1115        if (pkg == null) {
1116            System.err.println("Error: no package specified");
1117            showUsage();
1118            return;
1119        }
1120
1121        ClearDataObserver obs = new ClearDataObserver();
1122        try {
1123            // XXX TO DO: add user arg
1124            if (!ActivityManagerNative.getDefault().clearApplicationUserData(pkg, obs, 0)) {
1125                System.err.println("Failed");
1126            }
1127
1128            synchronized (obs) {
1129                while (!obs.finished) {
1130                    try {
1131                        obs.wait();
1132                    } catch (InterruptedException e) {
1133                    }
1134                }
1135            }
1136
1137            if (obs.result) {
1138                System.err.println("Success");
1139            } else {
1140                System.err.println("Failed");
1141            }
1142        } catch (RemoteException e) {
1143            System.err.println(e.toString());
1144            System.err.println(PM_NOT_RUNNING_ERR);
1145        }
1146    }
1147
1148    private static String enabledSettingToString(int state) {
1149        switch (state) {
1150            case PackageManager.COMPONENT_ENABLED_STATE_DEFAULT:
1151                return "default";
1152            case PackageManager.COMPONENT_ENABLED_STATE_ENABLED:
1153                return "enabled";
1154            case PackageManager.COMPONENT_ENABLED_STATE_DISABLED:
1155                return "disabled";
1156            case PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER:
1157                return "disabled-user";
1158        }
1159        return "unknown";
1160    }
1161
1162    private boolean isNumber(String s) {
1163        try {
1164            Integer.parseInt(s);
1165        } catch (NumberFormatException nfe) {
1166            return false;
1167        }
1168        return true;
1169    }
1170
1171    private void runSetEnabledSetting(int state) {
1172        int userId = 0;
1173        String option = nextOption();
1174        if (option != null && option.equals("--user")) {
1175            String optionData = nextOptionData();
1176            if (optionData == null || !isNumber(optionData)) {
1177                System.err.println("Error: no USER_ID specified");
1178                showUsage();
1179                return;
1180            } else {
1181                userId = Integer.parseInt(optionData);
1182            }
1183        }
1184
1185        String pkg = nextArg();
1186        if (pkg == null) {
1187            System.err.println("Error: no package or component specified");
1188            showUsage();
1189            return;
1190        }
1191        ComponentName cn = ComponentName.unflattenFromString(pkg);
1192        if (cn == null) {
1193            try {
1194                mPm.setApplicationEnabledSetting(pkg, state, 0, userId);
1195                System.err.println("Package " + pkg + " new state: "
1196                        + enabledSettingToString(
1197                        mPm.getApplicationEnabledSetting(pkg, userId)));
1198            } catch (RemoteException e) {
1199                System.err.println(e.toString());
1200                System.err.println(PM_NOT_RUNNING_ERR);
1201            }
1202        } else {
1203            try {
1204                mPm.setComponentEnabledSetting(cn, state, 0, userId);
1205                System.err.println("Component " + cn.toShortString() + " new state: "
1206                        + enabledSettingToString(
1207                        mPm.getComponentEnabledSetting(cn, userId)));
1208            } catch (RemoteException e) {
1209                System.err.println(e.toString());
1210                System.err.println(PM_NOT_RUNNING_ERR);
1211            }
1212        }
1213    }
1214
1215    private void runGrantRevokePermission(boolean grant) {
1216        String pkg = nextArg();
1217        if (pkg == null) {
1218            System.err.println("Error: no package specified");
1219            showUsage();
1220            return;
1221        }
1222        String perm = nextArg();
1223        if (perm == null) {
1224            System.err.println("Error: no permission specified");
1225            showUsage();
1226            return;
1227        }
1228        try {
1229            if (grant) {
1230                mPm.grantPermission(pkg, perm);
1231            } else {
1232                mPm.revokePermission(pkg, perm);
1233            }
1234        } catch (RemoteException e) {
1235            System.err.println(e.toString());
1236            System.err.println(PM_NOT_RUNNING_ERR);
1237        } catch (IllegalArgumentException e) {
1238            System.err.println("Bad argument: " + e.toString());
1239            showUsage();
1240        } catch (SecurityException e) {
1241            System.err.println("Operation not allowed: " + e.toString());
1242        }
1243    }
1244
1245    private void runSetPermissionEnforced() {
1246        final String permission = nextArg();
1247        if (permission == null) {
1248            System.err.println("Error: no permission specified");
1249            showUsage();
1250            return;
1251        }
1252        final String enforcedRaw = nextArg();
1253        if (enforcedRaw == null) {
1254            System.err.println("Error: no enforcement specified");
1255            showUsage();
1256            return;
1257        }
1258        final boolean enforced = Boolean.parseBoolean(enforcedRaw);
1259        try {
1260            mPm.setPermissionEnforced(permission, enforced);
1261        } catch (RemoteException e) {
1262            System.err.println(e.toString());
1263            System.err.println(PM_NOT_RUNNING_ERR);
1264        } catch (IllegalArgumentException e) {
1265            System.err.println("Bad argument: " + e.toString());
1266            showUsage();
1267        } catch (SecurityException e) {
1268            System.err.println("Operation not allowed: " + e.toString());
1269        }
1270    }
1271
1272    static class ClearCacheObserver extends IPackageDataObserver.Stub {
1273        boolean finished;
1274        boolean result;
1275
1276        @Override
1277        public void onRemoveCompleted(String packageName, boolean succeeded) throws RemoteException {
1278            synchronized (this) {
1279                finished = true;
1280                result = succeeded;
1281                notifyAll();
1282            }
1283        }
1284
1285    }
1286
1287    private void runTrimCaches() {
1288        String size = nextArg();
1289        if (size == null) {
1290            System.err.println("Error: no size specified");
1291            showUsage();
1292            return;
1293        }
1294        int len = size.length();
1295        long multiplier = 1;
1296        if (len > 1) {
1297            char c = size.charAt(len-1);
1298            if (c == 'K' || c == 'k') {
1299                multiplier = 1024L;
1300            } else if (c == 'M' || c == 'm') {
1301                multiplier = 1024L*1024L;
1302            } else if (c == 'G' || c == 'g') {
1303                multiplier = 1024L*1024L*1024L;
1304            } else {
1305                System.err.println("Invalid suffix: " + c);
1306                showUsage();
1307                return;
1308            }
1309            size = size.substring(0, len-1);
1310        }
1311        long sizeVal;
1312        try {
1313            sizeVal = Long.parseLong(size) * multiplier;
1314        } catch (NumberFormatException e) {
1315            System.err.println("Error: expected number at: " + size);
1316            showUsage();
1317            return;
1318        }
1319        ClearDataObserver obs = new ClearDataObserver();
1320        try {
1321            mPm.freeStorageAndNotify(sizeVal, obs);
1322            synchronized (obs) {
1323                while (!obs.finished) {
1324                    try {
1325                        obs.wait();
1326                    } catch (InterruptedException e) {
1327                    }
1328                }
1329            }
1330        } catch (RemoteException e) {
1331            System.err.println(e.toString());
1332            System.err.println(PM_NOT_RUNNING_ERR);
1333        } catch (IllegalArgumentException e) {
1334            System.err.println("Bad argument: " + e.toString());
1335            showUsage();
1336        } catch (SecurityException e) {
1337            System.err.println("Operation not allowed: " + e.toString());
1338        }
1339    }
1340
1341    /**
1342     * Displays the package file for a package.
1343     * @param pckg
1344     */
1345    private void displayPackageFilePath(String pckg) {
1346        try {
1347            PackageInfo info = mPm.getPackageInfo(pckg, 0, 0);
1348            if (info != null && info.applicationInfo != null) {
1349                System.out.print("package:");
1350                System.out.println(info.applicationInfo.sourceDir);
1351            }
1352        } catch (RemoteException e) {
1353            System.err.println(e.toString());
1354            System.err.println(PM_NOT_RUNNING_ERR);
1355        }
1356    }
1357
1358    private Resources getResources(PackageItemInfo pii) {
1359        Resources res = mResourceCache.get(pii.packageName);
1360        if (res != null) return res;
1361
1362        try {
1363            ApplicationInfo ai = mPm.getApplicationInfo(pii.packageName, 0, 0);
1364            AssetManager am = new AssetManager();
1365            am.addAssetPath(ai.publicSourceDir);
1366            res = new Resources(am, null, null);
1367            mResourceCache.put(pii.packageName, res);
1368            return res;
1369        } catch (RemoteException e) {
1370            System.err.println(e.toString());
1371            System.err.println(PM_NOT_RUNNING_ERR);
1372            return null;
1373        }
1374    }
1375
1376    private String nextOption() {
1377        if (mNextArg >= mArgs.length) {
1378            return null;
1379        }
1380        String arg = mArgs[mNextArg];
1381        if (!arg.startsWith("-")) {
1382            return null;
1383        }
1384        mNextArg++;
1385        if (arg.equals("--")) {
1386            return null;
1387        }
1388        if (arg.length() > 1 && arg.charAt(1) != '-') {
1389            if (arg.length() > 2) {
1390                mCurArgData = arg.substring(2);
1391                return arg.substring(0, 2);
1392            } else {
1393                mCurArgData = null;
1394                return arg;
1395            }
1396        }
1397        mCurArgData = null;
1398        return arg;
1399    }
1400
1401    private String nextOptionData() {
1402        if (mCurArgData != null) {
1403            return mCurArgData;
1404        }
1405        if (mNextArg >= mArgs.length) {
1406            return null;
1407        }
1408        String data = mArgs[mNextArg];
1409        mNextArg++;
1410        return data;
1411    }
1412
1413    private String nextArg() {
1414        if (mNextArg >= mArgs.length) {
1415            return null;
1416        }
1417        String arg = mArgs[mNextArg];
1418        mNextArg++;
1419        return arg;
1420    }
1421
1422    private static void showUsage() {
1423        System.err.println("usage: pm list packages [-f] [-d] [-e] [-s] [-3] [-i] [-u] [FILTER]");
1424        System.err.println("       pm list permission-groups");
1425        System.err.println("       pm list permissions [-g] [-f] [-d] [-u] [GROUP]");
1426        System.err.println("       pm list instrumentation [-f] [TARGET-PACKAGE]");
1427        System.err.println("       pm list features");
1428        System.err.println("       pm list libraries");
1429        System.err.println("       pm list users");
1430        System.err.println("       pm path PACKAGE");
1431        System.err.println("       pm install [-l] [-r] [-t] [-i INSTALLER_PACKAGE_NAME] [-s] [-f]");
1432        System.err.println("                  [--algo <algorithm name> --key <key-in-hex> --iv <IV-in-hex>]");
1433        System.err.println("                  [--originating-uri <URI>] [--referrer <URI>] PATH");
1434        System.err.println("       pm uninstall [-k] PACKAGE");
1435        System.err.println("       pm clear PACKAGE");
1436        System.err.println("       pm enable [--user USER_ID] PACKAGE_OR_COMPONENT");
1437        System.err.println("       pm disable [--user USER_ID] PACKAGE_OR_COMPONENT");
1438        System.err.println("       pm disable-user [--user USER_ID] PACKAGE_OR_COMPONENT");
1439        System.err.println("       pm grant PACKAGE PERMISSION");
1440        System.err.println("       pm revoke PACKAGE PERMISSION");
1441        System.err.println("       pm set-install-location [0/auto] [1/internal] [2/external]");
1442        System.err.println("       pm get-install-location");
1443        System.err.println("       pm set-permission-enforced PERMISSION [true|false]");
1444        System.err.println("       pm trim-caches DESIRED_FREE_SPACE");
1445        System.err.println("       pm create-user USER_NAME");
1446        System.err.println("       pm remove-user USER_ID");
1447        System.err.println("");
1448        System.err.println("pm list packages: prints all packages, optionally only");
1449        System.err.println("  those whose package name contains the text in FILTER.  Options:");
1450        System.err.println("    -f: see their associated file.");
1451        System.err.println("    -d: filter to only show disbled packages.");
1452        System.err.println("    -e: filter to only show enabled packages.");
1453        System.err.println("    -s: filter to only show system packages.");
1454        System.err.println("    -3: filter to only show third party packages.");
1455        System.err.println("    -i: see the installer for the packages.");
1456        System.err.println("    -u: also include uninstalled packages.");
1457        System.err.println("");
1458        System.err.println("pm list permission-groups: prints all known permission groups.");
1459        System.err.println("");
1460        System.err.println("pm list permissions: prints all known permissions, optionally only");
1461        System.err.println("  those in GROUP.  Options:");
1462        System.err.println("    -g: organize by group.");
1463        System.err.println("    -f: print all information.");
1464        System.err.println("    -s: short summary.");
1465        System.err.println("    -d: only list dangerous permissions.");
1466        System.err.println("    -u: list only the permissions users will see.");
1467        System.err.println("");
1468        System.err.println("pm list instrumentation: use to list all test packages; optionally");
1469        System.err.println("  supply <TARGET-PACKAGE> to list the test packages for a particular");
1470        System.err.println("  application.  Options:");
1471        System.err.println("    -f: list the .apk file for the test package.");
1472        System.err.println("");
1473        System.err.println("pm list features: prints all features of the system.");
1474        System.err.println("");
1475        System.err.println("pm list users: prints all users on the system.");
1476        System.err.println("");
1477        System.err.println("pm path: print the path to the .apk of the given PACKAGE.");
1478        System.err.println("");
1479        System.err.println("pm install: installs a package to the system.  Options:");
1480        System.err.println("    -l: install the package with FORWARD_LOCK.");
1481        System.err.println("    -r: reinstall an exisiting app, keeping its data.");
1482        System.err.println("    -t: allow test .apks to be installed.");
1483        System.err.println("    -i: specify the installer package name.");
1484        System.err.println("    -s: install package on sdcard.");
1485        System.err.println("    -f: install package on internal flash.");
1486        System.err.println("    -d: allow version code downgrade.");
1487        System.err.println("");
1488        System.err.println("pm uninstall: removes a package from the system. Options:");
1489        System.err.println("    -k: keep the data and cache directories around after package removal.");
1490        System.err.println("");
1491        System.err.println("pm clear: deletes all data associated with a package.");
1492        System.err.println("");
1493        System.err.println("pm enable, disable, disable-user: these commands change the enabled state");
1494        System.err.println("  of a given package or component (written as \"package/class\").");
1495        System.err.println("");
1496        System.err.println("pm grant, revoke: these commands either grant or revoke permissions");
1497        System.err.println("  to applications.  Only optional permissions the application has");
1498        System.err.println("  declared can be granted or revoked.");
1499        System.err.println("");
1500        System.err.println("pm get-install-location: returns the current install location.");
1501        System.err.println("    0 [auto]: Let system decide the best location");
1502        System.err.println("    1 [internal]: Install on internal device storage");
1503        System.err.println("    2 [external]: Install on external media");
1504        System.err.println("");
1505        System.err.println("pm set-install-location: changes the default install location.");
1506        System.err.println("  NOTE: this is only intended for debugging; using this can cause");
1507        System.err.println("  applications to break and other undersireable behavior.");
1508        System.err.println("    0 [auto]: Let system decide the best location");
1509        System.err.println("    1 [internal]: Install on internal device storage");
1510        System.err.println("    2 [external]: Install on external media");
1511        System.err.println("");
1512        System.err.println("pm trim-caches: trim cache files to reach the given free space.");
1513        System.err.println("");
1514        System.err.println("pm create-user: create a new user with the given USER_NAME,");
1515        System.err.println("  printing the new user identifier of the user.");
1516        System.err.println("");
1517        System.err.println("pm remove-user: remove the user with the given USER_IDENTIFIER,");
1518        System.err.println("  deleting all data associated with that user");
1519    }
1520}
1521