1 /* 2 * Copyright (C) 2018 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 android.permission; 18 19 import android.Manifest; 20 import android.annotation.IntRange; 21 import android.annotation.NonNull; 22 import android.annotation.Nullable; 23 import android.annotation.RequiresPermission; 24 import android.annotation.SystemApi; 25 import android.annotation.SystemService; 26 import android.annotation.TestApi; 27 import android.content.Context; 28 import android.content.pm.IPackageManager; 29 import android.content.pm.permission.SplitPermissionInfoParcelable; 30 import android.os.RemoteException; 31 import android.util.Log; 32 33 import com.android.internal.annotations.Immutable; 34 35 import java.util.ArrayList; 36 import java.util.Collections; 37 import java.util.List; 38 39 /** 40 * System level service for accessing the permission capabilities of the platform. 41 * 42 * @hide 43 */ 44 @TestApi 45 @SystemApi 46 @SystemService(Context.PERMISSION_SERVICE) 47 public final class PermissionManager { 48 private static final String TAG = PermissionManager.class.getName(); 49 50 private final @NonNull Context mContext; 51 52 private final IPackageManager mPackageManager; 53 54 private List<SplitPermissionInfo> mSplitPermissionInfos; 55 56 /** 57 * Creates a new instance. 58 * 59 * @param context The current context in which to operate. 60 * @hide 61 */ PermissionManager(@onNull Context context, IPackageManager packageManager)62 public PermissionManager(@NonNull Context context, IPackageManager packageManager) { 63 mContext = context; 64 mPackageManager = packageManager; 65 } 66 67 /** 68 * Gets the version of the runtime permission database. 69 * 70 * @return The database version, -1 when this is an upgrade from pre-Q, 0 when this is a fresh 71 * install. 72 * 73 * @hide 74 */ 75 @TestApi 76 @SystemApi 77 @RequiresPermission(Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY) getRuntimePermissionsVersion()78 public @IntRange(from = 0) int getRuntimePermissionsVersion() { 79 try { 80 return mPackageManager.getRuntimePermissionsVersion(mContext.getUserId()); 81 } catch (RemoteException e) { 82 throw e.rethrowFromSystemServer(); 83 } 84 } 85 86 /** 87 * Sets the version of the runtime permission database. 88 * 89 * @param version The new version. 90 * 91 * @hide 92 */ 93 @TestApi 94 @SystemApi 95 @RequiresPermission(Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY) setRuntimePermissionsVersion(@ntRangefrom = 0) int version)96 public void setRuntimePermissionsVersion(@IntRange(from = 0) int version) { 97 try { 98 mPackageManager.setRuntimePermissionsVersion(version, mContext.getUserId()); 99 } catch (RemoteException e) { 100 throw e.rethrowFromSystemServer(); 101 } 102 } 103 104 /** 105 * Get set of permissions that have been split into more granular or dependent permissions. 106 * 107 * <p>E.g. before {@link android.os.Build.VERSION_CODES#Q} an app that was granted 108 * {@link Manifest.permission#ACCESS_COARSE_LOCATION} could access he location while it was in 109 * foreground and background. On platforms after {@link android.os.Build.VERSION_CODES#Q} 110 * the location permission only grants location access while the app is in foreground. This 111 * would break apps that target before {@link android.os.Build.VERSION_CODES#Q}. Hence whenever 112 * such an old app asks for a location permission (i.e. the 113 * {@link SplitPermissionInfo#getSplitPermission()}), then the 114 * {@link Manifest.permission#ACCESS_BACKGROUND_LOCATION} permission (inside 115 * {@link SplitPermissionInfo#getNewPermissions}) is added. 116 * 117 * <p>Note: Regular apps do not have to worry about this. The platform and permission controller 118 * automatically add the new permissions where needed. 119 * 120 * @return All permissions that are split. 121 */ getSplitPermissions()122 public @NonNull List<SplitPermissionInfo> getSplitPermissions() { 123 if (mSplitPermissionInfos != null) { 124 return mSplitPermissionInfos; 125 } 126 127 List<SplitPermissionInfoParcelable> parcelableList; 128 try { 129 parcelableList = mPackageManager.getSplitPermissions(); 130 } catch (RemoteException e) { 131 Log.w(TAG, "Error getting split permissions", e); 132 return Collections.emptyList(); 133 } 134 135 mSplitPermissionInfos = splitPermissionInfoListToNonParcelableList(parcelableList); 136 137 return mSplitPermissionInfos; 138 } 139 splitPermissionInfoListToNonParcelableList( List<SplitPermissionInfoParcelable> parcelableList)140 private List<SplitPermissionInfo> splitPermissionInfoListToNonParcelableList( 141 List<SplitPermissionInfoParcelable> parcelableList) { 142 final int size = parcelableList.size(); 143 List<SplitPermissionInfo> list = new ArrayList<>(size); 144 for (int i = 0; i < size; i++) { 145 list.add(new SplitPermissionInfo(parcelableList.get(i))); 146 } 147 return list; 148 } 149 150 /** 151 * Converts a {@link List} of {@link SplitPermissionInfo} into a List of 152 * {@link SplitPermissionInfoParcelable} and returns it. 153 * @hide 154 */ splitPermissionInfoListToParcelableList( List<SplitPermissionInfo> splitPermissionsList)155 public static List<SplitPermissionInfoParcelable> splitPermissionInfoListToParcelableList( 156 List<SplitPermissionInfo> splitPermissionsList) { 157 final int size = splitPermissionsList.size(); 158 List<SplitPermissionInfoParcelable> outList = new ArrayList<>(size); 159 for (int i = 0; i < size; i++) { 160 SplitPermissionInfo info = splitPermissionsList.get(i); 161 outList.add(new SplitPermissionInfoParcelable( 162 info.getSplitPermission(), info.getNewPermissions(), info.getTargetSdk())); 163 } 164 return outList; 165 } 166 167 /** 168 * A permission that was added in a previous API level might have split into several 169 * permissions. This object describes one such split. 170 */ 171 @Immutable 172 public static final class SplitPermissionInfo { 173 private @NonNull final SplitPermissionInfoParcelable mSplitPermissionInfoParcelable; 174 175 @Override equals(@ullable Object o)176 public boolean equals(@Nullable Object o) { 177 if (this == o) return true; 178 if (o == null || getClass() != o.getClass()) return false; 179 SplitPermissionInfo that = (SplitPermissionInfo) o; 180 return mSplitPermissionInfoParcelable.equals(that.mSplitPermissionInfoParcelable); 181 } 182 183 @Override hashCode()184 public int hashCode() { 185 return mSplitPermissionInfoParcelable.hashCode(); 186 } 187 188 /** 189 * Get the permission that is split. 190 */ getSplitPermission()191 public @NonNull String getSplitPermission() { 192 return mSplitPermissionInfoParcelable.getSplitPermission(); 193 } 194 195 /** 196 * Get the permissions that are added. 197 */ getNewPermissions()198 public @NonNull List<String> getNewPermissions() { 199 return mSplitPermissionInfoParcelable.getNewPermissions(); 200 } 201 202 /** 203 * Get the target API level when the permission was split. 204 */ getTargetSdk()205 public int getTargetSdk() { 206 return mSplitPermissionInfoParcelable.getTargetSdk(); 207 } 208 209 /** 210 * Constructs a split permission. 211 * 212 * @param splitPerm old permission that will be split 213 * @param newPerms list of new permissions that {@code rootPerm} will be split into 214 * @param targetSdk apps targetting SDK versions below this will have {@code rootPerm} 215 * split into {@code newPerms} 216 * @hide 217 */ SplitPermissionInfo(@onNull String splitPerm, @NonNull List<String> newPerms, int targetSdk)218 public SplitPermissionInfo(@NonNull String splitPerm, @NonNull List<String> newPerms, 219 int targetSdk) { 220 this(new SplitPermissionInfoParcelable(splitPerm, newPerms, targetSdk)); 221 } 222 SplitPermissionInfo(@onNull SplitPermissionInfoParcelable parcelable)223 private SplitPermissionInfo(@NonNull SplitPermissionInfoParcelable parcelable) { 224 mSplitPermissionInfoParcelable = parcelable; 225 } 226 } 227 } 228