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