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.Context; 21 import android.content.Intent; 22 import android.content.pm.ApplicationInfo; 23 import android.content.pm.LauncherActivityInfo; 24 import android.content.pm.PackageManager; 25 import android.content.pm.PackageManager.NameNotFoundException; 26 import android.content.pm.ResolveInfo; 27 import android.net.Uri; 28 import android.os.Build; 29 import android.os.UserHandle; 30 import android.text.TextUtils; 31 32 import com.android.launcher3.AppInfo; 33 import com.android.launcher3.R; 34 import com.android.launcher3.Utilities; 35 import com.android.launcher3.compat.LauncherAppsCompat; 36 37 import java.net.URISyntaxException; 38 import java.util.List; 39 40 /** 41 * Utility methods using package manager 42 */ 43 public class PackageManagerHelper { 44 45 private final Context mContext; 46 private final PackageManager mPm; 47 private final LauncherAppsCompat mLauncherApps; 48 PackageManagerHelper(Context context)49 public PackageManagerHelper(Context context) { 50 mContext = context; 51 mPm = context.getPackageManager(); 52 mLauncherApps = LauncherAppsCompat.getInstance(context); 53 } 54 55 /** 56 * Returns true if the app can possibly be on the SDCard. This is just a workaround and doesn't 57 * guarantee that the app is on SD card. 58 */ isAppOnSdcard(String packageName, UserHandle user)59 public boolean isAppOnSdcard(String packageName, UserHandle user) { 60 ApplicationInfo info = mLauncherApps.getApplicationInfo( 61 packageName, PackageManager.MATCH_UNINSTALLED_PACKAGES, user); 62 return info != null && (info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0; 63 } 64 65 /** 66 * Returns whether the target app is suspended for a given user as per 67 * {@link android.app.admin.DevicePolicyManager#isPackageSuspended}. 68 */ isAppSuspended(String packageName, UserHandle user)69 public boolean isAppSuspended(String packageName, UserHandle user) { 70 ApplicationInfo info = mLauncherApps.getApplicationInfo(packageName, 0, user); 71 return info != null && isAppSuspended(info); 72 } 73 isSafeMode()74 public boolean isSafeMode() { 75 return mContext.getPackageManager().isSafeMode(); 76 } 77 getAppLaunchIntent(String pkg, UserHandle user)78 public Intent getAppLaunchIntent(String pkg, UserHandle user) { 79 List<LauncherActivityInfo> activities = mLauncherApps.getActivityList(pkg, user); 80 return activities.isEmpty() ? null : 81 AppInfo.makeLaunchIntent(activities.get(0)); 82 } 83 84 /** 85 * Returns whether an application is suspended as per 86 * {@link android.app.admin.DevicePolicyManager#isPackageSuspended}. 87 */ isAppSuspended(ApplicationInfo info)88 public static boolean isAppSuspended(ApplicationInfo info) { 89 // The value of FLAG_SUSPENDED was reused by a hidden constant 90 // ApplicationInfo.FLAG_PRIVILEGED prior to N, so only check for suspended flag on N 91 // or later. 92 if (Utilities.ATLEAST_NOUGAT) { 93 return (info.flags & ApplicationInfo.FLAG_SUSPENDED) != 0; 94 } else { 95 return false; 96 } 97 } 98 99 /** 100 * Returns true if {@param srcPackage} has the permission required to start the activity from 101 * {@param intent}. If {@param srcPackage} is null, then the activity should not need 102 * any permissions 103 */ hasPermissionForActivity(Intent intent, String srcPackage)104 public boolean hasPermissionForActivity(Intent intent, String srcPackage) { 105 ResolveInfo target = mPm.resolveActivity(intent, 0); 106 if (target == null) { 107 // Not a valid target 108 return false; 109 } 110 if (TextUtils.isEmpty(target.activityInfo.permission)) { 111 // No permission is needed 112 return true; 113 } 114 if (TextUtils.isEmpty(srcPackage)) { 115 // The activity requires some permission but there is no source. 116 return false; 117 } 118 119 // Source does not have sufficient permissions. 120 if(mPm.checkPermission(target.activityInfo.permission, srcPackage) != 121 PackageManager.PERMISSION_GRANTED) { 122 return false; 123 } 124 125 if (!Utilities.ATLEAST_MARSHMALLOW) { 126 // These checks are sufficient for below M devices. 127 return true; 128 } 129 130 // On M and above also check AppOpsManager for compatibility mode permissions. 131 if (TextUtils.isEmpty(AppOpsManager.permissionToOp(target.activityInfo.permission))) { 132 // There is no app-op for this permission, which could have been disabled. 133 return true; 134 } 135 136 // There is no direct way to check if the app-op is allowed for a particular app. Since 137 // app-op is only enabled for apps running in compatibility mode, simply block such apps. 138 139 try { 140 return mPm.getApplicationInfo(srcPackage, 0).targetSdkVersion >= Build.VERSION_CODES.M; 141 } catch (NameNotFoundException e) { } 142 143 return false; 144 } 145 getMarketIntent(String packageName)146 public static Intent getMarketIntent(String packageName) { 147 return new Intent(Intent.ACTION_VIEW) 148 .setData(new Uri.Builder() 149 .scheme("market") 150 .authority("details") 151 .appendQueryParameter("id", packageName) 152 .build()); 153 } 154 155 /** 156 * Creates a new market search intent. 157 */ getMarketSearchIntent(Context context, String query)158 public static Intent getMarketSearchIntent(Context context, String query) { 159 try { 160 Intent intent = Intent.parseUri(context.getString(R.string.market_search_intent), 0); 161 if (!TextUtils.isEmpty(query)) { 162 intent.setData( 163 intent.getData().buildUpon().appendQueryParameter("q", query).build()); 164 } 165 return intent; 166 } catch (URISyntaxException e) { 167 throw new RuntimeException(e); 168 } 169 } 170 } 171