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