blob: db98ff82581c8a9deb5a761f968170b8e1b6c7b6 [file] [log] [blame]
Jason Monk8f5f7ff2017-10-17 14:12:42 -04001/*
2 * Copyright (C) 2017 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.app.slice;
18
Jason Monke2c64512017-12-11 15:14:54 -050019import android.annotation.NonNull;
Jason Monkb9e06a82018-01-16 15:32:53 -050020import android.annotation.Nullable;
Jason Monk5e676a22018-03-08 14:18:55 -050021import android.annotation.SdkConstant;
22import android.annotation.SdkConstant.SdkConstantType;
Jason Monk8f5f7ff2017-10-17 14:12:42 -040023import android.annotation.SystemService;
Jason Monk632def12018-02-01 15:21:16 -050024import android.content.ContentProviderClient;
Jason Monkb9e06a82018-01-16 15:32:53 -050025import android.content.ContentResolver;
Jason Monk8f5f7ff2017-10-17 14:12:42 -040026import android.content.Context;
Jason Monkb9e06a82018-01-16 15:32:53 -050027import android.content.Intent;
Jason Monkf2008872018-02-23 08:59:31 -050028import android.content.pm.PackageManager;
Jason Monk71888552018-03-30 14:14:51 -040029import android.content.pm.PackageManager.PermissionResult;
Jason Monkb9e06a82018-01-16 15:32:53 -050030import android.content.pm.ResolveInfo;
Jason Monk74f5e362017-12-06 08:56:33 -050031import android.net.Uri;
Jason Monk38df2802018-02-22 19:28:12 -050032import android.os.Binder;
Jason Monkb9e06a82018-01-16 15:32:53 -050033import android.os.Bundle;
Jason Monk8f5f7ff2017-10-17 14:12:42 -040034import android.os.Handler;
Jason Monk38df2802018-02-22 19:28:12 -050035import android.os.IBinder;
Jason Monkac112382018-03-23 15:06:35 -040036import android.os.Process;
Jason Monk74f5e362017-12-06 08:56:33 -050037import android.os.RemoteException;
Jason Monk8f5f7ff2017-10-17 14:12:42 -040038import android.os.ServiceManager;
39import android.os.ServiceManager.ServiceNotFoundException;
Jason Monkac112382018-03-23 15:06:35 -040040import android.os.UserHandle;
Jason Monk5f8cc272018-01-16 17:57:20 -050041import android.util.Log;
Jason Monke2c64512017-12-11 15:14:54 -050042
Jason Monkb9e06a82018-01-16 15:32:53 -050043import com.android.internal.util.Preconditions;
44
45import java.util.ArrayList;
Jason Monke2c64512017-12-11 15:14:54 -050046import java.util.Arrays;
Jason Monk5f8cc272018-01-16 17:57:20 -050047import java.util.Collection;
48import java.util.Collections;
Jason Monke2c64512017-12-11 15:14:54 -050049import java.util.List;
Jason Monk8f5f7ff2017-10-17 14:12:42 -040050
51/**
Jason Monke2c64512017-12-11 15:14:54 -050052 * Class to handle interactions with {@link Slice}s.
53 * <p>
54 * The SliceManager manages permissions and pinned state for slices.
Jason Monk8f5f7ff2017-10-17 14:12:42 -040055 */
56@SystemService(Context.SLICE_SERVICE)
57public class SliceManager {
58
Jason Monk5f8cc272018-01-16 17:57:20 -050059 private static final String TAG = "SliceManager";
60
Jason Monke8f8be72018-01-21 10:10:35 -050061 /**
62 * @hide
63 */
64 public static final String ACTION_REQUEST_SLICE_PERMISSION =
65 "android.intent.action.REQUEST_SLICE_PERMISSION";
66
Mady Mellor3267ed82018-02-21 11:42:31 -080067 /**
Jason Monk5e676a22018-03-08 14:18:55 -050068 * Category used to resolve intents that can be rendered as slices.
69 * <p>
70 * This category should be included on intent filters on providers that extend
71 * {@link SliceProvider}.
72 * @see SliceProvider
73 * @see SliceProvider#onMapIntentToUri(Intent)
74 * @see #mapIntentToUri(Intent)
75 */
76 @SdkConstant(SdkConstantType.INTENT_CATEGORY)
77 public static final String CATEGORY_SLICE = "android.app.slice.category.SLICE";
78
79 /**
Mady Mellor3267ed82018-02-21 11:42:31 -080080 * The meta-data key that allows an activity to easily be linked directly to a slice.
81 * <p>
82 * An activity can be statically linked to a slice uri by including a meta-data item
83 * for this key that contains a valid slice uri for the same application declaring
84 * the activity.
85 */
86 public static final String SLICE_METADATA_KEY = "android.metadata.SLICE_URI";
87
Jason Monk8f5f7ff2017-10-17 14:12:42 -040088 private final ISliceManager mService;
89 private final Context mContext;
Jason Monk38df2802018-02-22 19:28:12 -050090 private final IBinder mToken = new Binder();
Jason Monk8f5f7ff2017-10-17 14:12:42 -040091
Jason Monke2c64512017-12-11 15:14:54 -050092 /**
Jason Monke8f8be72018-01-21 10:10:35 -050093 * Permission denied.
94 * @hide
95 */
96 public static final int PERMISSION_DENIED = -1;
97 /**
98 * Permission granted.
99 * @hide
100 */
101 public static final int PERMISSION_GRANTED = 0;
102 /**
103 * Permission just granted by the user, and should be granted uri permission as well.
104 * @hide
105 */
106 public static final int PERMISSION_USER_GRANTED = 1;
107
108 /**
Jason Monke2c64512017-12-11 15:14:54 -0500109 * @hide
110 */
Jason Monk8f5f7ff2017-10-17 14:12:42 -0400111 public SliceManager(Context context, Handler handler) throws ServiceNotFoundException {
112 mContext = context;
113 mService = ISliceManager.Stub.asInterface(
114 ServiceManager.getServiceOrThrow(Context.SLICE_SERVICE));
115 }
Jason Monk74f5e362017-12-06 08:56:33 -0500116
117 /**
Jason Monke2c64512017-12-11 15:14:54 -0500118 * Ensures that a slice is in a pinned state.
119 * <p>
120 * Pinned state is not persisted across reboots, so apps are expected to re-pin any slices
121 * they still care about after a reboot.
Jason Monk632def12018-02-01 15:21:16 -0500122 * <p>
123 * This may only be called by apps that are the default launcher for the device
124 * or the default voice interaction service. Otherwise will throw {@link SecurityException}.
Jason Monke2c64512017-12-11 15:14:54 -0500125 *
126 * @param uri The uri of the slice being pinned.
127 * @param specs The list of supported {@link SliceSpec}s of the callback.
128 * @see SliceProvider#onSlicePinned(Uri)
Jason Monk632def12018-02-01 15:21:16 -0500129 * @see Intent#ACTION_ASSIST
130 * @see Intent#CATEGORY_HOME
Jason Monk74f5e362017-12-06 08:56:33 -0500131 */
Jason Monke2c64512017-12-11 15:14:54 -0500132 public void pinSlice(@NonNull Uri uri, @NonNull List<SliceSpec> specs) {
Jason Monk74f5e362017-12-06 08:56:33 -0500133 try {
Jason Monke2c64512017-12-11 15:14:54 -0500134 mService.pinSlice(mContext.getPackageName(), uri,
Jason Monk38df2802018-02-22 19:28:12 -0500135 specs.toArray(new SliceSpec[specs.size()]), mToken);
Jason Monk74f5e362017-12-06 08:56:33 -0500136 } catch (RemoteException e) {
137 throw e.rethrowFromSystemServer();
138 }
139 }
140
141 /**
Jason Monke2c64512017-12-11 15:14:54 -0500142 * Remove a pin for a slice.
143 * <p>
144 * If the slice has no other pins/callbacks then the slice will be unpinned.
Jason Monk632def12018-02-01 15:21:16 -0500145 * <p>
146 * This may only be called by apps that are the default launcher for the device
147 * or the default voice interaction service. Otherwise will throw {@link SecurityException}.
Jason Monke2c64512017-12-11 15:14:54 -0500148 *
149 * @param uri The uri of the slice being unpinned.
150 * @see #pinSlice
151 * @see SliceProvider#onSliceUnpinned(Uri)
Jason Monk632def12018-02-01 15:21:16 -0500152 * @see Intent#ACTION_ASSIST
153 * @see Intent#CATEGORY_HOME
Jason Monk74f5e362017-12-06 08:56:33 -0500154 */
Jason Monke2c64512017-12-11 15:14:54 -0500155 public void unpinSlice(@NonNull Uri uri) {
Jason Monk74f5e362017-12-06 08:56:33 -0500156 try {
Jason Monk38df2802018-02-22 19:28:12 -0500157 mService.unpinSlice(mContext.getPackageName(), uri, mToken);
Jason Monk74f5e362017-12-06 08:56:33 -0500158 } catch (RemoteException e) {
159 throw e.rethrowFromSystemServer();
160 }
161 }
162
163 /**
Jason Monke2c64512017-12-11 15:14:54 -0500164 * @hide
Jason Monk74f5e362017-12-06 08:56:33 -0500165 */
166 public boolean hasSliceAccess() {
167 try {
168 return mService.hasSliceAccess(mContext.getPackageName());
169 } catch (RemoteException e) {
170 throw e.rethrowFromSystemServer();
171 }
172 }
173
174 /**
Jason Monke2c64512017-12-11 15:14:54 -0500175 * Get the current set of specs for a pinned slice.
176 * <p>
177 * This is the set of specs supported for a specific pinned slice. It will take
178 * into account all clients and returns only specs supported by all.
179 * @see SliceSpec
Jason Monk74f5e362017-12-06 08:56:33 -0500180 */
Jason Monke2c64512017-12-11 15:14:54 -0500181 public @NonNull List<SliceSpec> getPinnedSpecs(Uri uri) {
Jason Monk74f5e362017-12-06 08:56:33 -0500182 try {
Jason Monke2c64512017-12-11 15:14:54 -0500183 return Arrays.asList(mService.getPinnedSpecs(uri, mContext.getPackageName()));
Jason Monk74f5e362017-12-06 08:56:33 -0500184 } catch (RemoteException e) {
185 throw e.rethrowFromSystemServer();
186 }
187 }
188
189 /**
Jason Monkf88d25e2018-03-06 20:13:24 -0500190 * Get the list of currently pinned slices for this app.
191 * @see SliceProvider#onSlicePinned
192 */
193 public @NonNull List<Uri> getPinnedSlices() {
194 try {
195 return Arrays.asList(mService.getPinnedSlices(mContext.getPackageName()));
196 } catch (RemoteException e) {
197 throw e.rethrowFromSystemServer();
198 }
199 }
200
201 /**
Jason Monk5f8cc272018-01-16 17:57:20 -0500202 * Obtains a list of slices that are descendants of the specified Uri.
203 * <p>
204 * Not all slice providers will implement this functionality, in which case,
205 * an empty collection will be returned.
206 *
207 * @param uri The uri to look for descendants under.
208 * @return All slices within the space.
209 * @see SliceProvider#onGetSliceDescendants(Uri)
210 */
211 public @NonNull Collection<Uri> getSliceDescendants(@NonNull Uri uri) {
212 ContentResolver resolver = mContext.getContentResolver();
Jason Monk632def12018-02-01 15:21:16 -0500213 try (ContentProviderClient provider = resolver.acquireContentProviderClient(uri)) {
Jason Monk5f8cc272018-01-16 17:57:20 -0500214 Bundle extras = new Bundle();
215 extras.putParcelable(SliceProvider.EXTRA_BIND_URI, uri);
Jason Monk632def12018-02-01 15:21:16 -0500216 final Bundle res = provider.call(SliceProvider.METHOD_GET_DESCENDANTS, null, extras);
Jason Monk5f8cc272018-01-16 17:57:20 -0500217 return res.getParcelableArrayList(SliceProvider.EXTRA_SLICE_DESCENDANTS);
218 } catch (RemoteException e) {
219 Log.e(TAG, "Unable to get slice descendants", e);
Jason Monk5f8cc272018-01-16 17:57:20 -0500220 }
221 return Collections.emptyList();
222 }
223
224 /**
Jason Monkb9e06a82018-01-16 15:32:53 -0500225 * Turns a slice Uri into slice content.
226 *
227 * @param uri The URI to a slice provider
228 * @param supportedSpecs List of supported specs.
229 * @return The Slice provided by the app or null if none is given.
230 * @see Slice
231 */
232 public @Nullable Slice bindSlice(@NonNull Uri uri, @NonNull List<SliceSpec> supportedSpecs) {
233 Preconditions.checkNotNull(uri, "uri");
234 ContentResolver resolver = mContext.getContentResolver();
Jason Monk632def12018-02-01 15:21:16 -0500235 try (ContentProviderClient provider = resolver.acquireContentProviderClient(uri)) {
236 if (provider == null) {
237 throw new IllegalArgumentException("Unknown URI " + uri);
238 }
Jason Monkb9e06a82018-01-16 15:32:53 -0500239 Bundle extras = new Bundle();
240 extras.putParcelable(SliceProvider.EXTRA_BIND_URI, uri);
241 extras.putParcelableArrayList(SliceProvider.EXTRA_SUPPORTED_SPECS,
242 new ArrayList<>(supportedSpecs));
Jason Monk632def12018-02-01 15:21:16 -0500243 final Bundle res = provider.call(SliceProvider.METHOD_SLICE, null, extras);
Jason Monkb9e06a82018-01-16 15:32:53 -0500244 Bundle.setDefusable(res, true);
245 if (res == null) {
246 return null;
247 }
248 return res.getParcelable(SliceProvider.EXTRA_SLICE);
249 } catch (RemoteException e) {
250 // Arbitrary and not worth documenting, as Activity
251 // Manager will kill this process shortly anyway.
252 return null;
Jason Monkb9e06a82018-01-16 15:32:53 -0500253 }
254 }
255
256 /**
Jason Monkf2008872018-02-23 08:59:31 -0500257 * Turns a slice intent into a slice uri. Expects an explicit intent.
Jason Monk5e676a22018-03-08 14:18:55 -0500258 * <p>
259 * This goes through a several stage resolution process to determine if any slice
260 * can represent this intent.
261 * - If the intent contains data that {@link ContentResolver#getType} is
262 * {@link SliceProvider#SLICE_TYPE} then the data will be returned.
263 * - If the intent with {@link #CATEGORY_SLICE} added resolves to a provider, then
264 * the provider will be asked to {@link SliceProvider#onMapIntentToUri} and that result
265 * will be returned.
266 * - Lastly, if the intent explicitly points at an activity, and that activity has
267 * meta-data for key {@link #SLICE_METADATA_KEY}, then the Uri specified there will be
268 * returned.
269 * - If no slice is found, then {@code null} is returned.
Jason Monk7b8fef22018-01-30 16:04:14 -0500270 *
271 * @param intent The intent associated with a slice.
Jason Monkf2008872018-02-23 08:59:31 -0500272 * @return The Slice Uri provided by the app or null if none exists.
Jason Monk7b8fef22018-01-30 16:04:14 -0500273 * @see Slice
274 * @see SliceProvider#onMapIntentToUri(Intent)
275 * @see Intent
276 */
277 public @Nullable Uri mapIntentToUri(@NonNull Intent intent) {
278 Preconditions.checkNotNull(intent, "intent");
Jason Monk135f4172018-03-15 17:48:47 -0400279 Preconditions.checkArgument(intent.getComponent() != null || intent.getPackage() != null
280 || intent.getData() != null,
Jason Monk7b8fef22018-01-30 16:04:14 -0500281 "Slice intent must be explicit %s", intent);
282 ContentResolver resolver = mContext.getContentResolver();
283
284 // Check if the intent has data for the slice uri on it and use that
285 final Uri intentData = intent.getData();
286 if (intentData != null && SliceProvider.SLICE_TYPE.equals(resolver.getType(intentData))) {
287 return intentData;
288 }
289 // Otherwise ask the app
Jason Monk5e676a22018-03-08 14:18:55 -0500290 Intent queryIntent = new Intent(intent);
291 if (!queryIntent.hasCategory(CATEGORY_SLICE)) {
292 queryIntent.addCategory(CATEGORY_SLICE);
293 }
Jason Monk7b8fef22018-01-30 16:04:14 -0500294 List<ResolveInfo> providers =
Jason Monk5e676a22018-03-08 14:18:55 -0500295 mContext.getPackageManager().queryIntentContentProviders(queryIntent, 0);
Jason Monk7b8fef22018-01-30 16:04:14 -0500296 if (providers == null || providers.isEmpty()) {
Jason Monkf2008872018-02-23 08:59:31 -0500297 // There are no providers, see if this activity has a direct link.
298 ResolveInfo resolve = mContext.getPackageManager().resolveActivity(intent,
299 PackageManager.GET_META_DATA);
300 if (resolve != null && resolve.activityInfo != null
301 && resolve.activityInfo.metaData != null
302 && resolve.activityInfo.metaData.containsKey(SLICE_METADATA_KEY)) {
303 return Uri.parse(
304 resolve.activityInfo.metaData.getString(SLICE_METADATA_KEY));
305 }
306 return null;
Jason Monk7b8fef22018-01-30 16:04:14 -0500307 }
308 String authority = providers.get(0).providerInfo.authority;
309 Uri uri = new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
310 .authority(authority).build();
Jason Monk632def12018-02-01 15:21:16 -0500311 try (ContentProviderClient provider = resolver.acquireContentProviderClient(uri)) {
312 if (provider == null) {
313 throw new IllegalArgumentException("Unknown URI " + uri);
314 }
Jason Monk7b8fef22018-01-30 16:04:14 -0500315 Bundle extras = new Bundle();
316 extras.putParcelable(SliceProvider.EXTRA_INTENT, intent);
Jason Monk632def12018-02-01 15:21:16 -0500317 final Bundle res = provider.call(SliceProvider.METHOD_MAP_ONLY_INTENT, null, extras);
Jason Monk7b8fef22018-01-30 16:04:14 -0500318 if (res == null) {
319 return null;
320 }
321 return res.getParcelable(SliceProvider.EXTRA_SLICE);
322 } catch (RemoteException e) {
323 // Arbitrary and not worth documenting, as Activity
324 // Manager will kill this process shortly anyway.
325 return null;
Jason Monk7b8fef22018-01-30 16:04:14 -0500326 }
327 }
328
329 /**
Jason Monkb9e06a82018-01-16 15:32:53 -0500330 * Turns a slice intent into slice content. Expects an explicit intent. If there is no
331 * {@link android.content.ContentProvider} associated with the given intent this will throw
332 * {@link IllegalArgumentException}.
333 *
334 * @param intent The intent associated with a slice.
335 * @param supportedSpecs List of supported specs.
336 * @return The Slice provided by the app or null if none is given.
337 * @see Slice
338 * @see SliceProvider#onMapIntentToUri(Intent)
339 * @see Intent
340 */
341 public @Nullable Slice bindSlice(@NonNull Intent intent,
342 @NonNull List<SliceSpec> supportedSpecs) {
343 Preconditions.checkNotNull(intent, "intent");
Jason Monk135f4172018-03-15 17:48:47 -0400344 Preconditions.checkArgument(intent.getComponent() != null || intent.getPackage() != null
345 || intent.getData() != null,
Jason Monk7b8fef22018-01-30 16:04:14 -0500346 "Slice intent must be explicit %s", intent);
Jason Monkb9e06a82018-01-16 15:32:53 -0500347 ContentResolver resolver = mContext.getContentResolver();
348
349 // Check if the intent has data for the slice uri on it and use that
350 final Uri intentData = intent.getData();
351 if (intentData != null && SliceProvider.SLICE_TYPE.equals(resolver.getType(intentData))) {
352 return bindSlice(intentData, supportedSpecs);
353 }
354 // Otherwise ask the app
355 List<ResolveInfo> providers =
356 mContext.getPackageManager().queryIntentContentProviders(intent, 0);
Jason Monk7b8fef22018-01-30 16:04:14 -0500357 if (providers == null || providers.isEmpty()) {
Jason Monkf2008872018-02-23 08:59:31 -0500358 // There are no providers, see if this activity has a direct link.
359 ResolveInfo resolve = mContext.getPackageManager().resolveActivity(intent,
360 PackageManager.GET_META_DATA);
361 if (resolve != null && resolve.activityInfo != null
362 && resolve.activityInfo.metaData != null
363 && resolve.activityInfo.metaData.containsKey(SLICE_METADATA_KEY)) {
364 return bindSlice(Uri.parse(resolve.activityInfo.metaData
365 .getString(SLICE_METADATA_KEY)), supportedSpecs);
366 }
367 return null;
Jason Monkb9e06a82018-01-16 15:32:53 -0500368 }
369 String authority = providers.get(0).providerInfo.authority;
370 Uri uri = new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
371 .authority(authority).build();
Jason Monk632def12018-02-01 15:21:16 -0500372 try (ContentProviderClient provider = resolver.acquireContentProviderClient(uri)) {
373 if (provider == null) {
374 throw new IllegalArgumentException("Unknown URI " + uri);
375 }
Jason Monkb9e06a82018-01-16 15:32:53 -0500376 Bundle extras = new Bundle();
377 extras.putParcelable(SliceProvider.EXTRA_INTENT, intent);
378 extras.putParcelableArrayList(SliceProvider.EXTRA_SUPPORTED_SPECS,
379 new ArrayList<>(supportedSpecs));
Jason Monk632def12018-02-01 15:21:16 -0500380 final Bundle res = provider.call(SliceProvider.METHOD_MAP_INTENT, null, extras);
Jason Monkb9e06a82018-01-16 15:32:53 -0500381 if (res == null) {
382 return null;
383 }
384 return res.getParcelable(SliceProvider.EXTRA_SLICE);
385 } catch (RemoteException e) {
386 // Arbitrary and not worth documenting, as Activity
387 // Manager will kill this process shortly anyway.
388 return null;
Jason Monkb9e06a82018-01-16 15:32:53 -0500389 }
390 }
391
392 /**
Jason Monk71888552018-03-30 14:14:51 -0400393 * Determine whether a particular process and user ID has been granted
394 * permission to access a specific slice URI.
395 *
396 * @param uri The uri that is being checked.
397 * @param pid The process ID being checked against. Must be &gt; 0.
398 * @param uid The user ID being checked against. A uid of 0 is the root
399 * user, which will pass every permission check.
400 *
401 * @return {@link PackageManager#PERMISSION_GRANTED} if the given
402 * pid/uid is allowed to access that uri, or
403 * {@link PackageManager#PERMISSION_DENIED} if it is not.
404 *
405 * @see #grantSlicePermission(String, Uri)
406 */
407 public @PermissionResult int checkSlicePermission(@NonNull Uri uri, int pid, int uid) {
408 // TODO: Switch off Uri permissions.
409 return mContext.checkUriPermission(uri, pid, uid,
410 Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
411 }
412
413 /**
414 * Grant permission to access a specific slice Uri to another package.
415 *
416 * @param toPackage The package you would like to allow to access the Uri.
417 * @param uri The Uri you would like to grant access to.
418 *
419 * @see #revokeSlicePermission
420 */
421 public void grantSlicePermission(@NonNull String toPackage, @NonNull Uri uri) {
422 // TODO: Switch off Uri permissions.
423 mContext.grantUriPermission(toPackage, uri,
424 Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION
425 | Intent.FLAG_GRANT_WRITE_URI_PERMISSION
426 | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION);
427 }
428
429 /**
430 * Remove permissions to access a particular content provider Uri
431 * that were previously added with {@link #grantSlicePermission} for a specific target
432 * package. The given Uri will match all previously granted Uris that are the same or a
433 * sub-path of the given Uri. That is, revoking "content://foo/target" will
434 * revoke both "content://foo/target" and "content://foo/target/sub", but not
435 * "content://foo". It will not remove any prefix grants that exist at a
436 * higher level.
437 *
438 * @param toPackage The package you would like to allow to access the Uri.
439 * @param uri The Uri you would like to revoke access to.
440 *
441 * @see #grantSlicePermission
442 */
443 public void revokeSlicePermission(@NonNull String toPackage, @NonNull Uri uri) {
444 // TODO: Switch off Uri permissions.
445 mContext.revokeUriPermission(toPackage, uri,
446 Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION
447 | Intent.FLAG_GRANT_WRITE_URI_PERMISSION
448 | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION);
449 }
450
451 /**
Jason Monke8f8be72018-01-21 10:10:35 -0500452 * Does the permission check to see if a caller has access to a specific slice.
453 * @hide
454 */
Jason Monk42e03f82018-03-30 11:26:56 -0400455 public void enforceSlicePermission(Uri uri, String pkg, int pid, int uid,
456 String[] autoGrantPermissions) {
Jason Monke8f8be72018-01-21 10:10:35 -0500457 try {
Jason Monkac112382018-03-23 15:06:35 -0400458 if (UserHandle.isSameApp(uid, Process.myUid())) {
459 return;
460 }
Jason Monke8f8be72018-01-21 10:10:35 -0500461 if (pkg == null) {
462 throw new SecurityException("No pkg specified");
463 }
Jason Monk42e03f82018-03-30 11:26:56 -0400464 int result = mService.checkSlicePermission(uri, pkg, pid, uid, autoGrantPermissions);
Jason Monke8f8be72018-01-21 10:10:35 -0500465 if (result == PERMISSION_DENIED) {
466 throw new SecurityException("User " + uid + " does not have slice permission for "
467 + uri + ".");
468 }
469 if (result == PERMISSION_USER_GRANTED) {
470 // We just had a user grant of this permission and need to grant this to the app
471 // permanently.
472 mContext.grantUriPermission(pkg, uri.buildUpon().path("").build(),
473 Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION
474 | Intent.FLAG_GRANT_WRITE_URI_PERMISSION
475 | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION);
Jason Monk42e03f82018-03-30 11:26:56 -0400476 // Notify a change has happened because we just granted a permission.
477 mContext.getContentResolver().notifyChange(uri, null);
Jason Monke8f8be72018-01-21 10:10:35 -0500478 }
479 } catch (RemoteException e) {
480 throw e.rethrowFromSystemServer();
481 }
482 }
483
484 /**
485 * Called by SystemUI to grant a slice permission after a dialog is shown.
486 * @hide
487 */
488 public void grantPermissionFromUser(Uri uri, String pkg, boolean allSlices) {
489 try {
490 mService.grantPermissionFromUser(uri, pkg, mContext.getPackageName(), allSlices);
491 } catch (RemoteException e) {
492 throw e.rethrowFromSystemServer();
493 }
494 }
Jason Monk8f5f7ff2017-10-17 14:12:42 -0400495}