Home | History | Annotate | Download | only in util
      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 
     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      */
     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      */
     83     public boolean isAppSuspended(String packageName, UserHandle user) {
     84         ApplicationInfo info = mLauncherApps.getApplicationInfo(packageName, 0, user);
     85         return info != null && isAppSuspended(info);
     86     }
     87 
     88     public boolean isSafeMode() {
     89         return mContext.getPackageManager().isSafeMode();
     90     }
     91 
     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      */
    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      */
    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 
    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      */
    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      */
    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