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 android.content.pm;
18
19import android.Manifest;
20import android.app.AppGlobals;
21import android.content.Intent;
22import android.os.RemoteException;
23import android.os.UserHandle;
24import android.util.ArraySet;
25import android.view.inputmethod.InputMethod;
26
27import com.android.internal.annotations.VisibleForTesting;
28
29import java.util.ArrayList;
30import java.util.List;
31
32/**
33 * Helper class for querying installed applications using multiple criteria.
34 *
35 * @hide
36 */
37public class AppsQueryHelper {
38
39    /**
40     * Return apps without launcher icon
41     */
42    public static int GET_NON_LAUNCHABLE_APPS = 1;
43
44    /**
45     * Return apps with {@link Manifest.permission#INTERACT_ACROSS_USERS} permission
46     */
47    public static int GET_APPS_WITH_INTERACT_ACROSS_USERS_PERM = 1 << 1;
48
49    /**
50     * Return all input methods available for the current user.
51     */
52    public static int GET_IMES = 1 << 2;
53
54    /**
55     * Return all apps that are flagged as required for the system user.
56     */
57    public static int GET_REQUIRED_FOR_SYSTEM_USER = 1 << 3;
58
59    private final IPackageManager mPackageManager;
60    private List<ApplicationInfo> mAllApps;
61
62    public AppsQueryHelper(IPackageManager packageManager) {
63        mPackageManager = packageManager;
64    }
65
66    public AppsQueryHelper() {
67        this(AppGlobals.getPackageManager());
68    }
69
70    /**
71     * Return a List of all packages that satisfy a specified criteria.
72     * @param flags search flags. Use any combination of {@link #GET_NON_LAUNCHABLE_APPS},
73     * {@link #GET_APPS_WITH_INTERACT_ACROSS_USERS_PERM} or {@link #GET_IMES}.
74     * @param systemAppsOnly if true, only system apps will be returned
75     * @param user user, whose apps are queried
76     */
77    public List<String> queryApps(int flags, boolean systemAppsOnly, UserHandle user) {
78        boolean nonLaunchableApps = (flags & GET_NON_LAUNCHABLE_APPS) > 0;
79        boolean interactAcrossUsers = (flags & GET_APPS_WITH_INTERACT_ACROSS_USERS_PERM) > 0;
80        boolean imes = (flags & GET_IMES) > 0;
81        boolean requiredForSystemUser = (flags & GET_REQUIRED_FOR_SYSTEM_USER) > 0;
82        if (mAllApps == null) {
83            mAllApps = getAllApps(user.getIdentifier());
84        }
85
86        List<String> result = new ArrayList<>();
87        if (flags == 0) {
88            final int allAppsSize = mAllApps.size();
89            for (int i = 0; i < allAppsSize; i++) {
90                final ApplicationInfo appInfo = mAllApps.get(i);
91                if (systemAppsOnly && !appInfo.isSystemApp()) {
92                    continue;
93                }
94                result.add(appInfo.packageName);
95            }
96            return result;
97        }
98
99        if (nonLaunchableApps) {
100            Intent intent = new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_LAUNCHER);
101            final List<ResolveInfo> resolveInfos = queryIntentActivitiesAsUser(intent,
102                    user.getIdentifier());
103
104            ArraySet<String> appsWithLaunchers = new ArraySet<>();
105            final int resolveInfosSize = resolveInfos.size();
106            for (int i = 0; i < resolveInfosSize; i++) {
107                appsWithLaunchers.add(resolveInfos.get(i).activityInfo.packageName);
108            }
109            final int allAppsSize = mAllApps.size();
110            for (int i = 0; i < allAppsSize; i++) {
111                final ApplicationInfo appInfo = mAllApps.get(i);
112                if (systemAppsOnly && !appInfo.isSystemApp()) {
113                    continue;
114                }
115                final String packageName = appInfo.packageName;
116                if (!appsWithLaunchers.contains(packageName)) {
117                    result.add(packageName);
118                }
119            }
120        }
121        if (interactAcrossUsers) {
122            final List<PackageInfo> packagesHoldingPermissions = getPackagesHoldingPermission(
123                    Manifest.permission.INTERACT_ACROSS_USERS, user.getIdentifier());
124            final int packagesHoldingPermissionsSize = packagesHoldingPermissions.size();
125            for (int i = 0; i < packagesHoldingPermissionsSize; i++) {
126                PackageInfo packageInfo = packagesHoldingPermissions.get(i);
127                if (systemAppsOnly && !packageInfo.applicationInfo.isSystemApp()) {
128                    continue;
129                }
130                if (!result.contains(packageInfo.packageName)) {
131                    result.add(packageInfo.packageName);
132                }
133            }
134        }
135
136        if (imes) {
137            final List<ResolveInfo> resolveInfos = queryIntentServicesAsUser(
138                    new Intent(InputMethod.SERVICE_INTERFACE), user.getIdentifier());
139            final int resolveInfosSize = resolveInfos.size();
140
141            for (int i = 0; i < resolveInfosSize; i++) {
142                ServiceInfo serviceInfo = resolveInfos.get(i).serviceInfo;
143                if (systemAppsOnly && !serviceInfo.applicationInfo.isSystemApp()) {
144                    continue;
145                }
146                if (!result.contains(serviceInfo.packageName)) {
147                    result.add(serviceInfo.packageName);
148                }
149            }
150        }
151
152        if (requiredForSystemUser) {
153            final int allAppsSize = mAllApps.size();
154            for (int i = 0; i < allAppsSize; i++) {
155                final ApplicationInfo appInfo = mAllApps.get(i);
156                if (systemAppsOnly && !appInfo.isSystemApp()) {
157                    continue;
158                }
159                if (appInfo.isRequiredForSystemUser()) {
160                    result.add(appInfo.packageName);
161                }
162            }
163        }
164        return result;
165    }
166
167    @VisibleForTesting
168    @SuppressWarnings("unchecked")
169    protected List<ApplicationInfo> getAllApps(int userId) {
170        try {
171            return mPackageManager.getInstalledApplications(
172                    PackageManager.MATCH_UNINSTALLED_PACKAGES
173                            | PackageManager.MATCH_DISABLED_COMPONENTS, userId).getList();
174        } catch (RemoteException e) {
175            throw e.rethrowFromSystemServer();
176        }
177    }
178
179    @VisibleForTesting
180    protected List<ResolveInfo> queryIntentActivitiesAsUser(Intent intent, int userId) {
181        try {
182            return mPackageManager.queryIntentActivities(intent, null,
183                    PackageManager.MATCH_DISABLED_COMPONENTS
184                            | PackageManager.MATCH_UNINSTALLED_PACKAGES
185                            | PackageManager.MATCH_DIRECT_BOOT_AWARE
186                            | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
187                    userId).getList();
188        } catch (RemoteException e) {
189            throw e.rethrowFromSystemServer();
190        }
191    }
192
193    @VisibleForTesting
194    protected List<ResolveInfo> queryIntentServicesAsUser(Intent intent, int userId) {
195        try {
196            return mPackageManager.queryIntentServices(intent, null,
197                    PackageManager.GET_META_DATA
198                            | PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS
199                            | PackageManager.MATCH_DIRECT_BOOT_AWARE
200                            | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userId)
201                    .getList();
202        } catch (RemoteException e) {
203            throw e.rethrowFromSystemServer();
204        }
205    }
206
207    @VisibleForTesting
208    @SuppressWarnings("unchecked")
209    protected List<PackageInfo> getPackagesHoldingPermission(String perm, int userId) {
210        try {
211            return mPackageManager.getPackagesHoldingPermissions(new String[]{perm}, 0,
212                    userId).getList();
213        } catch (RemoteException e) {
214            throw e.rethrowFromSystemServer();
215        }
216    }
217}
218