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