• 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.pm.ApplicationInfo;
25 import android.content.pm.LauncherActivityInfo;
26 import android.content.pm.PackageManager;
27 import android.content.pm.PackageManager.NameNotFoundException;
28 import android.content.pm.ResolveInfo;
29 import android.graphics.Rect;
30 import android.net.Uri;
31 import android.os.Build;
32 import android.os.Bundle;
33 import android.os.UserHandle;
34 import android.text.TextUtils;
35 import android.util.Log;
36 import android.widget.Toast;
37 
38 import com.android.launcher3.AppInfo;
39 import com.android.launcher3.ItemInfo;
40 import com.android.launcher3.Launcher;
41 import com.android.launcher3.LauncherAppWidgetInfo;
42 import com.android.launcher3.PendingAddItemInfo;
43 import com.android.launcher3.PromiseAppInfo;
44 import com.android.launcher3.R;
45 import com.android.launcher3.ShortcutInfo;
46 import com.android.launcher3.Utilities;
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         // The value of FLAG_SUSPENDED was reused by a hidden constant
104         // ApplicationInfo.FLAG_PRIVILEGED prior to N, so only check for suspended flag on N
105         // or later.
106         if (Utilities.ATLEAST_NOUGAT) {
107             return (info.flags & ApplicationInfo.FLAG_SUSPENDED) != 0;
108         } else {
109             return false;
110         }
111     }
112 
113     /**
114      * Returns true if {@param srcPackage} has the permission required to start the activity from
115      * {@param intent}. If {@param srcPackage} is null, then the activity should not need
116      * any permissions
117      */
hasPermissionForActivity(Intent intent, String srcPackage)118     public boolean hasPermissionForActivity(Intent intent, String srcPackage) {
119         ResolveInfo target = mPm.resolveActivity(intent, 0);
120         if (target == null) {
121             // Not a valid target
122             return false;
123         }
124         if (TextUtils.isEmpty(target.activityInfo.permission)) {
125             // No permission is needed
126             return true;
127         }
128         if (TextUtils.isEmpty(srcPackage)) {
129             // The activity requires some permission but there is no source.
130             return false;
131         }
132 
133         // Source does not have sufficient permissions.
134         if(mPm.checkPermission(target.activityInfo.permission, srcPackage) !=
135                 PackageManager.PERMISSION_GRANTED) {
136             return false;
137         }
138 
139         if (!Utilities.ATLEAST_MARSHMALLOW) {
140             // These checks are sufficient for below M devices.
141             return true;
142         }
143 
144         // On M and above also check AppOpsManager for compatibility mode permissions.
145         if (TextUtils.isEmpty(AppOpsManager.permissionToOp(target.activityInfo.permission))) {
146             // There is no app-op for this permission, which could have been disabled.
147             return true;
148         }
149 
150         // There is no direct way to check if the app-op is allowed for a particular app. Since
151         // app-op is only enabled for apps running in compatibility mode, simply block such apps.
152 
153         try {
154             return mPm.getApplicationInfo(srcPackage, 0).targetSdkVersion >= Build.VERSION_CODES.M;
155         } catch (NameNotFoundException e) { }
156 
157         return false;
158     }
159 
getMarketIntent(String packageName)160     public Intent getMarketIntent(String packageName) {
161         return new Intent(Intent.ACTION_VIEW)
162                 .setData(new Uri.Builder()
163                         .scheme("market")
164                         .authority("details")
165                         .appendQueryParameter("id", packageName)
166                         .build())
167                 .putExtra(Intent.EXTRA_REFERRER, new Uri.Builder().scheme("android-app")
168                         .authority(mContext.getPackageName()).build());
169     }
170 
171     /**
172      * Creates a new market search intent.
173      */
getMarketSearchIntent(Context context, String query)174     public static Intent getMarketSearchIntent(Context context, String query) {
175         try {
176             Intent intent = Intent.parseUri(context.getString(R.string.market_search_intent), 0);
177             if (!TextUtils.isEmpty(query)) {
178                 intent.setData(
179                         intent.getData().buildUpon().appendQueryParameter("q", query).build());
180             }
181             return intent;
182         } catch (URISyntaxException e) {
183             throw new RuntimeException(e);
184         }
185     }
186 
187 
188     /**
189      * Starts the details activity for {@code info}
190      */
startDetailsActivityForInfo(ItemInfo info, Rect sourceBounds, Bundle opts)191     public void startDetailsActivityForInfo(ItemInfo info, Rect sourceBounds, Bundle opts) {
192         if (info instanceof PromiseAppInfo) {
193             PromiseAppInfo promiseAppInfo = (PromiseAppInfo) info;
194             mContext.startActivity(promiseAppInfo.getMarketIntent(mContext));
195             return;
196         }
197         ComponentName componentName = null;
198         if (info instanceof AppInfo) {
199             componentName = ((AppInfo) info).componentName;
200         } else if (info instanceof ShortcutInfo) {
201             componentName = info.getTargetComponent();
202         } else if (info instanceof PendingAddItemInfo) {
203             componentName = ((PendingAddItemInfo) info).componentName;
204         } else if (info instanceof LauncherAppWidgetInfo) {
205             componentName = ((LauncherAppWidgetInfo) info).providerName;
206         }
207         if (componentName != null) {
208             try {
209                 mLauncherApps.showAppDetailsForProfile(
210                         componentName, info.user, sourceBounds, opts);
211             } catch (SecurityException | ActivityNotFoundException e) {
212                 Toast.makeText(mContext, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
213                 Log.e(TAG, "Unable to launch settings", e);
214             }
215         }
216     }
217 }
218