Home | History | Annotate | Download | only in compat
      1 /*
      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 package androidx.slice.compat;
     17 
     18 import static android.app.slice.SliceManager.CATEGORY_SLICE;
     19 import static android.app.slice.SliceManager.SLICE_METADATA_KEY;
     20 import static android.app.slice.SliceProvider.SLICE_TYPE;
     21 
     22 import static androidx.core.content.PermissionChecker.PERMISSION_DENIED;
     23 import static androidx.core.content.PermissionChecker.PERMISSION_GRANTED;
     24 
     25 import android.content.ContentProviderClient;
     26 import android.content.ContentResolver;
     27 import android.content.Context;
     28 import android.content.Intent;
     29 import android.content.SharedPreferences;
     30 import android.content.pm.PackageManager;
     31 import android.content.pm.ResolveInfo;
     32 import android.net.Uri;
     33 import android.os.Binder;
     34 import android.os.Build;
     35 import android.os.Bundle;
     36 import android.os.Handler;
     37 import android.os.Looper;
     38 import android.os.Parcelable;
     39 import android.os.Process;
     40 import android.os.RemoteException;
     41 import android.os.StrictMode;
     42 import android.util.Log;
     43 
     44 import androidx.annotation.NonNull;
     45 import androidx.annotation.RestrictTo;
     46 import androidx.annotation.RestrictTo.Scope;
     47 import androidx.collection.ArraySet;
     48 import androidx.core.util.Preconditions;
     49 import androidx.slice.Slice;
     50 import androidx.slice.SliceProvider;
     51 import androidx.slice.SliceSpec;
     52 
     53 import java.util.ArrayList;
     54 import java.util.Collection;
     55 import java.util.Collections;
     56 import java.util.List;
     57 import java.util.Set;
     58 
     59 /**
     60  * @hide
     61  */
     62 @RestrictTo(Scope.LIBRARY)
     63 public class SliceProviderCompat {
     64     public static final String PERMS_PREFIX = "slice_perms_";
     65     private static final String TAG = "SliceProviderCompat";
     66     private static final String DATA_PREFIX = "slice_data_";
     67     private static final String ALL_FILES = DATA_PREFIX + "all_slice_files";
     68 
     69     private static final long SLICE_BIND_ANR = 2000;
     70 
     71     public static final String METHOD_SLICE = "bind_slice";
     72     public static final String METHOD_MAP_INTENT = "map_slice";
     73     public static final String METHOD_PIN = "pin_slice";
     74     public static final String METHOD_UNPIN = "unpin_slice";
     75     public static final String METHOD_GET_PINNED_SPECS = "get_specs";
     76     public static final String METHOD_MAP_ONLY_INTENT = "map_only";
     77     public static final String METHOD_GET_DESCENDANTS = "get_descendants";
     78     public static final String METHOD_CHECK_PERMISSION = "check_perms";
     79     public static final String METHOD_GRANT_PERMISSION = "grant_perms";
     80     public static final String METHOD_REVOKE_PERMISSION = "revoke_perms";
     81 
     82     public static final String EXTRA_BIND_URI = "slice_uri";
     83     public static final String EXTRA_INTENT = "slice_intent";
     84     public static final String EXTRA_SLICE = "slice";
     85     public static final String EXTRA_SUPPORTED_SPECS = "specs";
     86     public static final String EXTRA_SUPPORTED_SPECS_REVS = "revs";
     87     public static final String EXTRA_PKG = "pkg";
     88     public static final String EXTRA_PROVIDER_PKG = "provider_pkg";
     89     public static final String EXTRA_SLICE_DESCENDANTS = "slice_descendants";
     90     public static final String EXTRA_UID = "uid";
     91     public static final String EXTRA_PID = "pid";
     92     public static final String EXTRA_RESULT = "result";
     93 
     94     private final Handler mHandler = new Handler(Looper.getMainLooper());
     95     private final Context mContext;
     96 
     97     private String mCallback;
     98     private final SliceProvider mProvider;
     99     private CompatPinnedList mPinnedList;
    100     private CompatPermissionManager mPermissionManager;
    101 
    102     public SliceProviderCompat(SliceProvider provider, CompatPermissionManager permissionManager,
    103             Context context) {
    104         mProvider = provider;
    105         mContext = context;
    106         String prefsFile = DATA_PREFIX + getClass().getName();
    107         SharedPreferences allFiles = mContext.getSharedPreferences(ALL_FILES, 0);
    108         Set<String> files = allFiles.getStringSet(ALL_FILES, Collections.<String>emptySet());
    109         if (!files.contains(prefsFile)) {
    110             // Make sure this is editable.
    111             files = new ArraySet<>(files);
    112             files.add(prefsFile);
    113             allFiles.edit()
    114                     .putStringSet(ALL_FILES, files)
    115                     .commit();
    116         }
    117         mPinnedList = new CompatPinnedList(mContext, prefsFile);
    118         mPermissionManager = permissionManager;
    119     }
    120 
    121     private Context getContext() {
    122         return mContext;
    123     }
    124 
    125     public String getCallingPackage() {
    126         return mProvider.getCallingPackage();
    127     }
    128 
    129     /**
    130      * Called by SliceProvider when compat is needed.
    131      */
    132     public Bundle call(String method, String arg, Bundle extras) {
    133         if (method.equals(METHOD_SLICE)) {
    134             Uri uri = extras.getParcelable(EXTRA_BIND_URI);
    135             Set<SliceSpec> specs = getSpecs(extras);
    136 
    137             Slice s = handleBindSlice(uri, specs, getCallingPackage());
    138             Bundle b = new Bundle();
    139             b.putParcelable(EXTRA_SLICE, s != null ? s.toBundle() : null);
    140             return b;
    141         } else if (method.equals(METHOD_MAP_INTENT)) {
    142             Intent intent = extras.getParcelable(EXTRA_INTENT);
    143             Uri uri = mProvider.onMapIntentToUri(intent);
    144             Bundle b = new Bundle();
    145             if (uri != null) {
    146                 Set<SliceSpec> specs = getSpecs(extras);
    147                 Slice s = handleBindSlice(uri, specs, getCallingPackage());
    148                 b.putParcelable(EXTRA_SLICE, s != null ? s.toBundle() : null);
    149             } else {
    150                 b.putParcelable(EXTRA_SLICE, null);
    151             }
    152             return b;
    153         } else if (method.equals(METHOD_MAP_ONLY_INTENT)) {
    154             Intent intent = extras.getParcelable(EXTRA_INTENT);
    155             Uri uri = mProvider.onMapIntentToUri(intent);
    156             Bundle b = new Bundle();
    157             b.putParcelable(EXTRA_SLICE, uri);
    158             return b;
    159         } else if (method.equals(METHOD_PIN)) {
    160             Uri uri = extras.getParcelable(EXTRA_BIND_URI);
    161             Set<SliceSpec> specs = getSpecs(extras);
    162             String pkg = extras.getString(EXTRA_PKG);
    163             if (mPinnedList.addPin(uri, pkg, specs)) {
    164                 handleSlicePinned(uri);
    165             }
    166             return null;
    167         } else if (method.equals(METHOD_UNPIN)) {
    168             Uri uri = extras.getParcelable(EXTRA_BIND_URI);
    169             String pkg = extras.getString(EXTRA_PKG);
    170             if (mPinnedList.removePin(uri, pkg)) {
    171                 handleSliceUnpinned(uri);
    172             }
    173             return null;
    174         } else if (method.equals(METHOD_GET_PINNED_SPECS)) {
    175             Uri uri = extras.getParcelable(EXTRA_BIND_URI);
    176             Bundle b = new Bundle();
    177             addSpecs(b, mPinnedList.getSpecs(uri));
    178             return b;
    179         } else if (method.equals(METHOD_GET_DESCENDANTS)) {
    180             Uri uri = extras.getParcelable(EXTRA_BIND_URI);
    181             Bundle b = new Bundle();
    182             b.putParcelableArrayList(EXTRA_SLICE_DESCENDANTS,
    183                     new ArrayList<>(handleGetDescendants(uri)));
    184             return b;
    185         } else if (method.equals(METHOD_CHECK_PERMISSION)) {
    186             Uri uri = extras.getParcelable(EXTRA_BIND_URI);
    187             String pkg = extras.getString(EXTRA_PKG);
    188             int pid = extras.getInt(EXTRA_PID);
    189             int uid = extras.getInt(EXTRA_UID);
    190             Bundle b = new Bundle();
    191             b.putInt(EXTRA_RESULT, mPermissionManager.checkSlicePermission(uri, pid, uid));
    192             return b;
    193         } else if (method.equals(METHOD_GRANT_PERMISSION)) {
    194             Uri uri = extras.getParcelable(EXTRA_BIND_URI);
    195             String toPkg = extras.getString(EXTRA_PKG);
    196             if (Binder.getCallingUid() != Process.myUid()) {
    197                 throw new SecurityException("Only the owning process can manage slice permissions");
    198             }
    199             mPermissionManager.grantSlicePermission(uri, toPkg);
    200         } else if (method.equals(METHOD_REVOKE_PERMISSION)) {
    201             Uri uri = extras.getParcelable(EXTRA_BIND_URI);
    202             String toPkg = extras.getString(EXTRA_PKG);
    203             if (Binder.getCallingUid() != Process.myUid()) {
    204                 throw new SecurityException("Only the owning process can manage slice permissions");
    205             }
    206             mPermissionManager.revokeSlicePermission(uri, toPkg);
    207         }
    208         return null;
    209     }
    210 
    211     private Collection<Uri> handleGetDescendants(Uri uri) {
    212         mCallback = "onGetSliceDescendants";
    213         mHandler.postDelayed(mAnr, SLICE_BIND_ANR);
    214         try {
    215             return mProvider.onGetSliceDescendants(uri);
    216         } finally {
    217             mHandler.removeCallbacks(mAnr);
    218         }
    219     }
    220 
    221     private void handleSlicePinned(final Uri sliceUri) {
    222         mCallback = "onSlicePinned";
    223         mHandler.postDelayed(mAnr, SLICE_BIND_ANR);
    224         try {
    225             mProvider.onSlicePinned(sliceUri);
    226         } finally {
    227             mHandler.removeCallbacks(mAnr);
    228         }
    229     }
    230 
    231     private void handleSliceUnpinned(final Uri sliceUri) {
    232         mCallback = "onSliceUnpinned";
    233         mHandler.postDelayed(mAnr, SLICE_BIND_ANR);
    234         try {
    235             mProvider.onSliceUnpinned(sliceUri);
    236         } finally {
    237             mHandler.removeCallbacks(mAnr);
    238         }
    239     }
    240 
    241     private Slice handleBindSlice(final Uri sliceUri, final Set<SliceSpec> specs,
    242             final String callingPkg) {
    243         // This can be removed once Slice#bindSlice is removed and everyone is using
    244         // SliceManager#bindSlice.
    245         String pkg = callingPkg != null ? callingPkg
    246                 : getContext().getPackageManager().getNameForUid(Binder.getCallingUid());
    247         if (mPermissionManager.checkSlicePermission(sliceUri, Binder.getCallingPid(),
    248                 Binder.getCallingUid()) != PERMISSION_GRANTED) {
    249             return mProvider.createPermissionSlice(getContext(), sliceUri, pkg);
    250         }
    251         return onBindSliceStrict(sliceUri, specs);
    252     }
    253 
    254     private Slice onBindSliceStrict(Uri sliceUri, Set<SliceSpec> specs) {
    255         StrictMode.ThreadPolicy oldPolicy = StrictMode.getThreadPolicy();
    256         mCallback = "onBindSlice";
    257         mHandler.postDelayed(mAnr, SLICE_BIND_ANR);
    258         try {
    259             StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
    260                     .detectAll()
    261                     .penaltyDeath()
    262                     .build());
    263             SliceProvider.setSpecs(specs);
    264             try {
    265                 return mProvider.onBindSlice(sliceUri);
    266             } finally {
    267                 SliceProvider.setSpecs(null);
    268                 mHandler.removeCallbacks(mAnr);
    269             }
    270         } finally {
    271             StrictMode.setThreadPolicy(oldPolicy);
    272         }
    273     }
    274 
    275     private final Runnable mAnr = new Runnable() {
    276         @Override
    277         public void run() {
    278             Process.sendSignal(Process.myPid(), Process.SIGNAL_QUIT);
    279             Log.wtf(TAG, "Timed out while handling slice callback " + mCallback);
    280         }
    281     };
    282 
    283     /**
    284      * Compat version of {@link Slice#bindSlice}.
    285      */
    286     public static Slice bindSlice(Context context, Uri uri,
    287             Set<SliceSpec> supportedSpecs) {
    288         ProviderHolder holder = acquireClient(context.getContentResolver(), uri);
    289         if (holder.mProvider == null) {
    290             throw new IllegalArgumentException("Unknown URI " + uri);
    291         }
    292         try {
    293             Bundle extras = new Bundle();
    294             extras.putParcelable(EXTRA_BIND_URI, uri);
    295             addSpecs(extras, supportedSpecs);
    296             final Bundle res = holder.mProvider.call(METHOD_SLICE, null, extras);
    297             if (res == null) {
    298                 return null;
    299             }
    300             Parcelable bundle = res.getParcelable(EXTRA_SLICE);
    301             if (!(bundle instanceof Bundle)) {
    302                 return null;
    303             }
    304             return new Slice((Bundle) bundle);
    305         } catch (RemoteException e) {
    306             Log.e(TAG, "Unable to bind slice", e);
    307             return null;
    308         } finally {
    309         }
    310     }
    311 
    312     /**
    313      * Compat way to push specs through the call.
    314      */
    315     public static void addSpecs(Bundle extras, Set<SliceSpec> supportedSpecs) {
    316         ArrayList<String> types = new ArrayList<>();
    317         ArrayList<Integer> revs = new ArrayList<>();
    318         for (SliceSpec spec : supportedSpecs) {
    319             types.add(spec.getType());
    320             revs.add(spec.getRevision());
    321         }
    322         extras.putStringArrayList(EXTRA_SUPPORTED_SPECS, types);
    323         extras.putIntegerArrayList(EXTRA_SUPPORTED_SPECS_REVS, revs);
    324     }
    325 
    326     /**
    327      * Compat way to push specs through the call.
    328      */
    329     public static Set<SliceSpec> getSpecs(Bundle extras) {
    330         ArraySet<SliceSpec> specs = new ArraySet<>();
    331         ArrayList<String> types = extras.getStringArrayList(EXTRA_SUPPORTED_SPECS);
    332         ArrayList<Integer> revs = extras.getIntegerArrayList(EXTRA_SUPPORTED_SPECS_REVS);
    333         for (int i = 0; i < types.size(); i++) {
    334             specs.add(new SliceSpec(types.get(i), revs.get(i)));
    335         }
    336         return specs;
    337     }
    338 
    339     /**
    340      * Compat version of {@link Slice#bindSlice}.
    341      */
    342     public static Slice bindSlice(Context context, Intent intent,
    343             Set<SliceSpec> supportedSpecs) {
    344         Preconditions.checkNotNull(intent, "intent");
    345         Preconditions.checkArgument(intent.getComponent() != null || intent.getPackage() != null
    346                 || intent.getData() != null,
    347                 String.format("Slice intent must be explicit %s", intent));
    348         ContentResolver resolver = context.getContentResolver();
    349 
    350         // Check if the intent has data for the slice uri on it and use that
    351         final Uri intentData = intent.getData();
    352         if (intentData != null && SLICE_TYPE.equals(resolver.getType(intentData))) {
    353             return bindSlice(context, intentData, supportedSpecs);
    354         }
    355         // Otherwise ask the app
    356         Intent queryIntent = new Intent(intent);
    357         if (!queryIntent.hasCategory(CATEGORY_SLICE)) {
    358             queryIntent.addCategory(CATEGORY_SLICE);
    359         }
    360         List<ResolveInfo> providers =
    361                 context.getPackageManager().queryIntentContentProviders(queryIntent, 0);
    362         if (providers == null || providers.isEmpty()) {
    363             // There are no providers, see if this activity has a direct link.
    364             ResolveInfo resolve = context.getPackageManager().resolveActivity(intent,
    365                     PackageManager.GET_META_DATA);
    366             if (resolve != null && resolve.activityInfo != null
    367                     && resolve.activityInfo.metaData != null
    368                     && resolve.activityInfo.metaData.containsKey(SLICE_METADATA_KEY)) {
    369                 return bindSlice(context, Uri.parse(
    370                         resolve.activityInfo.metaData.getString(SLICE_METADATA_KEY)),
    371                         supportedSpecs);
    372             }
    373             return null;
    374         }
    375         String authority = providers.get(0).providerInfo.authority;
    376         Uri uri = new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
    377                 .authority(authority).build();
    378         ProviderHolder holder = acquireClient(resolver, uri);
    379         if (holder.mProvider == null) {
    380             throw new IllegalArgumentException("Unknown URI " + uri);
    381         }
    382         try {
    383             Bundle extras = new Bundle();
    384             extras.putParcelable(EXTRA_INTENT, intent);
    385             addSpecs(extras, supportedSpecs);
    386             final Bundle res = holder.mProvider.call(METHOD_MAP_INTENT, null, extras);
    387             if (res == null) {
    388                 return null;
    389             }
    390             Parcelable bundle = res.getParcelable(EXTRA_SLICE);
    391             if (!(bundle instanceof Bundle)) {
    392                 return null;
    393             }
    394             return new Slice((Bundle) bundle);
    395         } catch (RemoteException e) {
    396             Log.e(TAG, "Unable to bind slice", e);
    397             return null;
    398         }
    399     }
    400 
    401     /**
    402      * Compat version of {@link android.app.slice.SliceManager#pinSlice}.
    403      */
    404     public static void pinSlice(Context context, Uri uri,
    405             Set<SliceSpec> supportedSpecs) {
    406         ProviderHolder holder = acquireClient(context.getContentResolver(), uri);
    407         if (holder.mProvider == null) {
    408             throw new IllegalArgumentException("Unknown URI " + uri);
    409         }
    410         try {
    411             Bundle extras = new Bundle();
    412             extras.putParcelable(EXTRA_BIND_URI, uri);
    413             extras.putString(EXTRA_PKG, context.getPackageName());
    414             addSpecs(extras, supportedSpecs);
    415             holder.mProvider.call(METHOD_PIN, null, extras);
    416         } catch (RemoteException e) {
    417             Log.e(TAG, "Unable to pin slice", e);
    418         }
    419     }
    420 
    421     /**
    422      * Compat version of {@link android.app.slice.SliceManager#unpinSlice}.
    423      */
    424     public static void unpinSlice(Context context, Uri uri,
    425             Set<SliceSpec> supportedSpecs) {
    426         ProviderHolder holder = acquireClient(context.getContentResolver(), uri);
    427         if (holder.mProvider == null) {
    428             throw new IllegalArgumentException("Unknown URI " + uri);
    429         }
    430         try {
    431             Bundle extras = new Bundle();
    432             extras.putParcelable(EXTRA_BIND_URI, uri);
    433             extras.putString(EXTRA_PKG, context.getPackageName());
    434             addSpecs(extras, supportedSpecs);
    435             holder.mProvider.call(METHOD_UNPIN, null, extras);
    436         } catch (RemoteException e) {
    437             Log.e(TAG, "Unable to unpin slice", e);
    438         }
    439     }
    440 
    441     /**
    442      * Compat version of {@link android.app.slice.SliceManager#getPinnedSpecs(Uri)}.
    443      */
    444     public static Set<SliceSpec> getPinnedSpecs(Context context, Uri uri) {
    445         ProviderHolder holder = acquireClient(context.getContentResolver(), uri);
    446         if (holder.mProvider == null) {
    447             throw new IllegalArgumentException("Unknown URI " + uri);
    448         }
    449         try {
    450             Bundle extras = new Bundle();
    451             extras.putParcelable(EXTRA_BIND_URI, uri);
    452             final Bundle res = holder.mProvider.call(METHOD_GET_PINNED_SPECS, null, extras);
    453             if (res != null) {
    454                 return getSpecs(res);
    455             }
    456         } catch (RemoteException e) {
    457             Log.e(TAG, "Unable to get pinned specs", e);
    458         }
    459         return null;
    460     }
    461 
    462     /**
    463      * Compat version of {@link android.app.slice.SliceManager#mapIntentToUri}.
    464      */
    465     public static Uri mapIntentToUri(Context context, Intent intent) {
    466         Preconditions.checkNotNull(intent, "intent");
    467         Preconditions.checkArgument(intent.getComponent() != null || intent.getPackage() != null
    468                 || intent.getData() != null,
    469                 String.format("Slice intent must be explicit %s", intent));
    470         ContentResolver resolver = context.getContentResolver();
    471 
    472         // Check if the intent has data for the slice uri on it and use that
    473         final Uri intentData = intent.getData();
    474         if (intentData != null && SLICE_TYPE.equals(resolver.getType(intentData))) {
    475             return intentData;
    476         }
    477         // Otherwise ask the app
    478         Intent queryIntent = new Intent(intent);
    479         if (!queryIntent.hasCategory(CATEGORY_SLICE)) {
    480             queryIntent.addCategory(CATEGORY_SLICE);
    481         }
    482         List<ResolveInfo> providers =
    483                 context.getPackageManager().queryIntentContentProviders(queryIntent, 0);
    484         if (providers == null || providers.isEmpty()) {
    485             // There are no providers, see if this activity has a direct link.
    486             ResolveInfo resolve = context.getPackageManager().resolveActivity(intent,
    487                     PackageManager.GET_META_DATA);
    488             if (resolve != null && resolve.activityInfo != null
    489                     && resolve.activityInfo.metaData != null
    490                     && resolve.activityInfo.metaData.containsKey(SLICE_METADATA_KEY)) {
    491                 return Uri.parse(
    492                         resolve.activityInfo.metaData.getString(SLICE_METADATA_KEY));
    493             }
    494             return null;
    495         }
    496         String authority = providers.get(0).providerInfo.authority;
    497         Uri uri = new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
    498                 .authority(authority).build();
    499         try (ProviderHolder holder = acquireClient(resolver, uri)) {
    500             if (holder.mProvider == null) {
    501                 throw new IllegalArgumentException("Unknown URI " + uri);
    502             }
    503             Bundle extras = new Bundle();
    504             extras.putParcelable(EXTRA_INTENT, intent);
    505             final Bundle res = holder.mProvider.call(METHOD_MAP_ONLY_INTENT, null, extras);
    506             if (res != null) {
    507                 return res.getParcelable(EXTRA_SLICE);
    508             }
    509         } catch (RemoteException e) {
    510             Log.e(TAG, "Unable to map slice", e);
    511         }
    512         return null;
    513     }
    514 
    515     /**
    516      * Compat version of {@link android.app.slice.SliceManager#getSliceDescendants(Uri)}
    517      */
    518     public static @NonNull Collection<Uri> getSliceDescendants(Context context, @NonNull Uri uri) {
    519         ContentResolver resolver = context.getContentResolver();
    520         try (ProviderHolder holder = acquireClient(resolver, uri)) {
    521             Bundle extras = new Bundle();
    522             extras.putParcelable(EXTRA_BIND_URI, uri);
    523             final Bundle res = holder.mProvider.call(METHOD_GET_DESCENDANTS, null, extras);
    524             if (res != null) {
    525                 return res.getParcelableArrayList(EXTRA_SLICE_DESCENDANTS);
    526             }
    527         } catch (RemoteException e) {
    528             Log.e(TAG, "Unable to get slice descendants", e);
    529         }
    530         return Collections.emptyList();
    531     }
    532 
    533     /**
    534      * Compat version of {@link android.app.slice.SliceManager#checkSlicePermission}.
    535      */
    536     public static int checkSlicePermission(Context context, String packageName, Uri uri, int pid,
    537             int uid) {
    538         ContentResolver resolver = context.getContentResolver();
    539         try (ProviderHolder holder = acquireClient(resolver, uri)) {
    540             Bundle extras = new Bundle();
    541             extras.putParcelable(EXTRA_BIND_URI, uri);
    542             extras.putString(EXTRA_PKG, packageName);
    543             extras.putInt(EXTRA_PID, pid);
    544             extras.putInt(EXTRA_UID, uid);
    545 
    546             final Bundle res = holder.mProvider.call(METHOD_CHECK_PERMISSION, null, extras);
    547             if (res != null) {
    548                 return res.getInt(EXTRA_RESULT);
    549             }
    550         } catch (RemoteException e) {
    551             Log.e(TAG, "Unable to check slice permission", e);
    552         }
    553         return PERMISSION_DENIED;
    554     }
    555 
    556     /**
    557      * Compat version of {@link android.app.slice.SliceManager#grantSlicePermission}.
    558      */
    559     public static void grantSlicePermission(Context context, String packageName, String toPackage,
    560             Uri uri) {
    561         ContentResolver resolver = context.getContentResolver();
    562         try (ProviderHolder holder = acquireClient(resolver, uri)) {
    563             Bundle extras = new Bundle();
    564             extras.putParcelable(EXTRA_BIND_URI, uri);
    565             extras.putString(EXTRA_PROVIDER_PKG, packageName);
    566             extras.putString(EXTRA_PKG, toPackage);
    567 
    568             holder.mProvider.call(METHOD_GRANT_PERMISSION, null, extras);
    569         } catch (RemoteException e) {
    570             Log.e(TAG, "Unable to get slice descendants", e);
    571         }
    572     }
    573 
    574     /**
    575      * Compat version of {@link android.app.slice.SliceManager#revokeSlicePermission}.
    576      */
    577     public static void revokeSlicePermission(Context context, String packageName, String toPackage,
    578             Uri uri) {
    579         ContentResolver resolver = context.getContentResolver();
    580         try (ProviderHolder holder = acquireClient(resolver, uri)) {
    581             Bundle extras = new Bundle();
    582             extras.putParcelable(EXTRA_BIND_URI, uri);
    583             extras.putString(EXTRA_PROVIDER_PKG, packageName);
    584             extras.putString(EXTRA_PKG, toPackage);
    585 
    586             holder.mProvider.call(METHOD_REVOKE_PERMISSION, null, extras);
    587         } catch (RemoteException e) {
    588             Log.e(TAG, "Unable to get slice descendants", e);
    589         }
    590     }
    591 
    592     /**
    593      * Compat version of {@link android.app.slice.SliceManager#getPinnedSlices}.
    594      */
    595     public static List<Uri> getPinnedSlices(Context context) {
    596         ArrayList<Uri> pinnedSlices = new ArrayList<>();
    597         SharedPreferences prefs = context.getSharedPreferences(ALL_FILES, 0);
    598         Set<String> prefSet = prefs.getStringSet(ALL_FILES, Collections.<String>emptySet());
    599         for (String pref : prefSet) {
    600             pinnedSlices.addAll(new CompatPinnedList(context, pref).getPinnedSlices());
    601         }
    602         return pinnedSlices;
    603     }
    604 
    605     private static ProviderHolder acquireClient(ContentResolver resolver, Uri uri) {
    606         ContentProviderClient provider = resolver.acquireContentProviderClient(uri);
    607         if (provider == null) {
    608             throw new IllegalArgumentException("No provider found for " + uri);
    609         }
    610         return new ProviderHolder(provider);
    611     }
    612 
    613     private static class ProviderHolder implements AutoCloseable {
    614         private final ContentProviderClient mProvider;
    615 
    616         ProviderHolder(ContentProviderClient provider) {
    617             this.mProvider = provider;
    618         }
    619 
    620         @Override
    621         public void close() {
    622             if (mProvider == null) return;
    623             if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
    624                 mProvider.close();
    625             } else {
    626                 mProvider.release();
    627             }
    628         }
    629     }
    630 }
    631