blob: 88b198b40be98811050c9555117d3eb9acdf497d [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 Monkb9e06a82018-01-16 15:32:53 -050029import android.content.pm.ResolveInfo;
Jason Monk74f5e362017-12-06 08:56:33 -050030import android.net.Uri;
Jason Monk38df2802018-02-22 19:28:12 -050031import android.os.Binder;
Jason Monkb9e06a82018-01-16 15:32:53 -050032import android.os.Bundle;
Jason Monk8f5f7ff2017-10-17 14:12:42 -040033import android.os.Handler;
Jason Monk38df2802018-02-22 19:28:12 -050034import android.os.IBinder;
Jason Monkac112382018-03-23 15:06:35 -040035import android.os.Process;
Jason Monk74f5e362017-12-06 08:56:33 -050036import android.os.RemoteException;
Jason Monk8f5f7ff2017-10-17 14:12:42 -040037import android.os.ServiceManager;
38import android.os.ServiceManager.ServiceNotFoundException;
Jason Monkac112382018-03-23 15:06:35 -040039import android.os.UserHandle;
Jason Monk5f8cc272018-01-16 17:57:20 -050040import android.util.Log;
Jason Monke2c64512017-12-11 15:14:54 -050041
Jason Monkb9e06a82018-01-16 15:32:53 -050042import com.android.internal.util.Preconditions;
43
44import java.util.ArrayList;
Jason Monke2c64512017-12-11 15:14:54 -050045import java.util.Arrays;
Jason Monk5f8cc272018-01-16 17:57:20 -050046import java.util.Collection;
47import java.util.Collections;
Jason Monke2c64512017-12-11 15:14:54 -050048import java.util.List;
Jason Monk8f5f7ff2017-10-17 14:12:42 -040049
50/**
Jason Monke2c64512017-12-11 15:14:54 -050051 * Class to handle interactions with {@link Slice}s.
52 * <p>
53 * The SliceManager manages permissions and pinned state for slices.
Jason Monk8f5f7ff2017-10-17 14:12:42 -040054 */
55@SystemService(Context.SLICE_SERVICE)
56public class SliceManager {
57
Jason Monk5f8cc272018-01-16 17:57:20 -050058 private static final String TAG = "SliceManager";
59
Jason Monke8f8be72018-01-21 10:10:35 -050060 /**
61 * @hide
62 */
63 public static final String ACTION_REQUEST_SLICE_PERMISSION =
64 "android.intent.action.REQUEST_SLICE_PERMISSION";
65
Mady Mellor3267ed82018-02-21 11:42:31 -080066 /**
Jason Monk5e676a22018-03-08 14:18:55 -050067 * Category used to resolve intents that can be rendered as slices.
68 * <p>
69 * This category should be included on intent filters on providers that extend
70 * {@link SliceProvider}.
71 * @see SliceProvider
72 * @see SliceProvider#onMapIntentToUri(Intent)
73 * @see #mapIntentToUri(Intent)
74 */
75 @SdkConstant(SdkConstantType.INTENT_CATEGORY)
76 public static final String CATEGORY_SLICE = "android.app.slice.category.SLICE";
77
78 /**
Mady Mellor3267ed82018-02-21 11:42:31 -080079 * The meta-data key that allows an activity to easily be linked directly to a slice.
80 * <p>
81 * An activity can be statically linked to a slice uri by including a meta-data item
82 * for this key that contains a valid slice uri for the same application declaring
83 * the activity.
Jason Monk39b9fb32018-03-27 10:45:23 -040084 *
85 * <pre class="prettyprint">
86 * {@literal
87 * <activity android:name="com.example.mypkg.MyActivity">
88 * <meta-data android:name="android.metadata.SLICE_URI"
89 * android:value="content://com.example.mypkg/main_slice" />
90 * </activity>}
91 * </pre>
92 *
93 * @see #mapIntentToUri(Intent)
94 * @see SliceProvider#onMapIntentToUri(Intent)
Mady Mellor3267ed82018-02-21 11:42:31 -080095 */
96 public static final String SLICE_METADATA_KEY = "android.metadata.SLICE_URI";
97
Jason Monk8f5f7ff2017-10-17 14:12:42 -040098 private final ISliceManager mService;
99 private final Context mContext;
Jason Monk38df2802018-02-22 19:28:12 -0500100 private final IBinder mToken = new Binder();
Jason Monk8f5f7ff2017-10-17 14:12:42 -0400101
Jason Monke2c64512017-12-11 15:14:54 -0500102 /**
Jason Monke8f8be72018-01-21 10:10:35 -0500103 * Permission denied.
104 * @hide
105 */
106 public static final int PERMISSION_DENIED = -1;
107 /**
108 * Permission granted.
109 * @hide
110 */
111 public static final int PERMISSION_GRANTED = 0;
112 /**
113 * Permission just granted by the user, and should be granted uri permission as well.
114 * @hide
115 */
116 public static final int PERMISSION_USER_GRANTED = 1;
117
118 /**
Jason Monke2c64512017-12-11 15:14:54 -0500119 * @hide
120 */
Jason Monk8f5f7ff2017-10-17 14:12:42 -0400121 public SliceManager(Context context, Handler handler) throws ServiceNotFoundException {
122 mContext = context;
123 mService = ISliceManager.Stub.asInterface(
124 ServiceManager.getServiceOrThrow(Context.SLICE_SERVICE));
125 }
Jason Monk74f5e362017-12-06 08:56:33 -0500126
127 /**
Jason Monke2c64512017-12-11 15:14:54 -0500128 * Ensures that a slice is in a pinned state.
129 * <p>
130 * Pinned state is not persisted across reboots, so apps are expected to re-pin any slices
131 * they still care about after a reboot.
Jason Monk632def12018-02-01 15:21:16 -0500132 * <p>
133 * This may only be called by apps that are the default launcher for the device
134 * or the default voice interaction service. Otherwise will throw {@link SecurityException}.
Jason Monke2c64512017-12-11 15:14:54 -0500135 *
136 * @param uri The uri of the slice being pinned.
137 * @param specs The list of supported {@link SliceSpec}s of the callback.
138 * @see SliceProvider#onSlicePinned(Uri)
Jason Monk632def12018-02-01 15:21:16 -0500139 * @see Intent#ACTION_ASSIST
140 * @see Intent#CATEGORY_HOME
Jason Monk74f5e362017-12-06 08:56:33 -0500141 */
Jason Monke2c64512017-12-11 15:14:54 -0500142 public void pinSlice(@NonNull Uri uri, @NonNull List<SliceSpec> specs) {
Jason Monk74f5e362017-12-06 08:56:33 -0500143 try {
Jason Monke2c64512017-12-11 15:14:54 -0500144 mService.pinSlice(mContext.getPackageName(), uri,
Jason Monk38df2802018-02-22 19:28:12 -0500145 specs.toArray(new SliceSpec[specs.size()]), mToken);
Jason Monk74f5e362017-12-06 08:56:33 -0500146 } catch (RemoteException e) {
147 throw e.rethrowFromSystemServer();
148 }
149 }
150
151 /**
Jason Monke2c64512017-12-11 15:14:54 -0500152 * Remove a pin for a slice.
153 * <p>
154 * If the slice has no other pins/callbacks then the slice will be unpinned.
Jason Monk632def12018-02-01 15:21:16 -0500155 * <p>
156 * This may only be called by apps that are the default launcher for the device
157 * or the default voice interaction service. Otherwise will throw {@link SecurityException}.
Jason Monke2c64512017-12-11 15:14:54 -0500158 *
159 * @param uri The uri of the slice being unpinned.
160 * @see #pinSlice
161 * @see SliceProvider#onSliceUnpinned(Uri)
Jason Monk632def12018-02-01 15:21:16 -0500162 * @see Intent#ACTION_ASSIST
163 * @see Intent#CATEGORY_HOME
Jason Monk74f5e362017-12-06 08:56:33 -0500164 */
Jason Monke2c64512017-12-11 15:14:54 -0500165 public void unpinSlice(@NonNull Uri uri) {
Jason Monk74f5e362017-12-06 08:56:33 -0500166 try {
Jason Monk38df2802018-02-22 19:28:12 -0500167 mService.unpinSlice(mContext.getPackageName(), uri, mToken);
Jason Monk74f5e362017-12-06 08:56:33 -0500168 } catch (RemoteException e) {
169 throw e.rethrowFromSystemServer();
170 }
171 }
172
173 /**
Jason Monke2c64512017-12-11 15:14:54 -0500174 * @hide
Jason Monk74f5e362017-12-06 08:56:33 -0500175 */
176 public boolean hasSliceAccess() {
177 try {
178 return mService.hasSliceAccess(mContext.getPackageName());
179 } catch (RemoteException e) {
180 throw e.rethrowFromSystemServer();
181 }
182 }
183
184 /**
Jason Monke2c64512017-12-11 15:14:54 -0500185 * Get the current set of specs for a pinned slice.
186 * <p>
187 * This is the set of specs supported for a specific pinned slice. It will take
188 * into account all clients and returns only specs supported by all.
189 * @see SliceSpec
Jason Monk74f5e362017-12-06 08:56:33 -0500190 */
Jason Monke2c64512017-12-11 15:14:54 -0500191 public @NonNull List<SliceSpec> getPinnedSpecs(Uri uri) {
Jason Monk74f5e362017-12-06 08:56:33 -0500192 try {
Jason Monke2c64512017-12-11 15:14:54 -0500193 return Arrays.asList(mService.getPinnedSpecs(uri, mContext.getPackageName()));
Jason Monk74f5e362017-12-06 08:56:33 -0500194 } catch (RemoteException e) {
195 throw e.rethrowFromSystemServer();
196 }
197 }
198
199 /**
Jason Monkf88d25e2018-03-06 20:13:24 -0500200 * Get the list of currently pinned slices for this app.
201 * @see SliceProvider#onSlicePinned
202 */
203 public @NonNull List<Uri> getPinnedSlices() {
204 try {
205 return Arrays.asList(mService.getPinnedSlices(mContext.getPackageName()));
206 } catch (RemoteException e) {
207 throw e.rethrowFromSystemServer();
208 }
209 }
210
211 /**
Jason Monk5f8cc272018-01-16 17:57:20 -0500212 * Obtains a list of slices that are descendants of the specified Uri.
213 * <p>
214 * Not all slice providers will implement this functionality, in which case,
215 * an empty collection will be returned.
216 *
217 * @param uri The uri to look for descendants under.
218 * @return All slices within the space.
219 * @see SliceProvider#onGetSliceDescendants(Uri)
220 */
221 public @NonNull Collection<Uri> getSliceDescendants(@NonNull Uri uri) {
222 ContentResolver resolver = mContext.getContentResolver();
Jason Monk632def12018-02-01 15:21:16 -0500223 try (ContentProviderClient provider = resolver.acquireContentProviderClient(uri)) {
Jason Monk5f8cc272018-01-16 17:57:20 -0500224 Bundle extras = new Bundle();
225 extras.putParcelable(SliceProvider.EXTRA_BIND_URI, uri);
Jason Monk632def12018-02-01 15:21:16 -0500226 final Bundle res = provider.call(SliceProvider.METHOD_GET_DESCENDANTS, null, extras);
Jason Monk5f8cc272018-01-16 17:57:20 -0500227 return res.getParcelableArrayList(SliceProvider.EXTRA_SLICE_DESCENDANTS);
228 } catch (RemoteException e) {
229 Log.e(TAG, "Unable to get slice descendants", e);
Jason Monk5f8cc272018-01-16 17:57:20 -0500230 }
231 return Collections.emptyList();
232 }
233
234 /**
Jason Monkb9e06a82018-01-16 15:32:53 -0500235 * Turns a slice Uri into slice content.
236 *
237 * @param uri The URI to a slice provider
238 * @param supportedSpecs List of supported specs.
239 * @return The Slice provided by the app or null if none is given.
240 * @see Slice
241 */
242 public @Nullable Slice bindSlice(@NonNull Uri uri, @NonNull List<SliceSpec> supportedSpecs) {
243 Preconditions.checkNotNull(uri, "uri");
244 ContentResolver resolver = mContext.getContentResolver();
Jason Monk632def12018-02-01 15:21:16 -0500245 try (ContentProviderClient provider = resolver.acquireContentProviderClient(uri)) {
246 if (provider == null) {
247 throw new IllegalArgumentException("Unknown URI " + uri);
248 }
Jason Monkb9e06a82018-01-16 15:32:53 -0500249 Bundle extras = new Bundle();
250 extras.putParcelable(SliceProvider.EXTRA_BIND_URI, uri);
251 extras.putParcelableArrayList(SliceProvider.EXTRA_SUPPORTED_SPECS,
252 new ArrayList<>(supportedSpecs));
Jason Monk632def12018-02-01 15:21:16 -0500253 final Bundle res = provider.call(SliceProvider.METHOD_SLICE, null, extras);
Jason Monkb9e06a82018-01-16 15:32:53 -0500254 Bundle.setDefusable(res, true);
255 if (res == null) {
256 return null;
257 }
258 return res.getParcelable(SliceProvider.EXTRA_SLICE);
259 } catch (RemoteException e) {
260 // Arbitrary and not worth documenting, as Activity
261 // Manager will kill this process shortly anyway.
262 return null;
Jason Monkb9e06a82018-01-16 15:32:53 -0500263 }
264 }
265
266 /**
Jason Monkf2008872018-02-23 08:59:31 -0500267 * Turns a slice intent into a slice uri. Expects an explicit intent.
Jason Monk5e676a22018-03-08 14:18:55 -0500268 * <p>
269 * This goes through a several stage resolution process to determine if any slice
270 * can represent this intent.
Jason Monk39b9fb32018-03-27 10:45:23 -0400271 * <ol>
272 * <li> If the intent contains data that {@link ContentResolver#getType} is
273 * {@link SliceProvider#SLICE_TYPE} then the data will be returned.</li>
274 * <li>If the intent with {@link #CATEGORY_SLICE} added resolves to a provider, then
Jason Monk5e676a22018-03-08 14:18:55 -0500275 * the provider will be asked to {@link SliceProvider#onMapIntentToUri} and that result
Jason Monk39b9fb32018-03-27 10:45:23 -0400276 * will be returned.</li>
277 * <li>Lastly, if the intent explicitly points at an activity, and that activity has
Jason Monk5e676a22018-03-08 14:18:55 -0500278 * meta-data for key {@link #SLICE_METADATA_KEY}, then the Uri specified there will be
Jason Monk39b9fb32018-03-27 10:45:23 -0400279 * returned.</li>
280 * <li>If no slice is found, then {@code null} is returned.</li>
281 * </ol>
Jason Monk7b8fef22018-01-30 16:04:14 -0500282 * @param intent The intent associated with a slice.
Jason Monkf2008872018-02-23 08:59:31 -0500283 * @return The Slice Uri provided by the app or null if none exists.
Jason Monk7b8fef22018-01-30 16:04:14 -0500284 * @see Slice
285 * @see SliceProvider#onMapIntentToUri(Intent)
286 * @see Intent
287 */
288 public @Nullable Uri mapIntentToUri(@NonNull Intent intent) {
289 Preconditions.checkNotNull(intent, "intent");
Jason Monk135f4172018-03-15 17:48:47 -0400290 Preconditions.checkArgument(intent.getComponent() != null || intent.getPackage() != null
291 || intent.getData() != null,
Jason Monk7b8fef22018-01-30 16:04:14 -0500292 "Slice intent must be explicit %s", intent);
293 ContentResolver resolver = mContext.getContentResolver();
294
295 // Check if the intent has data for the slice uri on it and use that
296 final Uri intentData = intent.getData();
297 if (intentData != null && SliceProvider.SLICE_TYPE.equals(resolver.getType(intentData))) {
298 return intentData;
299 }
300 // Otherwise ask the app
Jason Monk5e676a22018-03-08 14:18:55 -0500301 Intent queryIntent = new Intent(intent);
302 if (!queryIntent.hasCategory(CATEGORY_SLICE)) {
303 queryIntent.addCategory(CATEGORY_SLICE);
304 }
Jason Monk7b8fef22018-01-30 16:04:14 -0500305 List<ResolveInfo> providers =
Jason Monk5e676a22018-03-08 14:18:55 -0500306 mContext.getPackageManager().queryIntentContentProviders(queryIntent, 0);
Jason Monk7b8fef22018-01-30 16:04:14 -0500307 if (providers == null || providers.isEmpty()) {
Jason Monkf2008872018-02-23 08:59:31 -0500308 // There are no providers, see if this activity has a direct link.
309 ResolveInfo resolve = mContext.getPackageManager().resolveActivity(intent,
310 PackageManager.GET_META_DATA);
311 if (resolve != null && resolve.activityInfo != null
312 && resolve.activityInfo.metaData != null
313 && resolve.activityInfo.metaData.containsKey(SLICE_METADATA_KEY)) {
314 return Uri.parse(
315 resolve.activityInfo.metaData.getString(SLICE_METADATA_KEY));
316 }
317 return null;
Jason Monk7b8fef22018-01-30 16:04:14 -0500318 }
319 String authority = providers.get(0).providerInfo.authority;
320 Uri uri = new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
321 .authority(authority).build();
Jason Monk632def12018-02-01 15:21:16 -0500322 try (ContentProviderClient provider = resolver.acquireContentProviderClient(uri)) {
323 if (provider == null) {
324 throw new IllegalArgumentException("Unknown URI " + uri);
325 }
Jason Monk7b8fef22018-01-30 16:04:14 -0500326 Bundle extras = new Bundle();
327 extras.putParcelable(SliceProvider.EXTRA_INTENT, intent);
Jason Monk632def12018-02-01 15:21:16 -0500328 final Bundle res = provider.call(SliceProvider.METHOD_MAP_ONLY_INTENT, null, extras);
Jason Monk7b8fef22018-01-30 16:04:14 -0500329 if (res == null) {
330 return null;
331 }
332 return res.getParcelable(SliceProvider.EXTRA_SLICE);
333 } catch (RemoteException e) {
334 // Arbitrary and not worth documenting, as Activity
335 // Manager will kill this process shortly anyway.
336 return null;
Jason Monk7b8fef22018-01-30 16:04:14 -0500337 }
338 }
339
340 /**
Jason Monkb9e06a82018-01-16 15:32:53 -0500341 * Turns a slice intent into slice content. Expects an explicit intent. If there is no
342 * {@link android.content.ContentProvider} associated with the given intent this will throw
343 * {@link IllegalArgumentException}.
344 *
345 * @param intent The intent associated with a slice.
346 * @param supportedSpecs List of supported specs.
347 * @return The Slice provided by the app or null if none is given.
348 * @see Slice
349 * @see SliceProvider#onMapIntentToUri(Intent)
350 * @see Intent
351 */
352 public @Nullable Slice bindSlice(@NonNull Intent intent,
353 @NonNull List<SliceSpec> supportedSpecs) {
354 Preconditions.checkNotNull(intent, "intent");
Jason Monk135f4172018-03-15 17:48:47 -0400355 Preconditions.checkArgument(intent.getComponent() != null || intent.getPackage() != null
356 || intent.getData() != null,
Jason Monk7b8fef22018-01-30 16:04:14 -0500357 "Slice intent must be explicit %s", intent);
Jason Monkb9e06a82018-01-16 15:32:53 -0500358 ContentResolver resolver = mContext.getContentResolver();
359
360 // Check if the intent has data for the slice uri on it and use that
361 final Uri intentData = intent.getData();
362 if (intentData != null && SliceProvider.SLICE_TYPE.equals(resolver.getType(intentData))) {
363 return bindSlice(intentData, supportedSpecs);
364 }
365 // Otherwise ask the app
366 List<ResolveInfo> providers =
367 mContext.getPackageManager().queryIntentContentProviders(intent, 0);
Jason Monk7b8fef22018-01-30 16:04:14 -0500368 if (providers == null || providers.isEmpty()) {
Jason Monkf2008872018-02-23 08:59:31 -0500369 // There are no providers, see if this activity has a direct link.
370 ResolveInfo resolve = mContext.getPackageManager().resolveActivity(intent,
371 PackageManager.GET_META_DATA);
372 if (resolve != null && resolve.activityInfo != null
373 && resolve.activityInfo.metaData != null
374 && resolve.activityInfo.metaData.containsKey(SLICE_METADATA_KEY)) {
375 return bindSlice(Uri.parse(resolve.activityInfo.metaData
376 .getString(SLICE_METADATA_KEY)), supportedSpecs);
377 }
378 return null;
Jason Monkb9e06a82018-01-16 15:32:53 -0500379 }
380 String authority = providers.get(0).providerInfo.authority;
381 Uri uri = new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
382 .authority(authority).build();
Jason Monk632def12018-02-01 15:21:16 -0500383 try (ContentProviderClient provider = resolver.acquireContentProviderClient(uri)) {
384 if (provider == null) {
385 throw new IllegalArgumentException("Unknown URI " + uri);
386 }
Jason Monkb9e06a82018-01-16 15:32:53 -0500387 Bundle extras = new Bundle();
388 extras.putParcelable(SliceProvider.EXTRA_INTENT, intent);
389 extras.putParcelableArrayList(SliceProvider.EXTRA_SUPPORTED_SPECS,
390 new ArrayList<>(supportedSpecs));
Jason Monk632def12018-02-01 15:21:16 -0500391 final Bundle res = provider.call(SliceProvider.METHOD_MAP_INTENT, null, extras);
Jason Monkb9e06a82018-01-16 15:32:53 -0500392 if (res == null) {
393 return null;
394 }
395 return res.getParcelable(SliceProvider.EXTRA_SLICE);
396 } catch (RemoteException e) {
397 // Arbitrary and not worth documenting, as Activity
398 // Manager will kill this process shortly anyway.
399 return null;
Jason Monkb9e06a82018-01-16 15:32:53 -0500400 }
401 }
402
403 /**
Jason Monke8f8be72018-01-21 10:10:35 -0500404 * Does the permission check to see if a caller has access to a specific slice.
405 * @hide
406 */
407 public void enforceSlicePermission(Uri uri, String pkg, int pid, int uid) {
408 try {
Jason Monkac112382018-03-23 15:06:35 -0400409 if (UserHandle.isSameApp(uid, Process.myUid())) {
410 return;
411 }
Jason Monke8f8be72018-01-21 10:10:35 -0500412 if (pkg == null) {
413 throw new SecurityException("No pkg specified");
414 }
415 int result = mService.checkSlicePermission(uri, pkg, pid, uid);
416 if (result == PERMISSION_DENIED) {
417 throw new SecurityException("User " + uid + " does not have slice permission for "
418 + uri + ".");
419 }
420 if (result == PERMISSION_USER_GRANTED) {
421 // We just had a user grant of this permission and need to grant this to the app
422 // permanently.
423 mContext.grantUriPermission(pkg, uri.buildUpon().path("").build(),
424 Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION
425 | Intent.FLAG_GRANT_WRITE_URI_PERMISSION
426 | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION);
427 }
428 } catch (RemoteException e) {
429 throw e.rethrowFromSystemServer();
430 }
431 }
432
433 /**
434 * Called by SystemUI to grant a slice permission after a dialog is shown.
435 * @hide
436 */
437 public void grantPermissionFromUser(Uri uri, String pkg, boolean allSlices) {
438 try {
439 mService.grantPermissionFromUser(uri, pkg, mContext.getPackageName(), allSlices);
440 } catch (RemoteException e) {
441 throw e.rethrowFromSystemServer();
442 }
443 }
Jason Monk8f5f7ff2017-10-17 14:12:42 -0400444}