• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 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 
17 package com.android.launcher3.util;
18 
19 import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
20 
21 import android.app.AppOpsManager;
22 import android.content.ActivityNotFoundException;
23 import android.content.ComponentName;
24 import android.content.Context;
25 import android.content.Intent;
26 import android.content.IntentFilter;
27 import android.content.pm.ApplicationInfo;
28 import android.content.pm.LauncherActivityInfo;
29 import android.content.pm.LauncherApps;
30 import android.content.pm.PackageInfo;
31 import android.content.pm.PackageManager;
32 import android.content.pm.PackageManager.NameNotFoundException;
33 import android.content.pm.ResolveInfo;
34 import android.content.res.Resources;
35 import android.graphics.Rect;
36 import android.net.Uri;
37 import android.os.Build;
38 import android.os.Bundle;
39 import android.os.PatternMatcher;
40 import android.os.UserHandle;
41 import android.text.TextUtils;
42 import android.util.Log;
43 import android.util.Pair;
44 import android.widget.Toast;
45 
46 import com.android.launcher3.PendingAddItemInfo;
47 import com.android.launcher3.R;
48 import com.android.launcher3.Utilities;
49 import com.android.launcher3.model.data.AppInfo;
50 import com.android.launcher3.model.data.ItemInfo;
51 import com.android.launcher3.model.data.ItemInfoWithIcon;
52 import com.android.launcher3.model.data.LauncherAppWidgetInfo;
53 import com.android.launcher3.model.data.WorkspaceItemInfo;
54 
55 import java.net.URISyntaxException;
56 import java.util.List;
57 
58 /**
59  * Utility methods using package manager
60  */
61 public class PackageManagerHelper {
62 
63     private static final String TAG = "PackageManagerHelper";
64 
65     private final Context mContext;
66     private final PackageManager mPm;
67     private final LauncherApps mLauncherApps;
68 
PackageManagerHelper(Context context)69     public PackageManagerHelper(Context context) {
70         mContext = context;
71         mPm = context.getPackageManager();
72         mLauncherApps = context.getSystemService(LauncherApps.class);
73     }
74 
75     /**
76      * Returns true if the app can possibly be on the SDCard. This is just a workaround and doesn't
77      * guarantee that the app is on SD card.
78      */
isAppOnSdcard(String packageName, UserHandle user)79     public boolean isAppOnSdcard(String packageName, UserHandle user) {
80         ApplicationInfo info = getApplicationInfo(
81                 packageName, user, PackageManager.MATCH_UNINSTALLED_PACKAGES);
82         return info != null && (info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0;
83     }
84 
85     /**
86      * Returns whether the target app is suspended for a given user as per
87      * {@link android.app.admin.DevicePolicyManager#isPackageSuspended}.
88      */
isAppSuspended(String packageName, UserHandle user)89     public boolean isAppSuspended(String packageName, UserHandle user) {
90         ApplicationInfo info = getApplicationInfo(packageName, user, 0);
91         return info != null && isAppSuspended(info);
92     }
93 
94     /**
95      * Returns whether the target app is installed for a given user
96      */
isAppInstalled(String packageName, UserHandle user)97     public boolean isAppInstalled(String packageName, UserHandle user) {
98         ApplicationInfo info = getApplicationInfo(packageName, user, 0);
99         return info != null;
100     }
101 
102     /**
103      * Returns the application info for the provided package or null
104      */
getApplicationInfo(String packageName, UserHandle user, int flags)105     public ApplicationInfo getApplicationInfo(String packageName, UserHandle user, int flags) {
106         try {
107             ApplicationInfo info = mLauncherApps.getApplicationInfo(packageName, flags, user);
108             return (info.flags & ApplicationInfo.FLAG_INSTALLED) == 0 || !info.enabled
109                     ? null : info;
110         } catch (PackageManager.NameNotFoundException e) {
111             return null;
112         }
113     }
114 
isSafeMode()115     public boolean isSafeMode() {
116         return mPm.isSafeMode();
117     }
118 
getAppLaunchIntent(String pkg, UserHandle user)119     public Intent getAppLaunchIntent(String pkg, UserHandle user) {
120         List<LauncherActivityInfo> activities = mLauncherApps.getActivityList(pkg, user);
121         return activities.isEmpty() ? null :
122                 AppInfo.makeLaunchIntent(activities.get(0));
123     }
124 
125     /**
126      * Returns whether an application is suspended as per
127      * {@link android.app.admin.DevicePolicyManager#isPackageSuspended}.
128      */
isAppSuspended(ApplicationInfo info)129     public static boolean isAppSuspended(ApplicationInfo info) {
130         return (info.flags & ApplicationInfo.FLAG_SUSPENDED) != 0;
131     }
132 
133     /**
134      * Returns true if {@param srcPackage} has the permission required to start the activity from
135      * {@param intent}. If {@param srcPackage} is null, then the activity should not need
136      * any permissions
137      */
hasPermissionForActivity(Intent intent, String srcPackage)138     public boolean hasPermissionForActivity(Intent intent, String srcPackage) {
139         ResolveInfo target = mPm.resolveActivity(intent, 0);
140         if (target == null) {
141             // Not a valid target
142             return false;
143         }
144         if (TextUtils.isEmpty(target.activityInfo.permission)) {
145             // No permission is needed
146             return true;
147         }
148         if (TextUtils.isEmpty(srcPackage)) {
149             // The activity requires some permission but there is no source.
150             return false;
151         }
152 
153         // Source does not have sufficient permissions.
154         if(mPm.checkPermission(target.activityInfo.permission, srcPackage) !=
155                 PackageManager.PERMISSION_GRANTED) {
156             return false;
157         }
158 
159         // On M and above also check AppOpsManager for compatibility mode permissions.
160         if (TextUtils.isEmpty(AppOpsManager.permissionToOp(target.activityInfo.permission))) {
161             // There is no app-op for this permission, which could have been disabled.
162             return true;
163         }
164 
165         // There is no direct way to check if the app-op is allowed for a particular app. Since
166         // app-op is only enabled for apps running in compatibility mode, simply block such apps.
167 
168         try {
169             return mPm.getApplicationInfo(srcPackage, 0).targetSdkVersion >= Build.VERSION_CODES.M;
170         } catch (NameNotFoundException e) { }
171 
172         return false;
173     }
174 
getMarketIntent(String packageName)175     public Intent getMarketIntent(String packageName) {
176         return new Intent(Intent.ACTION_VIEW)
177                 .setData(new Uri.Builder()
178                         .scheme("market")
179                         .authority("details")
180                         .appendQueryParameter("id", packageName)
181                         .build())
182                 .putExtra(Intent.EXTRA_REFERRER, new Uri.Builder().scheme("android-app")
183                         .authority(mContext.getPackageName()).build());
184     }
185 
186     /**
187      * Creates a new market search intent.
188      */
getMarketSearchIntent(Context context, String query)189     public static Intent getMarketSearchIntent(Context context, String query) {
190         try {
191             Intent intent = Intent.parseUri(context.getString(R.string.market_search_intent), 0);
192             if (!TextUtils.isEmpty(query)) {
193                 intent.setData(
194                         intent.getData().buildUpon().appendQueryParameter("q", query).build());
195             }
196             return intent;
197         } catch (URISyntaxException e) {
198             throw new RuntimeException(e);
199         }
200     }
201 
getStyleWallpapersIntent(Context context)202     public static Intent getStyleWallpapersIntent(Context context) {
203         return new Intent(Intent.ACTION_SET_WALLPAPER).setComponent(
204                 new ComponentName(context.getString(R.string.wallpaper_picker_package),
205                 "com.android.customization.picker.CustomizationPickerActivity"));
206     }
207 
208     /**
209      * Starts the details activity for {@code info}
210      */
startDetailsActivityForInfo(ItemInfo info, Rect sourceBounds, Bundle opts)211     public void startDetailsActivityForInfo(ItemInfo info, Rect sourceBounds, Bundle opts) {
212         if (info instanceof ItemInfoWithIcon
213                 && (((ItemInfoWithIcon) info).runtimeStatusFlags
214                     & ItemInfoWithIcon.FLAG_INSTALL_SESSION_ACTIVE) != 0) {
215             ItemInfoWithIcon appInfo = (ItemInfoWithIcon) info;
216             mContext.startActivity(new PackageManagerHelper(mContext)
217                     .getMarketIntent(appInfo.getTargetComponent().getPackageName()));
218             return;
219         }
220         ComponentName componentName = null;
221         if (info instanceof AppInfo) {
222             componentName = ((AppInfo) info).componentName;
223         } else if (info instanceof WorkspaceItemInfo) {
224             componentName = info.getTargetComponent();
225         } else if (info instanceof PendingAddItemInfo) {
226             componentName = ((PendingAddItemInfo) info).componentName;
227         } else if (info instanceof LauncherAppWidgetInfo) {
228             componentName = ((LauncherAppWidgetInfo) info).providerName;
229         }
230         if (componentName != null) {
231             try {
232                 mLauncherApps.startAppDetailsActivity(componentName, info.user, sourceBounds, opts);
233             } catch (SecurityException | ActivityNotFoundException e) {
234                 Toast.makeText(mContext, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
235                 Log.e(TAG, "Unable to launch settings", e);
236             }
237         }
238     }
239 
240     /**
241      * Creates an intent filter to listen for actions with a specific package in the data field.
242      */
getPackageFilter(String pkg, String... actions)243     public static IntentFilter getPackageFilter(String pkg, String... actions) {
244         IntentFilter packageFilter = new IntentFilter();
245         for (String action : actions) {
246             packageFilter.addAction(action);
247         }
248         packageFilter.addDataScheme("package");
249         packageFilter.addDataSchemeSpecificPart(pkg, PatternMatcher.PATTERN_LITERAL);
250         return packageFilter;
251     }
252 
isSystemApp(Context context, Intent intent)253     public static boolean isSystemApp(Context context, Intent intent) {
254         PackageManager pm = context.getPackageManager();
255         ComponentName cn = intent.getComponent();
256         String packageName = null;
257         if (cn == null) {
258             ResolveInfo info = pm.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY);
259             if ((info != null) && (info.activityInfo != null)) {
260                 packageName = info.activityInfo.packageName;
261             }
262         } else {
263             packageName = cn.getPackageName();
264         }
265         if (packageName == null) {
266             packageName = intent.getPackage();
267         }
268         if (packageName != null) {
269             try {
270                 PackageInfo info = pm.getPackageInfo(packageName, 0);
271                 return (info != null) && (info.applicationInfo != null) &&
272                         ((info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0);
273             } catch (NameNotFoundException e) {
274                 return false;
275             }
276         } else {
277             return false;
278         }
279     }
280 
281     /**
282      * Finds a system apk which had a broadcast receiver listening to a particular action.
283      * @param action intent action used to find the apk
284      * @return a pair of apk package name and the resources.
285      */
findSystemApk(String action, PackageManager pm)286     public static Pair<String, Resources> findSystemApk(String action, PackageManager pm) {
287         final Intent intent = new Intent(action);
288         for (ResolveInfo info : pm.queryBroadcastReceivers(intent, MATCH_SYSTEM_ONLY)) {
289             final String packageName = info.activityInfo.packageName;
290             try {
291                 final Resources res = pm.getResourcesForApplication(packageName);
292                 return Pair.create(packageName, res);
293             } catch (NameNotFoundException e) {
294                 Log.w(TAG, "Failed to find resources for " + packageName);
295             }
296         }
297         return null;
298     }
299 
300     /**
301      * Returns true if the intent is a valid launch intent for a launcher activity of an app.
302      * This is used to identify shortcuts which are different from the ones exposed by the
303      * applications' manifest file.
304      *
305      * @param launchIntent The intent that will be launched when the shortcut is clicked.
306      */
isLauncherAppTarget(Intent launchIntent)307     public static boolean isLauncherAppTarget(Intent launchIntent) {
308         if (launchIntent != null
309                 && Intent.ACTION_MAIN.equals(launchIntent.getAction())
310                 && launchIntent.getComponent() != null
311                 && launchIntent.getCategories() != null
312                 && launchIntent.getCategories().size() == 1
313                 && launchIntent.hasCategory(Intent.CATEGORY_LAUNCHER)
314                 && TextUtils.isEmpty(launchIntent.getDataString())) {
315             // An app target can either have no extra or have ItemInfo.EXTRA_PROFILE.
316             Bundle extras = launchIntent.getExtras();
317             return extras == null || extras.keySet().isEmpty();
318         }
319         return false;
320     }
321 
322     /**
323      * Returns true if Launcher has the permission to access shortcuts.
324      * @see LauncherApps#hasShortcutHostPermission()
325      */
hasShortcutsPermission(Context context)326     public static boolean hasShortcutsPermission(Context context) {
327         try {
328             return context.getSystemService(LauncherApps.class).hasShortcutHostPermission();
329         } catch (SecurityException | IllegalStateException e) {
330             Log.e(TAG, "Failed to make shortcut manager call", e);
331         }
332         return false;
333     }
334 
335     /** Returns the incremental download progress for the given shortcut's app. */
getLoadingProgress(LauncherActivityInfo info)336     public static int getLoadingProgress(LauncherActivityInfo info) {
337         if (Utilities.ATLEAST_S) {
338             return (int) (100 * info.getLoadingProgress());
339         }
340         return 100;
341     }
342 }
343