blob: 9a58b971baf5ffe4d6949e8f7dcd928633b1c8f8 [file] [log] [blame]
Philip P. Moltmanndbf78b82018-12-04 13:44:27 -08001/*
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
17package android.permission;
18
Philip P. Moltmann78689522018-12-17 20:45:40 -080019import static com.android.internal.util.Preconditions.checkArgument;
Joel Galenson5f63b832018-12-19 15:38:04 -080020import static com.android.internal.util.Preconditions.checkArgumentNonnegative;
Philip P. Moltmann08cac8e2018-12-04 15:09:59 -080021import static com.android.internal.util.Preconditions.checkCollectionElementsNotNull;
Philip P. Moltmanndbf78b82018-12-04 13:44:27 -080022import static com.android.internal.util.Preconditions.checkNotNull;
Hai Zhang19821872019-01-23 16:48:40 -080023import static com.android.internal.util.Preconditions.checkStringNotEmpty;
Philip P. Moltmanndbf78b82018-12-04 13:44:27 -080024import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
25
Philip P. Moltmannbc054d82018-12-21 09:41:58 -080026import android.Manifest;
Philip P. Moltmanndbf78b82018-12-04 13:44:27 -080027import android.annotation.NonNull;
28import android.annotation.SystemApi;
29import android.app.Service;
30import android.content.Context;
31import android.content.Intent;
Philip P. Moltmann78689522018-12-17 20:45:40 -080032import android.content.pm.PackageInfo;
33import android.content.pm.PackageManager;
Philip P. Moltmanndbf78b82018-12-04 13:44:27 -080034import android.os.Bundle;
35import android.os.Handler;
36import android.os.IBinder;
Philip P. Moltmann97142e22019-01-10 17:03:42 -080037import android.os.ParcelFileDescriptor;
Philip P. Moltmanndbf78b82018-12-04 13:44:27 -080038import android.os.RemoteCallback;
Philip P. Moltmann97142e22019-01-10 17:03:42 -080039import android.os.UserHandle;
Philip P. Moltmann78689522018-12-17 20:45:40 -080040import android.util.ArrayMap;
Philip P. Moltmann97142e22019-01-10 17:03:42 -080041import android.util.Log;
Philip P. Moltmanndbf78b82018-12-04 13:44:27 -080042
Philip P. Moltmann78689522018-12-17 20:45:40 -080043import com.android.internal.util.Preconditions;
44
Philip P. Moltmann97142e22019-01-10 17:03:42 -080045import java.io.IOException;
46import java.io.OutputStream;
Philip P. Moltmann78689522018-12-17 20:45:40 -080047import java.util.ArrayList;
Philip P. Moltmanndbf78b82018-12-04 13:44:27 -080048import java.util.List;
Philip P. Moltmann78689522018-12-17 20:45:40 -080049import java.util.Map;
Philip P. Moltmanndbf78b82018-12-04 13:44:27 -080050
51/**
Philip P. Moltmannbc054d82018-12-21 09:41:58 -080052 * This service is meant to be implemented by the app controlling permissions.
Philip P. Moltmanndbf78b82018-12-04 13:44:27 -080053 *
Philip P. Moltmannbc054d82018-12-21 09:41:58 -080054 * @see PermissionController
Philip P. Moltmanndbf78b82018-12-04 13:44:27 -080055 *
56 * @hide
57 */
58@SystemApi
Philip P. Moltmannbc054d82018-12-21 09:41:58 -080059public abstract class PermissionControllerService extends Service {
Philip P. Moltmann97142e22019-01-10 17:03:42 -080060 private static final String LOG_TAG = PermissionControllerService.class.getSimpleName();
Philip P. Moltmanndbf78b82018-12-04 13:44:27 -080061
62 /**
63 * The {@link Intent} action that must be declared as handled by a service
64 * in its manifest for the system to recognize it as a runtime permission
65 * presenter service.
66 */
Philip P. Moltmannbc054d82018-12-21 09:41:58 -080067 public static final String SERVICE_INTERFACE = "android.permission.PermissionControllerService";
Philip P. Moltmanndbf78b82018-12-04 13:44:27 -080068
69 // No need for locking - always set first and never modified
70 private Handler mHandler;
71
72 @Override
73 public final void attachBaseContext(Context base) {
74 super.attachBaseContext(base);
75 mHandler = new Handler(base.getMainLooper());
76 }
77
78 /**
Philip P. Moltmann78689522018-12-17 20:45:40 -080079 * Revoke a set of runtime permissions for various apps.
80 *
81 * @param requests The permissions to revoke as {@code Map<packageName, List<permission>>}
82 * @param doDryRun Compute the permissions that would be revoked, but not actually revoke them
83 * @param reason Why the permission should be revoked
84 * @param callerPackageName The package name of the calling app
85 *
86 * @return the actually removed permissions as {@code Map<packageName, List<permission>>}
87 */
88 public abstract @NonNull Map<String, List<String>> onRevokeRuntimePermissions(
89 @NonNull Map<String, List<String>> requests, boolean doDryRun,
90 @PermissionControllerManager.Reason int reason, @NonNull String callerPackageName);
91
92 /**
Philip P. Moltmann97142e22019-01-10 17:03:42 -080093 * Create a backup of the runtime permissions.
94 *
95 * @param user The user to back up
96 * @param out The stream to write the backup to
97 */
98 public abstract void onGetRuntimePermissionsBackup(@NonNull UserHandle user,
99 @NonNull OutputStream out);
100
101 /**
Philip P. Moltmanndbf78b82018-12-04 13:44:27 -0800102 * Gets the runtime permissions for an app.
103 *
104 * @param packageName The package for which to query.
105 *
106 * @return descriptions of the runtime permissions of the app
107 */
108 public abstract @NonNull List<RuntimePermissionPresentationInfo> onGetAppPermissions(
109 @NonNull String packageName);
110
111 /**
112 * Revokes the permission {@code permissionName} for app {@code packageName}
113 *
114 * @param packageName The package for which to revoke
115 * @param permissionName The permission to revoke
116 */
117 public abstract void onRevokeRuntimePermission(@NonNull String packageName,
118 @NonNull String permissionName);
119
Philip P. Moltmann08cac8e2018-12-04 15:09:59 -0800120 /**
121 * Count how many apps have one of a set of permissions.
122 *
123 * @param permissionNames The permissions the app might have
124 * @param countOnlyGranted Count an app only if the permission is granted to the app
125 * @param countSystem Also count system apps
126 *
127 * @return the number of apps that have one of the permissions
128 */
129 public abstract int onCountPermissionApps(@NonNull List<String> permissionNames,
130 boolean countOnlyGranted, boolean countSystem);
131
Joel Galenson5f63b832018-12-19 15:38:04 -0800132 /**
133 * Count how many apps have used permissions.
134 *
135 * @param countSystem Also count system apps
136 * @param numMillis The number of milliseconds in the past to check for uses
137 *
138 * @return descriptions of the users of permissions
139 */
Hai Zhang19821872019-01-23 16:48:40 -0800140 public abstract @NonNull List<RuntimePermissionUsageInfo> onGetPermissionUsages(
141 boolean countSystem, long numMillis);
142
143 /**
144 * Check whether an application is qualified for a role.
145 *
146 * @param roleName name of the role to check for
147 * @param packageName package name of the application to check for
148 *
149 * @return whether the application is qualified for the role.
150 */
151 public abstract boolean onIsApplicationQualifiedForRole(@NonNull String roleName,
152 @NonNull String packageName);
Joel Galenson5f63b832018-12-19 15:38:04 -0800153
Philip P. Moltmanndbf78b82018-12-04 13:44:27 -0800154 @Override
155 public final IBinder onBind(Intent intent) {
Philip P. Moltmannbc054d82018-12-21 09:41:58 -0800156 return new IPermissionController.Stub() {
Philip P. Moltmanndbf78b82018-12-04 13:44:27 -0800157 @Override
Philip P. Moltmann78689522018-12-17 20:45:40 -0800158 public void revokeRuntimePermissions(
159 Bundle bundleizedRequest, boolean doDryRun, int reason,
160 String callerPackageName, RemoteCallback callback) {
161 checkNotNull(bundleizedRequest, "bundleizedRequest");
162 checkNotNull(callerPackageName);
163 checkNotNull(callback);
164
165 Map<String, List<String>> request = new ArrayMap<>();
166 for (String packageName : bundleizedRequest.keySet()) {
167 Preconditions.checkNotNull(packageName);
168
169 ArrayList<String> permissions =
170 bundleizedRequest.getStringArrayList(packageName);
171 Preconditions.checkCollectionElementsNotNull(permissions, "permissions");
172
173 request.put(packageName, permissions);
174 }
175
176 enforceCallingPermission(Manifest.permission.REVOKE_RUNTIME_PERMISSIONS, null);
177
178 // Verify callerPackageName
179 try {
180 PackageInfo pkgInfo = getPackageManager().getPackageInfo(callerPackageName, 0);
181 checkArgument(getCallingUid() == pkgInfo.applicationInfo.uid);
182 } catch (PackageManager.NameNotFoundException e) {
183 throw new RuntimeException(e);
184 }
185
186 mHandler.sendMessage(obtainMessage(
187 PermissionControllerService::revokeRuntimePermissions,
188 PermissionControllerService.this, request, doDryRun, reason,
189 callerPackageName, callback));
190 }
191
192 @Override
Philip P. Moltmann97142e22019-01-10 17:03:42 -0800193 public void getRuntimePermissionBackup(UserHandle user, ParcelFileDescriptor pipe) {
194 checkNotNull(user);
195 checkNotNull(pipe);
196
197 enforceCallingPermission(Manifest.permission.GET_RUNTIME_PERMISSIONS, null);
198
199 mHandler.sendMessage(obtainMessage(
200 PermissionControllerService::getRuntimePermissionsBackup,
201 PermissionControllerService.this, user, pipe));
202 }
203
204 @Override
Philip P. Moltmanndbf78b82018-12-04 13:44:27 -0800205 public void getAppPermissions(String packageName, RemoteCallback callback) {
206 checkNotNull(packageName, "packageName");
207 checkNotNull(callback, "callback");
208
Philip P. Moltmannbc054d82018-12-21 09:41:58 -0800209 enforceCallingPermission(Manifest.permission.GET_RUNTIME_PERMISSIONS, null);
210
Philip P. Moltmanndbf78b82018-12-04 13:44:27 -0800211 mHandler.sendMessage(
Philip P. Moltmannbc054d82018-12-21 09:41:58 -0800212 obtainMessage(PermissionControllerService::getAppPermissions,
213 PermissionControllerService.this, packageName, callback));
Philip P. Moltmanndbf78b82018-12-04 13:44:27 -0800214 }
215
216 @Override
217 public void revokeRuntimePermission(String packageName, String permissionName) {
218 checkNotNull(packageName, "packageName");
219 checkNotNull(permissionName, "permissionName");
220
Philip P. Moltmannbc054d82018-12-21 09:41:58 -0800221 enforceCallingPermission(Manifest.permission.REVOKE_RUNTIME_PERMISSIONS, null);
222
Philip P. Moltmanndbf78b82018-12-04 13:44:27 -0800223 mHandler.sendMessage(
Philip P. Moltmannbc054d82018-12-21 09:41:58 -0800224 obtainMessage(PermissionControllerService::onRevokeRuntimePermission,
225 PermissionControllerService.this, packageName, permissionName));
Philip P. Moltmanndbf78b82018-12-04 13:44:27 -0800226 }
Philip P. Moltmann08cac8e2018-12-04 15:09:59 -0800227
228 @Override
229 public void countPermissionApps(List<String> permissionNames, boolean countOnlyGranted,
230 boolean countSystem, RemoteCallback callback) {
231 checkCollectionElementsNotNull(permissionNames, "permissionNames");
232 checkNotNull(callback, "callback");
233
Philip P. Moltmannbc054d82018-12-21 09:41:58 -0800234 enforceCallingPermission(Manifest.permission.GET_RUNTIME_PERMISSIONS, null);
235
Philip P. Moltmann08cac8e2018-12-04 15:09:59 -0800236 mHandler.sendMessage(
Philip P. Moltmannbc054d82018-12-21 09:41:58 -0800237 obtainMessage(PermissionControllerService::countPermissionApps,
238 PermissionControllerService.this, permissionNames, countOnlyGranted,
239 countSystem, callback));
Philip P. Moltmann08cac8e2018-12-04 15:09:59 -0800240 }
Joel Galenson5f63b832018-12-19 15:38:04 -0800241
242 @Override
243 public void getPermissionUsages(boolean countSystem, long numMillis,
244 RemoteCallback callback) {
245 checkArgumentNonnegative(numMillis);
246 checkNotNull(callback, "callback");
247
248 enforceCallingPermission(Manifest.permission.GET_RUNTIME_PERMISSIONS, null);
249
250 mHandler.sendMessage(
251 obtainMessage(PermissionControllerService::getPermissionUsages,
252 PermissionControllerService.this, countSystem, numMillis,
253 callback));
254 }
Hai Zhang19821872019-01-23 16:48:40 -0800255
256 @Override
257 public void isApplicationQualifiedForRole(String roleName, String packageName,
258 RemoteCallback callback) {
259 checkStringNotEmpty(roleName);
260 checkStringNotEmpty(packageName);
261 checkNotNull(callback, "callback");
262
263 enforceCallingPermission(Manifest.permission.MANAGE_ROLE_HOLDERS, null);
264
265 mHandler.sendMessage(obtainMessage(
266 PermissionControllerService::isApplicationQualifiedForRole,
267 PermissionControllerService.this, roleName, packageName, callback));
268 }
Philip P. Moltmanndbf78b82018-12-04 13:44:27 -0800269 };
270 }
271
Philip P. Moltmann78689522018-12-17 20:45:40 -0800272 private void revokeRuntimePermissions(@NonNull Map<String, List<String>> requests,
273 boolean doDryRun, @PermissionControllerManager.Reason int reason,
274 @NonNull String callerPackageName, @NonNull RemoteCallback callback) {
275 Map<String, List<String>> revoked = onRevokeRuntimePermissions(requests,
276 doDryRun, reason, callerPackageName);
277
278 checkNotNull(revoked);
279 Bundle bundledizedRevoked = new Bundle();
280 for (Map.Entry<String, List<String>> appRevocation : revoked.entrySet()) {
281 checkNotNull(appRevocation.getKey());
282 checkCollectionElementsNotNull(appRevocation.getValue(), "permissions");
283
284 bundledizedRevoked.putStringArrayList(appRevocation.getKey(),
285 new ArrayList<>(appRevocation.getValue()));
286 }
287
288 Bundle result = new Bundle();
289 result.putBundle(PermissionControllerManager.KEY_RESULT, bundledizedRevoked);
290 callback.sendResult(result);
291 }
292
Philip P. Moltmann97142e22019-01-10 17:03:42 -0800293 private void getRuntimePermissionsBackup(@NonNull UserHandle user,
294 @NonNull ParcelFileDescriptor outFile) {
295 try (OutputStream out = new ParcelFileDescriptor.AutoCloseOutputStream(outFile)) {
296 onGetRuntimePermissionsBackup(user, out);
297 } catch (IOException e) {
298 Log.e(LOG_TAG, "Could not open pipe to write backup tp", e);
299 }
300 }
301
Philip P. Moltmanndbf78b82018-12-04 13:44:27 -0800302 private void getAppPermissions(@NonNull String packageName, @NonNull RemoteCallback callback) {
303 List<RuntimePermissionPresentationInfo> permissions = onGetAppPermissions(packageName);
304 if (permissions != null && !permissions.isEmpty()) {
305 Bundle result = new Bundle();
Philip P. Moltmannbc054d82018-12-21 09:41:58 -0800306 result.putParcelableList(PermissionControllerManager.KEY_RESULT, permissions);
Philip P. Moltmanndbf78b82018-12-04 13:44:27 -0800307 callback.sendResult(result);
308 } else {
309 callback.sendResult(null);
310 }
311 }
Philip P. Moltmann08cac8e2018-12-04 15:09:59 -0800312
313 private void countPermissionApps(@NonNull List<String> permissionNames,
314 boolean countOnlyGranted, boolean countSystem, @NonNull RemoteCallback callback) {
315 int numApps = onCountPermissionApps(permissionNames, countOnlyGranted, countSystem);
316
317 Bundle result = new Bundle();
Philip P. Moltmannbc054d82018-12-21 09:41:58 -0800318 result.putInt(PermissionControllerManager.KEY_RESULT, numApps);
Philip P. Moltmann08cac8e2018-12-04 15:09:59 -0800319 callback.sendResult(result);
320 }
Joel Galenson5f63b832018-12-19 15:38:04 -0800321
322 private void getPermissionUsages(boolean countSystem, long numMillis,
323 @NonNull RemoteCallback callback) {
324 List<RuntimePermissionUsageInfo> users =
Hai Zhang19821872019-01-23 16:48:40 -0800325 onGetPermissionUsages(countSystem, numMillis);
Joel Galenson5f63b832018-12-19 15:38:04 -0800326 if (users != null && !users.isEmpty()) {
327 Bundle result = new Bundle();
328 result.putParcelableList(PermissionControllerManager.KEY_RESULT, users);
329 callback.sendResult(result);
330 } else {
331 callback.sendResult(null);
332 }
333 }
Hai Zhang19821872019-01-23 16:48:40 -0800334
335 private void isApplicationQualifiedForRole(@NonNull String roleName,
336 @NonNull String packageName, @NonNull RemoteCallback callback) {
337 boolean qualified = onIsApplicationQualifiedForRole(roleName, packageName);
338 Bundle result = new Bundle();
339 result.putBoolean(PermissionControllerManager.KEY_RESULT, qualified);
340 callback.sendResult(result);
341 }
Philip P. Moltmanndbf78b82018-12-04 13:44:27 -0800342}