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