Home | History | Annotate | Download | only in model
      1 /*
      2  * Copyright (C) 2015 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.packageinstaller.permission.model;
     18 
     19 import android.app.ActivityManager;
     20 import android.app.AppOpsManager;
     21 import android.content.Context;
     22 import android.content.pm.PackageInfo;
     23 import android.content.pm.PackageItemInfo;
     24 import android.content.pm.PackageManager;
     25 import android.content.pm.PermissionGroupInfo;
     26 import android.content.pm.PermissionInfo;
     27 import android.os.Build;
     28 import android.os.Process;
     29 import android.os.UserHandle;
     30 import android.util.ArrayMap;
     31 
     32 import com.android.packageinstaller.R;
     33 import com.android.packageinstaller.permission.utils.ArrayUtils;
     34 import com.android.packageinstaller.permission.utils.LocationUtils;
     35 
     36 import java.util.ArrayList;
     37 import java.util.List;
     38 
     39 public final class AppPermissionGroup implements Comparable<AppPermissionGroup> {
     40     private static final String PLATFORM_PACKAGE_NAME = "android";
     41 
     42     private static final String KILL_REASON_APP_OP_CHANGE = "Permission related app op changed";
     43 
     44     private final Context mContext;
     45     private final UserHandle mUserHandle;
     46     private final PackageManager mPackageManager;
     47     private final AppOpsManager mAppOps;
     48     private final ActivityManager mActivityManager;
     49 
     50     private final PackageInfo mPackageInfo;
     51     private final String mName;
     52     private final String mDeclaringPackage;
     53     private final CharSequence mLabel;
     54     private final CharSequence mDescription;
     55     private final ArrayMap<String, Permission> mPermissions = new ArrayMap<>();
     56     private final String mIconPkg;
     57     private final int mIconResId;
     58 
     59     private final boolean mAppSupportsRuntimePermissions;
     60 
     61     public static AppPermissionGroup create(Context context, PackageInfo packageInfo,
     62             String permissionName) {
     63         PermissionInfo permissionInfo;
     64         try {
     65             permissionInfo = context.getPackageManager().getPermissionInfo(permissionName, 0);
     66         } catch (PackageManager.NameNotFoundException e) {
     67             return null;
     68         }
     69 
     70         if (permissionInfo.protectionLevel != PermissionInfo.PROTECTION_DANGEROUS
     71                 || (permissionInfo.flags & PermissionInfo.FLAG_INSTALLED) == 0
     72                 || (permissionInfo.flags & PermissionInfo.FLAG_REMOVED) != 0) {
     73             return null;
     74         }
     75 
     76         PackageItemInfo groupInfo = permissionInfo;
     77         if (permissionInfo.group != null) {
     78             try {
     79                 groupInfo = context.getPackageManager().getPermissionGroupInfo(
     80                         permissionInfo.group, 0);
     81             } catch (PackageManager.NameNotFoundException e) {
     82                 /* ignore */
     83             }
     84         }
     85 
     86         List<PermissionInfo> permissionInfos = null;
     87         if (groupInfo instanceof PermissionGroupInfo) {
     88             try {
     89                 permissionInfos = context.getPackageManager().queryPermissionsByGroup(
     90                         groupInfo.name, 0);
     91             } catch (PackageManager.NameNotFoundException e) {
     92                 /* ignore */
     93             }
     94         }
     95 
     96         return create(context, packageInfo, groupInfo, permissionInfos,
     97                 Process.myUserHandle());
     98     }
     99 
    100     public static AppPermissionGroup create(Context context, PackageInfo packageInfo,
    101             PackageItemInfo groupInfo, List<PermissionInfo> permissionInfos,
    102             UserHandle userHandle) {
    103 
    104         AppPermissionGroup group = new AppPermissionGroup(context, packageInfo, groupInfo.name,
    105                 groupInfo.packageName, groupInfo.loadLabel(context.getPackageManager()),
    106                 loadGroupDescription(context, groupInfo), groupInfo.packageName, groupInfo.icon,
    107                 userHandle);
    108 
    109         if (groupInfo instanceof PermissionInfo) {
    110             permissionInfos = new ArrayList<>();
    111             permissionInfos.add((PermissionInfo) groupInfo);
    112         }
    113 
    114         if (permissionInfos == null || permissionInfos.isEmpty()) {
    115             return null;
    116         }
    117 
    118         final int permissionCount = packageInfo.requestedPermissions.length;
    119         for (int i = 0; i < permissionCount; i++) {
    120             String requestedPermission = packageInfo.requestedPermissions[i];
    121 
    122             PermissionInfo requestedPermissionInfo = null;
    123 
    124             for (PermissionInfo permissionInfo : permissionInfos) {
    125                 if (requestedPermission.equals(permissionInfo.name)) {
    126                     requestedPermissionInfo = permissionInfo;
    127                     break;
    128                 }
    129             }
    130 
    131             if (requestedPermissionInfo == null) {
    132                 continue;
    133             }
    134 
    135             // Collect only runtime permissions.
    136             if (requestedPermissionInfo.protectionLevel != PermissionInfo.PROTECTION_DANGEROUS) {
    137                 continue;
    138             }
    139 
    140             // Don't allow toggling non-platform permission groups for legacy apps via app ops.
    141             if (packageInfo.applicationInfo.targetSdkVersion <= Build.VERSION_CODES.LOLLIPOP_MR1
    142                     && !PLATFORM_PACKAGE_NAME.equals(groupInfo.packageName)) {
    143                 continue;
    144             }
    145 
    146             final boolean granted = (packageInfo.requestedPermissionsFlags[i]
    147                     & PackageInfo.REQUESTED_PERMISSION_GRANTED) != 0;
    148 
    149             final String appOp = PLATFORM_PACKAGE_NAME.equals(requestedPermissionInfo.packageName)
    150                     ? AppOpsManager.permissionToOp(requestedPermissionInfo.name) : null;
    151 
    152             final boolean appOpAllowed = appOp != null
    153                     && context.getSystemService(AppOpsManager.class).checkOpNoThrow(appOp,
    154                     packageInfo.applicationInfo.uid, packageInfo.packageName)
    155                     == AppOpsManager.MODE_ALLOWED;
    156 
    157             final int flags = context.getPackageManager().getPermissionFlags(
    158                     requestedPermission, packageInfo.packageName, userHandle);
    159 
    160             Permission permission = new Permission(requestedPermission, granted,
    161                     appOp, appOpAllowed, flags);
    162             group.addPermission(permission);
    163         }
    164 
    165         return group;
    166     }
    167 
    168     private static CharSequence loadGroupDescription(Context context, PackageItemInfo group) {
    169         CharSequence description = null;
    170         if (group instanceof PermissionGroupInfo) {
    171             description = ((PermissionGroupInfo) group).loadDescription(
    172                     context.getPackageManager());
    173         } else if (group instanceof PermissionInfo) {
    174             description = ((PermissionInfo) group).loadDescription(
    175                     context.getPackageManager());
    176         }
    177 
    178         if (description == null || description.length() <= 0) {
    179             description = context.getString(R.string.default_permission_description);
    180         }
    181 
    182         return description;
    183     }
    184 
    185     private AppPermissionGroup(Context context, PackageInfo packageInfo, String name,
    186             String declaringPackage, CharSequence label, CharSequence description,
    187             String iconPkg, int iconResId, UserHandle userHandle) {
    188         mContext = context;
    189         mUserHandle = userHandle;
    190         mPackageManager = mContext.getPackageManager();
    191         mPackageInfo = packageInfo;
    192         mAppSupportsRuntimePermissions = packageInfo.applicationInfo
    193                 .targetSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1;
    194         mAppOps = context.getSystemService(AppOpsManager.class);
    195         mActivityManager = context.getSystemService(ActivityManager.class);
    196         mDeclaringPackage = declaringPackage;
    197         mName = name;
    198         mLabel = label;
    199         mDescription = description;
    200         if (iconResId != 0) {
    201             mIconPkg = iconPkg;
    202             mIconResId = iconResId;
    203         } else {
    204             mIconPkg = context.getPackageName();
    205             mIconResId = R.drawable.ic_perm_device_info;
    206         }
    207     }
    208 
    209     public boolean hasRuntimePermission() {
    210         return mAppSupportsRuntimePermissions;
    211     }
    212 
    213     public boolean isReviewRequired() {
    214         if (mAppSupportsRuntimePermissions) {
    215             return false;
    216         }
    217         final int permissionCount = mPermissions.size();
    218         for (int i = 0; i < permissionCount; i++) {
    219             Permission permission = mPermissions.valueAt(i);
    220             if (permission.isReviewRequired()) {
    221                 return true;
    222             }
    223         }
    224         return false;
    225     }
    226 
    227     public void resetReviewRequired() {
    228         final int permissionCount = mPermissions.size();
    229         for (int i = 0; i < permissionCount; i++) {
    230             Permission permission = mPermissions.valueAt(i);
    231             if (permission.isReviewRequired()) {
    232                 permission.resetReviewRequired();
    233                 mPackageManager.updatePermissionFlags(permission.getName(),
    234                         mPackageInfo.packageName,
    235                         PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED,
    236                         0, mUserHandle);
    237             }
    238         }
    239     }
    240 
    241     public boolean hasGrantedByDefaultPermission() {
    242         final int permissionCount = mPermissions.size();
    243         for (int i = 0; i < permissionCount; i++) {
    244             Permission permission = mPermissions.valueAt(i);
    245             if (permission.isGrantedByDefault()) {
    246                 return true;
    247             }
    248         }
    249         return false;
    250     }
    251 
    252     public PackageInfo getApp() {
    253         return mPackageInfo;
    254     }
    255 
    256     public String getName() {
    257         return mName;
    258     }
    259 
    260     public String getDeclaringPackage() {
    261         return mDeclaringPackage;
    262     }
    263 
    264     public String getIconPkg() {
    265         return mIconPkg;
    266     }
    267 
    268     public int getIconResId() {
    269         return mIconResId;
    270     }
    271 
    272     public CharSequence getLabel() {
    273         return mLabel;
    274     }
    275 
    276     public CharSequence getDescription() {
    277         return mDescription;
    278     }
    279 
    280     public int getUserId() {
    281         return mUserHandle.getIdentifier();
    282     }
    283 
    284     public boolean hasPermission(String permission) {
    285         return mPermissions.get(permission) != null;
    286     }
    287 
    288     public boolean areRuntimePermissionsGranted() {
    289         return areRuntimePermissionsGranted(null);
    290     }
    291 
    292     public boolean areRuntimePermissionsGranted(String[] filterPermissions) {
    293         if (LocationUtils.isLocationGroupAndProvider(mName, mPackageInfo.packageName)) {
    294             return LocationUtils.isLocationEnabled(mContext);
    295         }
    296         final int permissionCount = mPermissions.size();
    297         for (int i = 0; i < permissionCount; i++) {
    298             Permission permission = mPermissions.valueAt(i);
    299             if (filterPermissions != null
    300                     && !ArrayUtils.contains(filterPermissions, permission.getName())) {
    301                 continue;
    302             }
    303             if (mAppSupportsRuntimePermissions) {
    304                 if (permission.isGranted()) {
    305                     return true;
    306                 }
    307             } else if (permission.isGranted() && (permission.getAppOp() == null
    308                     || permission.isAppOpAllowed())) {
    309                 return true;
    310             }
    311         }
    312         return false;
    313     }
    314 
    315     public boolean grantRuntimePermissions(boolean fixedByTheUser) {
    316         return grantRuntimePermissions(fixedByTheUser, null);
    317     }
    318 
    319     public boolean grantRuntimePermissions(boolean fixedByTheUser, String[] filterPermissions) {
    320         final int uid = mPackageInfo.applicationInfo.uid;
    321 
    322         // We toggle permissions only to apps that support runtime
    323         // permissions, otherwise we toggle the app op corresponding
    324         // to the permission if the permission is granted to the app.
    325         for (Permission permission : mPermissions.values()) {
    326             if (filterPermissions != null
    327                     && !ArrayUtils.contains(filterPermissions, permission.getName())) {
    328                 continue;
    329             }
    330 
    331             if (mAppSupportsRuntimePermissions) {
    332                 // Do not touch permissions fixed by the system.
    333                 if (permission.isSystemFixed()) {
    334                     return false;
    335                 }
    336 
    337                 // Ensure the permission app op enabled before the permission grant.
    338                 if (permission.hasAppOp() && !permission.isAppOpAllowed()) {
    339                     permission.setAppOpAllowed(true);
    340                     mAppOps.setUidMode(permission.getAppOp(), uid, AppOpsManager.MODE_ALLOWED);
    341                 }
    342 
    343                 // Grant the permission if needed.
    344                 if (!permission.isGranted()) {
    345                     permission.setGranted(true);
    346                     mPackageManager.grantRuntimePermission(mPackageInfo.packageName,
    347                             permission.getName(), mUserHandle);
    348                 }
    349 
    350                 // Update the permission flags.
    351                 if (!fixedByTheUser) {
    352                     // Now the apps can ask for the permission as the user
    353                     // no longer has it fixed in a denied state.
    354                     if (permission.isUserFixed() || permission.isUserSet()) {
    355                         permission.setUserFixed(false);
    356                         permission.setUserSet(true);
    357                         mPackageManager.updatePermissionFlags(permission.getName(),
    358                                 mPackageInfo.packageName,
    359                                 PackageManager.FLAG_PERMISSION_USER_FIXED
    360                                         | PackageManager.FLAG_PERMISSION_USER_SET,
    361                                 0, mUserHandle);
    362                     }
    363                 }
    364             } else {
    365                 // Legacy apps cannot have a not granted permission but just in case.
    366                 if (!permission.isGranted()) {
    367                     continue;
    368                 }
    369 
    370                 int killUid = -1;
    371                 int mask = 0;
    372 
    373                 // If the permissions has no corresponding app op, then it is a
    374                 // third-party one and we do not offer toggling of such permissions.
    375                 if (permission.hasAppOp()) {
    376                     if (!permission.isAppOpAllowed()) {
    377                         permission.setAppOpAllowed(true);
    378                         // Enable the app op.
    379                         mAppOps.setUidMode(permission.getAppOp(), uid, AppOpsManager.MODE_ALLOWED);
    380 
    381                         // Legacy apps do not know that they have to retry access to a
    382                         // resource due to changes in runtime permissions (app ops in this
    383                         // case). Therefore, we restart them on app op change, so they
    384                         // can pick up the change.
    385                         killUid = uid;
    386                     }
    387 
    388                     // Mark that the permission should not be be granted on upgrade
    389                     // when the app begins supporting runtime permissions.
    390                     if (permission.shouldRevokeOnUpgrade()) {
    391                         permission.setRevokeOnUpgrade(false);
    392                         mask |= PackageManager.FLAG_PERMISSION_REVOKE_ON_UPGRADE;
    393                     }
    394                 }
    395 
    396                 if (mask != 0) {
    397                     mPackageManager.updatePermissionFlags(permission.getName(),
    398                             mPackageInfo.packageName, mask, 0, mUserHandle);
    399                 }
    400 
    401                 if (killUid != -1) {
    402                     mActivityManager.killUid(uid, KILL_REASON_APP_OP_CHANGE);
    403                 }
    404             }
    405         }
    406 
    407         return true;
    408     }
    409 
    410     public boolean revokeRuntimePermissions(boolean fixedByTheUser) {
    411         return revokeRuntimePermissions(fixedByTheUser, null);
    412     }
    413 
    414     public boolean revokeRuntimePermissions(boolean fixedByTheUser, String[] filterPermissions) {
    415         final int uid = mPackageInfo.applicationInfo.uid;
    416 
    417         // We toggle permissions only to apps that support runtime
    418         // permissions, otherwise we toggle the app op corresponding
    419         // to the permission if the permission is granted to the app.
    420         for (Permission permission : mPermissions.values()) {
    421             if (filterPermissions != null
    422                     && !ArrayUtils.contains(filterPermissions, permission.getName())) {
    423                 continue;
    424             }
    425 
    426             if (mAppSupportsRuntimePermissions) {
    427                 // Do not touch permissions fixed by the system.
    428                 if (permission.isSystemFixed()) {
    429                     return false;
    430                 }
    431 
    432                 // Revoke the permission if needed.
    433                 if (permission.isGranted()) {
    434                     permission.setGranted(false);
    435                     mPackageManager.revokeRuntimePermission(mPackageInfo.packageName,
    436                             permission.getName(), mUserHandle);
    437                 }
    438 
    439                 // Update the permission flags.
    440                 if (fixedByTheUser) {
    441                     // Take a note that the user fixed the permission.
    442                     if (permission.isUserSet() || !permission.isUserFixed()) {
    443                         permission.setUserSet(false);
    444                         permission.setUserFixed(true);
    445                         mPackageManager.updatePermissionFlags(permission.getName(),
    446                                 mPackageInfo.packageName,
    447                                 PackageManager.FLAG_PERMISSION_USER_SET
    448                                         | PackageManager.FLAG_PERMISSION_USER_FIXED,
    449                                 PackageManager.FLAG_PERMISSION_USER_FIXED,
    450                                 mUserHandle);
    451                     }
    452                 } else {
    453                     if (!permission.isUserSet()) {
    454                         permission.setUserSet(true);
    455                         // Take a note that the user already chose once.
    456                         mPackageManager.updatePermissionFlags(permission.getName(),
    457                                 mPackageInfo.packageName,
    458                                 PackageManager.FLAG_PERMISSION_USER_SET,
    459                                 PackageManager.FLAG_PERMISSION_USER_SET,
    460                                 mUserHandle);
    461                     }
    462                 }
    463             } else {
    464                 // Legacy apps cannot have a non-granted permission but just in case.
    465                 if (!permission.isGranted()) {
    466                     continue;
    467                 }
    468 
    469                 int mask = 0;
    470                 int flags = 0;
    471                 int killUid = -1;
    472 
    473                 // If the permission has no corresponding app op, then it is a
    474                 // third-party one and we do not offer toggling of such permissions.
    475                 if (permission.hasAppOp()) {
    476                     if (permission.isAppOpAllowed()) {
    477                         permission.setAppOpAllowed(false);
    478                         // Disable the app op.
    479                         mAppOps.setUidMode(permission.getAppOp(), uid, AppOpsManager.MODE_IGNORED);
    480 
    481                         // Disabling an app op may put the app in a situation in which it
    482                         // has a handle to state it shouldn't have, so we have to kill the
    483                         // app. This matches the revoke runtime permission behavior.
    484                         killUid = uid;
    485                     }
    486 
    487                     // Mark that the permission should not be granted on upgrade
    488                     // when the app begins supporting runtime permissions.
    489                     if (!permission.shouldRevokeOnUpgrade()) {
    490                         permission.setRevokeOnUpgrade(true);
    491                         mask |= PackageManager.FLAG_PERMISSION_REVOKE_ON_UPGRADE;
    492                         flags |= PackageManager.FLAG_PERMISSION_REVOKE_ON_UPGRADE;
    493                     }
    494                 }
    495 
    496                 if (mask != 0) {
    497                     mPackageManager.updatePermissionFlags(permission.getName(),
    498                             mPackageInfo.packageName, mask, flags, mUserHandle);
    499                 }
    500 
    501                 if (killUid != -1) {
    502                     mActivityManager.killUid(uid, KILL_REASON_APP_OP_CHANGE);
    503                 }
    504             }
    505         }
    506 
    507         return true;
    508     }
    509 
    510     public void setPolicyFixed() {
    511         final int permissionCount = mPermissions.size();
    512         for (int i = 0; i < permissionCount; i++) {
    513             Permission permission = mPermissions.valueAt(i);
    514             permission.setPolicyFixed(true);
    515             mPackageManager.updatePermissionFlags(permission.getName(),
    516                     mPackageInfo.packageName,
    517                     PackageManager.FLAG_PERMISSION_POLICY_FIXED,
    518                     PackageManager.FLAG_PERMISSION_POLICY_FIXED,
    519                     mUserHandle);
    520         }
    521     }
    522 
    523     public List<Permission> getPermissions() {
    524         return new ArrayList<>(mPermissions.values());
    525     }
    526 
    527     public int getFlags() {
    528         int flags = 0;
    529         final int permissionCount = mPermissions.size();
    530         for (int i = 0; i < permissionCount; i++) {
    531             Permission permission = mPermissions.valueAt(i);
    532             flags |= permission.getFlags();
    533         }
    534         return flags;
    535     }
    536 
    537     public boolean isUserFixed() {
    538         final int permissionCount = mPermissions.size();
    539         for (int i = 0; i < permissionCount; i++) {
    540             Permission permission = mPermissions.valueAt(i);
    541             if (!permission.isUserFixed()) {
    542                 return false;
    543             }
    544         }
    545         return true;
    546     }
    547 
    548     public boolean isPolicyFixed() {
    549         final int permissionCount = mPermissions.size();
    550         for (int i = 0; i < permissionCount; i++) {
    551             Permission permission = mPermissions.valueAt(i);
    552             if (permission.isPolicyFixed()) {
    553                 return true;
    554             }
    555         }
    556         return false;
    557     }
    558 
    559     public boolean isUserSet() {
    560         final int permissionCount = mPermissions.size();
    561         for (int i = 0; i < permissionCount; i++) {
    562             Permission permission = mPermissions.valueAt(i);
    563             if (!permission.isUserSet()) {
    564                 return false;
    565             }
    566         }
    567         return true;
    568     }
    569 
    570     public boolean isSystemFixed() {
    571         final int permissionCount = mPermissions.size();
    572         for (int i = 0; i < permissionCount; i++) {
    573             Permission permission = mPermissions.valueAt(i);
    574             if (permission.isSystemFixed()) {
    575                 return true;
    576             }
    577         }
    578         return false;
    579     }
    580 
    581     @Override
    582     public int compareTo(AppPermissionGroup another) {
    583         final int result = mLabel.toString().compareTo(another.mLabel.toString());
    584         if (result == 0) {
    585             // Unbadged before badged.
    586             return mPackageInfo.applicationInfo.uid
    587                     - another.mPackageInfo.applicationInfo.uid;
    588         }
    589         return result;
    590     }
    591 
    592     @Override
    593     public boolean equals(Object obj) {
    594         if (this == obj) {
    595             return true;
    596         }
    597 
    598         if (obj == null) {
    599             return false;
    600         }
    601 
    602         if (getClass() != obj.getClass()) {
    603             return false;
    604         }
    605 
    606         AppPermissionGroup other = (AppPermissionGroup) obj;
    607 
    608         if (mName == null) {
    609             if (other.mName != null) {
    610                 return false;
    611             }
    612         } else if (!mName.equals(other.mName)) {
    613             return false;
    614         }
    615 
    616         return true;
    617     }
    618 
    619     @Override
    620     public int hashCode() {
    621         return mName != null ? mName.hashCode() : 0;
    622     }
    623 
    624     @Override
    625     public String toString() {
    626         StringBuilder builder = new StringBuilder();
    627         builder.append(getClass().getSimpleName());
    628         builder.append("{name=").append(mName);
    629         if (!mPermissions.isEmpty()) {
    630             builder.append(", <has permissions>}");
    631         } else {
    632             builder.append('}');
    633         }
    634         return builder.toString();
    635     }
    636 
    637     private void addPermission(Permission permission) {
    638         mPermissions.put(permission.getName(), permission);
    639     }
    640 }
    641