PackageManagerShellCommand.java revision ec059d839de6d061085dcfd85ce6565dcf4d5b69
1/*
2 * Copyright (C) 2015 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.server.pm;
18
19import android.app.ActivityManager;
20import android.content.ComponentName;
21import android.content.IIntentReceiver;
22import android.content.IIntentSender;
23import android.content.Intent;
24import android.content.IntentSender;
25import android.content.pm.ApplicationInfo;
26import android.content.pm.FeatureInfo;
27import android.content.pm.IPackageManager;
28import android.content.pm.InstrumentationInfo;
29import android.content.pm.PackageInfo;
30import android.content.pm.PackageInstaller;
31import android.content.pm.PackageItemInfo;
32import android.content.pm.PackageManager;
33import android.content.pm.ParceledListSlice;
34import android.content.pm.PermissionGroupInfo;
35import android.content.pm.PermissionInfo;
36import android.content.pm.PackageInstaller.SessionInfo;
37import android.content.pm.PackageInstaller.SessionParams;
38import android.content.res.AssetManager;
39import android.content.res.Resources;
40import android.net.Uri;
41import android.os.Binder;
42import android.os.Build;
43import android.os.Bundle;
44import android.os.RemoteException;
45import android.os.ShellCommand;
46import android.os.UserHandle;
47import android.text.TextUtils;
48
49import com.android.internal.util.SizedInputStream;
50
51import libcore.io.IoUtils;
52
53import java.io.File;
54import java.io.FileInputStream;
55import java.io.IOException;
56import java.io.InputStream;
57import java.io.OutputStream;
58import java.io.PrintWriter;
59import java.util.ArrayList;
60import java.util.Collections;
61import java.util.Comparator;
62import java.util.List;
63import java.util.WeakHashMap;
64import java.util.concurrent.SynchronousQueue;
65import java.util.concurrent.TimeUnit;
66
67class PackageManagerShellCommand extends ShellCommand {
68    final IPackageManager mInterface;
69    final private WeakHashMap<String, Resources> mResourceCache =
70            new WeakHashMap<String, Resources>();
71
72    PackageManagerShellCommand(PackageManagerService service) {
73        mInterface = service;
74    }
75
76    @Override
77    public int onCommand(String cmd) {
78        if (cmd == null) {
79            return handleDefaultCommands(cmd);
80        }
81
82        final PrintWriter pw = getOutPrintWriter();
83        try {
84            switch(cmd) {
85                case "install":
86                    return runInstall();
87                case "install-abandon":
88                case "install-destroy":
89                    return runInstallAbandon();
90                case "install-commit":
91                    return runInstallCommit();
92                case "install-create":
93                    return runInstallCreate();
94                case "install-write":
95                    return runInstallWrite();
96                case "list":
97                    return runList();
98                case "uninstall":
99                    return runUninstall();
100                default:
101                    return handleDefaultCommands(cmd);
102            }
103        } catch (RemoteException e) {
104            pw.println("Remote exception: " + e);
105        }
106        return -1;
107    }
108
109    private int runInstall() throws RemoteException {
110        final PrintWriter pw = getOutPrintWriter();
111        final InstallParams params = makeInstallParams();
112        final int sessionId = doCreateSession(params.sessionParams,
113                params.installerPackageName, params.userId);
114
115        final String inPath = getNextArg();
116        if (inPath == null && params.sessionParams.sizeBytes == 0) {
117            pw.println("Error: must either specify a package size or an APK file");
118            return 1;
119        }
120        if (doWriteSession(sessionId, inPath, params.sessionParams.sizeBytes, "base.apk") != 0) {
121            return 1;
122        }
123        if (doCommitSession(sessionId) != 0) {
124            return 1;
125        }
126        return 0;
127    }
128
129    private int runInstallAbandon() throws RemoteException {
130        final int sessionId = Integer.parseInt(getNextArg());
131        return doAbandonSession(sessionId);
132    }
133
134    private int runInstallCommit() throws RemoteException {
135        final int sessionId = Integer.parseInt(getNextArg());
136        return doCommitSession(sessionId);
137    }
138
139    private int runInstallCreate() throws RemoteException {
140        final PrintWriter pw = getOutPrintWriter();
141        final InstallParams installParams = makeInstallParams();
142        final int sessionId = doCreateSession(installParams.sessionParams,
143                installParams.installerPackageName, installParams.userId);
144
145        // NOTE: adb depends on parsing this string
146        pw.println("Success: created install session [" + sessionId + "]");
147        return 0;
148    }
149
150    private int runInstallWrite() throws RemoteException {
151        long sizeBytes = -1;
152
153        String opt;
154        while ((opt = getNextOption()) != null) {
155            if (opt.equals("-S")) {
156                sizeBytes = Long.parseLong(getNextArg());
157            } else {
158                throw new IllegalArgumentException("Unknown option: " + opt);
159            }
160        }
161
162        final int sessionId = Integer.parseInt(getNextArg());
163        final String splitName = getNextArg();
164        final String path = getNextArg();
165        return doWriteSession(sessionId, path, sizeBytes, splitName);
166    }
167
168    private int runList() throws RemoteException {
169        final PrintWriter pw = getOutPrintWriter();
170        final String type = getNextArg();
171        if (type == null) {
172            pw.println("Error: didn't specify type of data to list");
173            return -1;
174        }
175        switch(type) {
176            case "features":
177                return runListFeatures();
178            case "instrumentation":
179                return runListInstrumentation();
180            case "libraries":
181                return runListLibraries();
182            case "package":
183            case "packages":
184                return runListPackages(false /*showSourceDir*/);
185            case "permission-groups":
186                return runListPermissionGroups();
187            case "permissions":
188                return runListPermissions();
189        }
190        pw.println("Error: unknown list type '" + type + "'");
191        return -1;
192    }
193
194    private int runListFeatures() throws RemoteException {
195        final PrintWriter pw = getOutPrintWriter();
196        final List<FeatureInfo> list = new ArrayList<FeatureInfo>();
197        final FeatureInfo[] rawList = mInterface.getSystemAvailableFeatures();
198        for (int i=0; i<rawList.length; i++) {
199            list.add(rawList[i]);
200        }
201
202        // sort by name
203        Collections.sort(list, new Comparator<FeatureInfo>() {
204            public int compare(FeatureInfo o1, FeatureInfo o2) {
205                if (o1.name == o2.name) return 0;
206                if (o1.name == null) return -1;
207                if (o2.name == null) return 1;
208                return o1.name.compareTo(o2.name);
209            }
210        });
211
212        final int count = (list != null) ? list.size() : 0;
213        for (int p = 0; p < count; p++) {
214            FeatureInfo fi = list.get(p);
215            pw.print("feature:");
216            if (fi.name != null) pw.println(fi.name);
217            else pw.println("reqGlEsVersion=0x"
218                    + Integer.toHexString(fi.reqGlEsVersion));
219        }
220        return 0;
221    }
222
223    private int runListInstrumentation() throws RemoteException {
224        final PrintWriter pw = getOutPrintWriter();
225        boolean showSourceDir = false;
226        String targetPackage = null;
227
228        try {
229            String opt;
230            while ((opt = getNextArg()) != null) {
231                switch (opt) {
232                    case "-f":
233                        showSourceDir = true;
234                        break;
235                    default:
236                        if (opt.charAt(0) != '-') {
237                            targetPackage = opt;
238                        } else {
239                            pw.println("Error: Unknown option: " + opt);
240                            return -1;
241                        }
242                        break;
243                }
244            }
245        } catch (RuntimeException ex) {
246            pw.println("Error: " + ex.toString());
247            return -1;
248        }
249
250        final List<InstrumentationInfo> list =
251                mInterface.queryInstrumentation(targetPackage, 0 /*flags*/);
252
253        // sort by target package
254        Collections.sort(list, new Comparator<InstrumentationInfo>() {
255            public int compare(InstrumentationInfo o1, InstrumentationInfo o2) {
256                return o1.targetPackage.compareTo(o2.targetPackage);
257            }
258        });
259
260        final int count = (list != null) ? list.size() : 0;
261        for (int p = 0; p < count; p++) {
262            final InstrumentationInfo ii = list.get(p);
263            pw.print("instrumentation:");
264            if (showSourceDir) {
265                pw.print(ii.sourceDir);
266                pw.print("=");
267            }
268            final ComponentName cn = new ComponentName(ii.packageName, ii.name);
269            pw.print(cn.flattenToShortString());
270            pw.print(" (target=");
271            pw.print(ii.targetPackage);
272            pw.println(")");
273        }
274        return 0;
275    }
276
277    private int runListLibraries() throws RemoteException {
278        final PrintWriter pw = getOutPrintWriter();
279        final List<String> list = new ArrayList<String>();
280        final String[] rawList = mInterface.getSystemSharedLibraryNames();
281        for (int i = 0; i < rawList.length; i++) {
282            list.add(rawList[i]);
283        }
284
285        // sort by name
286        Collections.sort(list, new Comparator<String>() {
287            public int compare(String o1, String o2) {
288                if (o1 == o2) return 0;
289                if (o1 == null) return -1;
290                if (o2 == null) return 1;
291                return o1.compareTo(o2);
292            }
293        });
294
295        final int count = (list != null) ? list.size() : 0;
296        for (int p = 0; p < count; p++) {
297            String lib = list.get(p);
298            pw.print("library:");
299            pw.println(lib);
300        }
301        return 0;
302    }
303
304    private int runListPackages(boolean showSourceDir) throws RemoteException {
305        final PrintWriter pw = getOutPrintWriter();
306        int getFlags = 0;
307        boolean listDisabled = false, listEnabled = false;
308        boolean listSystem = false, listThirdParty = false;
309        boolean listInstaller = false;
310        int userId = UserHandle.USER_SYSTEM;
311        try {
312            String opt;
313            while ((opt = getNextOption()) != null) {
314                switch (opt) {
315                    case "-d":
316                        listDisabled = true;
317                        break;
318                    case "-e":
319                        listEnabled = true;
320                        break;
321                    case "-f":
322                        showSourceDir = true;
323                        break;
324                    case "-i":
325                        listInstaller = true;
326                        break;
327                    case "-l":
328                        // old compat
329                        break;
330                    case "-lf":
331                        showSourceDir = true;
332                        break;
333                    case "-s":
334                        listSystem = true;
335                        break;
336                    case "-u":
337                        getFlags |= PackageManager.GET_UNINSTALLED_PACKAGES;
338                        break;
339                    case "-3":
340                        listThirdParty = true;
341                        break;
342                    case "--user":
343                        userId = Integer.parseInt(getNextArg());
344                        break;
345                    default:
346                        pw.println("Error: Unknown option: " + opt);
347                        return -1;
348                }
349            }
350        } catch (RuntimeException ex) {
351            pw.println("Error: " + ex.toString());
352            return -1;
353        }
354
355        final String filter = getNextArg();
356
357        @SuppressWarnings("unchecked")
358        final ParceledListSlice<PackageInfo> slice =
359                mInterface.getInstalledPackages(getFlags, userId);
360        final List<PackageInfo> packages = slice.getList();
361
362        final int count = packages.size();
363        for (int p = 0; p < count; p++) {
364            final PackageInfo info = packages.get(p);
365            if (filter != null && !info.packageName.contains(filter)) {
366                continue;
367            }
368            final boolean isSystem =
369                    (info.applicationInfo.flags&ApplicationInfo.FLAG_SYSTEM) != 0;
370            if ((!listDisabled || !info.applicationInfo.enabled) &&
371                    (!listEnabled || info.applicationInfo.enabled) &&
372                    (!listSystem || isSystem) &&
373                    (!listThirdParty || !isSystem)) {
374                pw.print("package:");
375                if (showSourceDir) {
376                    pw.print(info.applicationInfo.sourceDir);
377                    pw.print("=");
378                }
379                pw.print(info.packageName);
380                if (listInstaller) {
381                    pw.print("  installer=");
382                    pw.print(mInterface.getInstallerPackageName(info.packageName));
383                }
384                pw.println();
385            }
386        }
387        return 0;
388    }
389
390    private int runListPermissionGroups() throws RemoteException {
391        final PrintWriter pw = getOutPrintWriter();
392        final List<PermissionGroupInfo> pgs = mInterface.getAllPermissionGroups(0);
393
394        final int count = pgs.size();
395        for (int p = 0; p < count ; p++) {
396            final PermissionGroupInfo pgi = pgs.get(p);
397            pw.print("permission group:");
398            pw.println(pgi.name);
399        }
400        return 0;
401    }
402
403    private int runListPermissions() throws RemoteException {
404        final PrintWriter pw = getOutPrintWriter();
405        boolean labels = false;
406        boolean groups = false;
407        boolean userOnly = false;
408        boolean summary = false;
409        boolean dangerousOnly = false;
410        String opt;
411        while ((opt = getNextOption()) != null) {
412            switch (opt) {
413                case "-d":
414                    dangerousOnly = true;
415                    break;
416                case "-f":
417                    labels = true;
418                    break;
419                case "-g":
420                    groups = true;
421                    break;
422                case "-s":
423                    groups = true;
424                    labels = true;
425                    summary = true;
426                    break;
427                case "-u":
428                    userOnly = true;
429                    break;
430                default:
431                    pw.println("Error: Unknown option: " + opt);
432                    return 1;
433            }
434        }
435
436        final ArrayList<String> groupList = new ArrayList<String>();
437        if (groups) {
438            final List<PermissionGroupInfo> infos =
439                    mInterface.getAllPermissionGroups(0 /*flags*/);
440            final int count = infos.size();
441            for (int i = 0; i < count; i++) {
442                groupList.add(infos.get(i).name);
443            }
444            groupList.add(null);
445        } else {
446            final String grp = getNextArg();
447            groupList.add(grp);
448        }
449
450        if (dangerousOnly) {
451            pw.println("Dangerous Permissions:");
452            pw.println("");
453            doListPermissions(groupList, groups, labels, summary,
454                    PermissionInfo.PROTECTION_DANGEROUS,
455                    PermissionInfo.PROTECTION_DANGEROUS);
456            if (userOnly) {
457                pw.println("Normal Permissions:");
458                pw.println("");
459                doListPermissions(groupList, groups, labels, summary,
460                        PermissionInfo.PROTECTION_NORMAL,
461                        PermissionInfo.PROTECTION_NORMAL);
462            }
463        } else if (userOnly) {
464            pw.println("Dangerous and Normal Permissions:");
465            pw.println("");
466            doListPermissions(groupList, groups, labels, summary,
467                    PermissionInfo.PROTECTION_NORMAL,
468                    PermissionInfo.PROTECTION_DANGEROUS);
469        } else {
470            pw.println("All Permissions:");
471            pw.println("");
472            doListPermissions(groupList, groups, labels, summary,
473                    -10000, 10000);
474        }
475        return 0;
476    }
477
478    private int runUninstall() throws RemoteException {
479        final PrintWriter pw = getOutPrintWriter();
480        int flags = 0;
481        int userId = UserHandle.USER_ALL;
482
483        String opt;
484        while ((opt = getNextOption()) != null) {
485            switch (opt) {
486                case "-k":
487                    flags |= PackageManager.DELETE_KEEP_DATA;
488                    break;
489                case "--user":
490                    userId = Integer.parseInt(getNextArg());
491                    break;
492                default:
493                    pw.println("Error: Unknown option: " + opt);
494                    return 1;
495            }
496        }
497
498        String packageName = getNextArg();
499        if (packageName == null) {
500            pw.println("Error: package name not specified");
501            return 1;
502        }
503
504        userId = translateUserId(userId, "runUninstall");
505        if (userId == UserHandle.USER_ALL) {
506            userId = UserHandle.USER_SYSTEM;
507            flags |= PackageManager.DELETE_ALL_USERS;
508        } else {
509            final PackageInfo info = mInterface.getPackageInfo(packageName, 0, userId);
510            if (info == null) {
511                pw.println("Failure - not installed for " + userId);
512                return 1;
513            }
514            final boolean isSystem =
515                    (info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
516            // If we are being asked to delete a system app for just one
517            // user set flag so it disables rather than reverting to system
518            // version of the app.
519            if (isSystem) {
520                flags |= PackageManager.DELETE_SYSTEM_APP;
521            }
522        }
523
524        final LocalIntentReceiver receiver = new LocalIntentReceiver();
525        mInterface.getPackageInstaller().uninstall(packageName, null /*callerPackageName*/, flags,
526                receiver.getIntentSender(), userId);
527
528        final Intent result = receiver.getResult();
529        final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
530                PackageInstaller.STATUS_FAILURE);
531        if (status == PackageInstaller.STATUS_SUCCESS) {
532            pw.println("Success");
533            return 0;
534        } else {
535            pw.println("Failure ["
536                    + result.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE) + "]");
537            return 1;
538        }
539    }
540
541    private static class InstallParams {
542        SessionParams sessionParams;
543        String installerPackageName;
544        int userId = UserHandle.USER_ALL;
545    }
546
547    private InstallParams makeInstallParams() {
548        final SessionParams sessionParams = new SessionParams(SessionParams.MODE_FULL_INSTALL);
549        final InstallParams params = new InstallParams();
550        params.sessionParams = sessionParams;
551        String opt;
552        while ((opt = getNextOption()) != null) {
553            switch (opt) {
554                case "-l":
555                    sessionParams.installFlags |= PackageManager.INSTALL_FORWARD_LOCK;
556                    break;
557                case "-r":
558                    sessionParams.installFlags |= PackageManager.INSTALL_REPLACE_EXISTING;
559                    break;
560                case "-i":
561                    params.installerPackageName = getNextArg();
562                    if (params.installerPackageName == null) {
563                        throw new IllegalArgumentException("Missing installer package");
564                    }
565                    break;
566                case "-t":
567                    sessionParams.installFlags |= PackageManager.INSTALL_ALLOW_TEST;
568                    break;
569                case "-s":
570                    sessionParams.installFlags |= PackageManager.INSTALL_EXTERNAL;
571                    break;
572                case "-f":
573                    sessionParams.installFlags |= PackageManager.INSTALL_INTERNAL;
574                    break;
575                case "-d":
576                    sessionParams.installFlags |= PackageManager.INSTALL_ALLOW_DOWNGRADE;
577                    break;
578                case "-g":
579                    sessionParams.installFlags |= PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS;
580                    break;
581                case "--originating-uri":
582                    sessionParams.originatingUri = Uri.parse(getNextArg());
583                    break;
584                case "--referrer":
585                    sessionParams.referrerUri = Uri.parse(getNextArg());
586                    break;
587                case "-p":
588                    sessionParams.mode = SessionParams.MODE_INHERIT_EXISTING;
589                    sessionParams.appPackageName = getNextArg();
590                    if (sessionParams.appPackageName == null) {
591                        throw new IllegalArgumentException("Missing inherit package name");
592                    }
593                    break;
594                case "-S":
595                    sessionParams.setSize(Long.parseLong(getNextArg()));
596                    break;
597                case "--abi":
598                    sessionParams.abiOverride = checkAbiArgument(getNextArg());
599                    break;
600                case "--user":
601                    params.userId = Integer.parseInt(getNextArg());
602                    break;
603                case "--install-location":
604                    sessionParams.installLocation = Integer.parseInt(getNextArg());
605                    break;
606                case "--force-uuid":
607                    sessionParams.installFlags |= PackageManager.INSTALL_FORCE_VOLUME_UUID;
608                    sessionParams.volumeUuid = getNextArg();
609                    if ("internal".equals(sessionParams.volumeUuid)) {
610                        sessionParams.volumeUuid = null;
611                    }
612                    break;
613                default:
614                    throw new IllegalArgumentException("Unknown option " + opt);
615            }
616        }
617        return params;
618    }
619
620    private static String checkAbiArgument(String abi) {
621        if (TextUtils.isEmpty(abi)) {
622            throw new IllegalArgumentException("Missing ABI argument");
623        }
624
625        if ("-".equals(abi)) {
626            return abi;
627        }
628
629        final String[] supportedAbis = Build.SUPPORTED_ABIS;
630        for (String supportedAbi : supportedAbis) {
631            if (supportedAbi.equals(abi)) {
632                return abi;
633            }
634        }
635
636        throw new IllegalArgumentException("ABI " + abi + " not supported on this device");
637    }
638
639    private int translateUserId(int userId, String logContext) {
640        return ActivityManager.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
641                userId, true, true, logContext, "pm command");
642    }
643
644    private int doCreateSession(SessionParams params, String installerPackageName, int userId)
645            throws RemoteException {
646        userId = translateUserId(userId, "runInstallCreate");
647        if (userId == UserHandle.USER_ALL) {
648            userId = UserHandle.USER_SYSTEM;
649            params.installFlags |= PackageManager.INSTALL_ALL_USERS;
650        }
651
652        final int sessionId = mInterface.getPackageInstaller()
653                .createSession(params, installerPackageName, userId);
654        return sessionId;
655    }
656
657    private int doWriteSession(int sessionId, String inPath, long sizeBytes, String splitName)
658            throws RemoteException {
659        final PrintWriter pw = getOutPrintWriter();
660        if ("-".equals(inPath)) {
661            inPath = null;
662        } else if (inPath != null) {
663            final File file = new File(inPath);
664            if (file.isFile()) {
665                sizeBytes = file.length();
666            }
667        }
668
669        final SessionInfo info = mInterface.getPackageInstaller().getSessionInfo(sessionId);
670
671        PackageInstaller.Session session = null;
672        InputStream in = null;
673        OutputStream out = null;
674        try {
675            session = new PackageInstaller.Session(
676                    mInterface.getPackageInstaller().openSession(sessionId));
677
678            if (inPath != null) {
679                in = new FileInputStream(inPath);
680            } else {
681                in = new SizedInputStream(getInputStream(), sizeBytes);
682            }
683            out = session.openWrite(splitName, 0, sizeBytes);
684
685            int total = 0;
686            byte[] buffer = new byte[65536];
687            int c;
688            while ((c = in.read(buffer)) != -1) {
689                total += c;
690                out.write(buffer, 0, c);
691
692                if (info.sizeBytes > 0) {
693                    final float fraction = ((float) c / (float) info.sizeBytes);
694                    session.addProgress(fraction);
695                }
696            }
697            session.fsync(out);
698
699            pw.println("Success: streamed " + total + " bytes");
700            return 0;
701        } catch (IOException e) {
702            pw.println("Error: failed to write; " + e.getMessage());
703            return 1;
704        } finally {
705            IoUtils.closeQuietly(out);
706            IoUtils.closeQuietly(in);
707            IoUtils.closeQuietly(session);
708        }
709    }
710
711    private int doCommitSession(int sessionId) throws RemoteException {
712        final PrintWriter pw = getOutPrintWriter();
713        PackageInstaller.Session session = null;
714        try {
715            session = new PackageInstaller.Session(
716                    mInterface.getPackageInstaller().openSession(sessionId));
717
718            final LocalIntentReceiver receiver = new LocalIntentReceiver();
719            session.commit(receiver.getIntentSender());
720
721            final Intent result = receiver.getResult();
722            final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
723                    PackageInstaller.STATUS_FAILURE);
724            if (status == PackageInstaller.STATUS_SUCCESS) {
725                pw.println("Success");
726            } else {
727                pw.println("Failure ["
728                        + result.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE) + "]");
729                pw.println("Failure details: " + result.getExtras());
730            }
731            return status;
732        } finally {
733            IoUtils.closeQuietly(session);
734        }
735    }
736
737    private int doAbandonSession(int sessionId) throws RemoteException {
738        final PrintWriter pw = getOutPrintWriter();
739        PackageInstaller.Session session = null;
740        try {
741            session = new PackageInstaller.Session(
742                    mInterface.getPackageInstaller().openSession(sessionId));
743            session.abandon();
744            pw.println("Success");
745            return 0;
746        } finally {
747            IoUtils.closeQuietly(session);
748        }
749    }
750
751    private void doListPermissions(ArrayList<String> groupList, boolean groups, boolean labels,
752            boolean summary, int startProtectionLevel, int endProtectionLevel)
753                    throws RemoteException {
754        final PrintWriter pw = getOutPrintWriter();
755        final int groupCount = groupList.size();
756        for (int i = 0; i < groupCount; i++) {
757            String groupName = groupList.get(i);
758            String prefix = "";
759            if (groups) {
760                if (i > 0) {
761                    pw.println("");
762                }
763                if (groupName != null) {
764                    PermissionGroupInfo pgi =
765                            mInterface.getPermissionGroupInfo(groupName, 0 /*flags*/);
766                    if (summary) {
767                        Resources res = getResources(pgi);
768                        if (res != null) {
769                            pw.print(loadText(pgi, pgi.labelRes, pgi.nonLocalizedLabel) + ": ");
770                        } else {
771                            pw.print(pgi.name + ": ");
772
773                        }
774                    } else {
775                        pw.println((labels ? "+ " : "") + "group:" + pgi.name);
776                        if (labels) {
777                            pw.println("  package:" + pgi.packageName);
778                            Resources res = getResources(pgi);
779                            if (res != null) {
780                                pw.println("  label:"
781                                        + loadText(pgi, pgi.labelRes, pgi.nonLocalizedLabel));
782                                pw.println("  description:"
783                                        + loadText(pgi, pgi.descriptionRes,
784                                                pgi.nonLocalizedDescription));
785                            }
786                        }
787                    }
788                } else {
789                    pw.println(((labels && !summary) ? "+ " : "") + "ungrouped:");
790                }
791                prefix = "  ";
792            }
793            List<PermissionInfo> ps =
794                    mInterface.queryPermissionsByGroup(groupList.get(i), 0 /*flags*/);
795            final int count = ps.size();
796            boolean first = true;
797            for (int p = 0 ; p < count ; p++) {
798                PermissionInfo pi = ps.get(p);
799                if (groups && groupName == null && pi.group != null) {
800                    continue;
801                }
802                final int base = pi.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE;
803                if (base < startProtectionLevel
804                        || base > endProtectionLevel) {
805                    continue;
806                }
807                if (summary) {
808                    if (first) {
809                        first = false;
810                    } else {
811                        pw.print(", ");
812                    }
813                    Resources res = getResources(pi);
814                    if (res != null) {
815                        pw.print(loadText(pi, pi.labelRes,
816                                pi.nonLocalizedLabel));
817                    } else {
818                        pw.print(pi.name);
819                    }
820                } else {
821                    pw.println(prefix + (labels ? "+ " : "")
822                            + "permission:" + pi.name);
823                    if (labels) {
824                        pw.println(prefix + "  package:" + pi.packageName);
825                        Resources res = getResources(pi);
826                        if (res != null) {
827                            pw.println(prefix + "  label:"
828                                    + loadText(pi, pi.labelRes,
829                                            pi.nonLocalizedLabel));
830                            pw.println(prefix + "  description:"
831                                    + loadText(pi, pi.descriptionRes,
832                                            pi.nonLocalizedDescription));
833                        }
834                        pw.println(prefix + "  protectionLevel:"
835                                + PermissionInfo.protectionToString(pi.protectionLevel));
836                    }
837                }
838            }
839
840            if (summary) {
841                pw.println("");
842            }
843        }
844    }
845
846    private String loadText(PackageItemInfo pii, int res, CharSequence nonLocalized)
847            throws RemoteException {
848        if (nonLocalized != null) {
849            return nonLocalized.toString();
850        }
851        if (res != 0) {
852            Resources r = getResources(pii);
853            if (r != null) {
854                try {
855                    return r.getString(res);
856                } catch (Resources.NotFoundException e) {
857                }
858            }
859        }
860        return null;
861    }
862
863    private Resources getResources(PackageItemInfo pii) throws RemoteException {
864        Resources res = mResourceCache.get(pii.packageName);
865        if (res != null) return res;
866
867        ApplicationInfo ai = mInterface.getApplicationInfo(pii.packageName, 0, 0);
868        AssetManager am = new AssetManager();
869        am.addAssetPath(ai.publicSourceDir);
870        res = new Resources(am, null, null);
871        mResourceCache.put(pii.packageName, res);
872        return res;
873    }
874
875    @Override
876    public void onHelp() {
877        final PrintWriter pw = getOutPrintWriter();
878        pw.println("Package manager (package) commands:");
879        pw.println("  help");
880        pw.println("    Print this help text.");
881        pw.println("");
882        pw.println("  list features");
883        pw.println("    Prints all features of the system.");
884        pw.println("  list instrumentation [-f] [TARGET-PACKAGE]");
885        pw.println("    Prints all test packages; optionally only those targetting TARGET-PACKAGE");
886        pw.println("    Options:");
887        pw.println("      -f: dump the name of the .apk file containing the test package");
888        pw.println("  list libraries");
889        pw.println("    Prints all system libraries.");
890        pw.println("  list packages [-f] [-d] [-e] [-s] [-3] [-i] [-u] [--user USER_ID] [FILTER]");
891        pw.println("    Prints all packages; optionally only those whose name contains");
892        pw.println("    the text in FILTER.");
893        pw.println("    Options:");
894        pw.println("      -f: see their associated file");
895        pw.println("      -d: filter to only show disbled packages");
896        pw.println("      -e: filter to only show enabled packages");
897        pw.println("      -s: filter to only show system packages");
898        pw.println("      -3: filter to only show third party packages");
899        pw.println("      -i: see the installer for the packages");
900        pw.println("      -u: also include uninstalled packages");
901        pw.println("  list permission-groups");
902        pw.println("    Prints all known permission groups.");
903        pw.println("  list permissions [-g] [-f] [-d] [-u] [GROUP]");
904        pw.println("    Prints all known permissions; optionally only those in GROUP.");
905        pw.println("    Options:");
906        pw.println("      -g: organize by group");
907        pw.println("      -f: print all information");
908        pw.println("      -s: short summary");
909        pw.println("      -d: only list dangerous permissions");
910        pw.println("      -u: list only the permissions users will see");
911        pw.println("");
912    }
913
914    private static class LocalIntentReceiver {
915        private final SynchronousQueue<Intent> mResult = new SynchronousQueue<>();
916
917        private IIntentSender.Stub mLocalSender = new IIntentSender.Stub() {
918            @Override
919            public int send(int code, Intent intent, String resolvedType,
920                    IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) {
921                try {
922                    mResult.offer(intent, 5, TimeUnit.SECONDS);
923                } catch (InterruptedException e) {
924                    throw new RuntimeException(e);
925                }
926                return 0;
927            }
928        };
929
930        public IntentSender getIntentSender() {
931            return new IntentSender((IIntentSender) mLocalSender);
932        }
933
934        public Intent getResult() {
935            try {
936                return mResult.take();
937            } catch (InterruptedException e) {
938                throw new RuntimeException(e);
939            }
940        }
941    }
942}
943
944