• 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 android.app.AppOpsManager;
20 import android.content.ActivityNotFoundException;
21 import android.content.ComponentName;
22 import android.content.Context;
23 import android.content.Intent;
24 import android.content.IntentFilter;
25 import android.content.pm.ApplicationInfo;
26 import android.content.pm.LauncherActivityInfo;
27 import android.content.pm.PackageManager;
28 import android.content.pm.PackageManager.NameNotFoundException;
29 import android.content.pm.ResolveInfo;
30 import android.graphics.Rect;
31 import android.net.Uri;
32 import android.os.Build;
33 import android.os.Bundle;
34 import android.os.PatternMatcher;
35 import android.os.UserHandle;
36 import android.text.TextUtils;
37 import android.util.Log;
38 import android.widget.Toast;
39 
40 import com.android.launcher3.AppInfo;
41 import com.android.launcher3.ItemInfo;
42 import com.android.launcher3.LauncherAppWidgetInfo;
43 import com.android.launcher3.PendingAddItemInfo;
44 import com.android.launcher3.PromiseAppInfo;
45 import com.android.launcher3.R;
46 import com.android.launcher3.WorkspaceItemInfo;
47 import com.android.launcher3.compat.LauncherAppsCompat;
48 
49 import java.net.URISyntaxException;
50 import java.util.List;
51 
52 /**
53  * Utility methods using package manager
54  */
55 public class PackageManagerHelper {
56 
57     private static final String TAG = "PackageManagerHelper";
58 
59     private final Context mContext;
60     private final PackageManager mPm;
61     private final LauncherAppsCompat mLauncherApps;
62 
PackageManagerHelper(Context context)63     public PackageManagerHelper(Context context) {
64         mContext = context;
65         mPm = context.getPackageManager();
66         mLauncherApps = LauncherAppsCompat.getInstance(context);
67     }
68 
69     /**
70      * Returns true if the app can possibly be on the SDCard. This is just a workaround and doesn't
71      * guarantee that the app is on SD card.
72      */
isAppOnSdcard(String packageName, UserHandle user)73     public boolean isAppOnSdcard(String packageName, UserHandle user) {
74         ApplicationInfo info = mLauncherApps.getApplicationInfo(
75                 packageName, PackageManager.MATCH_UNINSTALLED_PACKAGES, user);
76         return info != null && (info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0;
77     }
78 
79     /**
80      * Returns whether the target app is suspended for a given user as per
81      * {@link android.app.admin.DevicePolicyManager#isPackageSuspended}.
82      */
isAppSuspended(String packageName, UserHandle user)83     public boolean isAppSuspended(String packageName, UserHandle user) {
84         ApplicationInfo info = mLauncherApps.getApplicationInfo(packageName, 0, user);
85         return info != null && isAppSuspended(info);
86     }
87 
isSafeMode()88     public boolean isSafeMode() {
89         return mContext.getPackageManager().isSafeMode();
90     }
91 
getAppLaunchIntent(String pkg, UserHandle user)92     public Intent getAppLaunchIntent(String pkg, UserHandle user) {
93         List<LauncherActivityInfo> activities = mLauncherApps.getActivityList(pkg, user);
94         return activities.isEmpty() ? null :
95                 AppInfo.makeLaunchIntent(activities.get(0));
96     }
97 
98     /**
99      * Returns whether an application is suspended as per
100      * {@link android.app.admin.DevicePolicyManager#isPackageSuspended}.
101      */
isAppSuspended(ApplicationInfo info)102     public static boolean isAppSuspended(ApplicationInfo info) {
103         return (info.flags & ApplicationInfo.FLAG_SUSPENDED) != 0;
104     }
105 
106     /**
107      * Returns true if {@param srcPackage} has the permission required to start the activity from
108      * {@param intent}. If {@param srcPackage} is null, then the activity should not need
109      * any permissions
110      */
hasPermissionForActivity(Intent intent, String srcPackage)111     public boolean hasPermissionForActivity(Intent intent, String srcPackage) {
112         ResolveInfo target = mPm.resolveActivity(intent, 0);
113         if (target == null) {
114             // Not a valid target
115             return false;
116         }
117         if (TextUtils.isEmpty(target.activityInfo.permission)) {
118             // No permission is needed
119             return true;
120         }
121         if (TextUtils.isEmpty(srcPackage)) {
122             // The activity requires some permission but there is no source.
123             return false;
124         }
125 
126         // Source does not have sufficient permissions.
127         if(mPm.checkPermission(target.activityInfo.permission, srcPackage) !=
128                 PackageManager.PERMISSION_GRANTED) {
129             return false;
130         }
131 
132         // On M and above also check AppOpsManager for compatibility mode permissions.
133         if (TextUtils.isEmpty(AppOpsManager.permissionToOp(target.activityInfo.permission))) {
134             // There is no app-op for this permission, which could have been disabled.
135             return true;
136         }
137 
138         // There is no direct way to check if the app-op is allowed for a particular app. Since
139         // app-op is only enabled for apps running in compatibility mode, simply block such apps.
140 
141         try {
142             return mPm.getApplicationInfo(srcPackage, 0).targetSdkVersion >= Build.VERSION_CODES.M;
143         } catch (NameNotFoundException e) { }
144 
145         return false;
146     }
147 
getMarketIntent(String packageName)148     public Intent getMarketIntent(String packageName) {
149         return new Intent(Intent.ACTION_VIEW)
150                 .setData(new Uri.Builder()
151                         .scheme("market")
152                         .authority("details")
153                         .appendQueryParameter("id", packageName)
154                         .build())
155                 .putExtra(Intent.EXTRA_REFERRER, new Uri.Builder().scheme("android-app")
156                         .authority(mContext.getPackageName()).build());
157     }
158 
159     /**
160      * Creates a new market search intent.
161      */
getMarketSearchIntent(Context context, String query)162     public static Intent getMarketSearchIntent(Context context, String query) {
163         try {
164             Intent intent = Intent.parseUri(context.getString(R.string.market_search_intent), 0);
165             if (!TextUtils.isEmpty(query)) {
166                 intent.setData(
167                         intent.getData().buildUpon().appendQueryParameter("q", query).build());
168             }
169             return intent;
170         } catch (URISyntaxException e) {
171             throw new RuntimeException(e);
172         }
173     }
174 
getStyleWallpapersIntent(Context context)175     public static Intent getStyleWallpapersIntent(Context context) {
176         return new Intent(Intent.ACTION_SET_WALLPAPER).setComponent(
177                 new ComponentName(context.getString(R.string.wallpaper_picker_package),
178                 "com.android.customization.picker.CustomizationPickerActivity"));
179     }
180 
181     /**
182      * Starts the details activity for {@code info}
183      */
startDetailsActivityForInfo(ItemInfo info, Rect sourceBounds, Bundle opts)184     public void startDetailsActivityForInfo(ItemInfo info, Rect sourceBounds, Bundle opts) {
185         if (info instanceof PromiseAppInfo) {
186             PromiseAppInfo promiseAppInfo = (PromiseAppInfo) info;
187             mContext.startActivity(promiseAppInfo.getMarketIntent(mContext));
188             return;
189         }
190         ComponentName componentName = null;
191         if (info instanceof AppInfo) {
192             componentName = ((AppInfo) info).componentName;
193         } else if (info instanceof WorkspaceItemInfo) {
194             componentName = info.getTargetComponent();
195         } else if (info instanceof PendingAddItemInfo) {
196             componentName = ((PendingAddItemInfo) info).componentName;
197         } else if (info instanceof LauncherAppWidgetInfo) {
198             componentName = ((LauncherAppWidgetInfo) info).providerName;
199         }
200         if (componentName != null) {
201             try {
202                 mLauncherApps.showAppDetailsForProfile(
203                         componentName, info.user, sourceBounds, opts);
204             } catch (SecurityException | ActivityNotFoundException e) {
205                 Toast.makeText(mContext, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
206                 Log.e(TAG, "Unable to launch settings", e);
207             }
208         }
209     }
210 
211     /**
212      * Creates an intent filter to listen for actions with a specific package in the data field.
213      */
getPackageFilter(String pkg, String... actions)214     public static IntentFilter getPackageFilter(String pkg, String... actions) {
215         IntentFilter packageFilter = new IntentFilter();
216         for (String action : actions) {
217             packageFilter.addAction(action);
218         }
219         packageFilter.addDataScheme("package");
220         packageFilter.addDataSchemeSpecificPart(pkg, PatternMatcher.PATTERN_LITERAL);
221         return packageFilter;
222     }
223 }
224