Add auto-grant permission slice API

Test: slice manager tests
Bug: 77313888
Change-Id: I21e9200c5c8cd7ff812d3f5eac997a704a9b86a2
diff --git a/api/current.txt b/api/current.txt
index 0fcf929..b3c4102 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -7306,6 +7306,7 @@
   }
 
   public abstract class SliceProvider extends android.content.ContentProvider {
+    ctor public SliceProvider(java.lang.String...);
     ctor public SliceProvider();
     method public final int delete(android.net.Uri, java.lang.String, java.lang.String[]);
     method public final java.lang.String getType(android.net.Uri);
diff --git a/core/java/android/app/slice/ISliceManager.aidl b/core/java/android/app/slice/ISliceManager.aidl
index 74e3c3e..a2aaf12 100644
--- a/core/java/android/app/slice/ISliceManager.aidl
+++ b/core/java/android/app/slice/ISliceManager.aidl
@@ -25,7 +25,8 @@
     void unpinSlice(String pkg, in Uri uri, in IBinder token);
     boolean hasSliceAccess(String pkg);
     SliceSpec[] getPinnedSpecs(in Uri uri, String pkg);
-    int checkSlicePermission(in Uri uri, String pkg, int pid, int uid);
+    int checkSlicePermission(in Uri uri, String pkg, int pid, int uid,
+            in String[] autoGrantPermissions);
     void grantPermissionFromUser(in Uri uri, String pkg, String callingPkg, boolean allSlices);
     Uri[] getPinnedSlices(String pkg);
 
diff --git a/core/java/android/app/slice/SliceManager.java b/core/java/android/app/slice/SliceManager.java
index 1afe53d..d36dea8 100644
--- a/core/java/android/app/slice/SliceManager.java
+++ b/core/java/android/app/slice/SliceManager.java
@@ -392,7 +392,8 @@
      * Does the permission check to see if a caller has access to a specific slice.
      * @hide
      */
-    public void enforceSlicePermission(Uri uri, String pkg, int pid, int uid) {
+    public void enforceSlicePermission(Uri uri, String pkg, int pid, int uid,
+            String[] autoGrantPermissions) {
         try {
             if (UserHandle.isSameApp(uid, Process.myUid())) {
                 return;
@@ -400,7 +401,7 @@
             if (pkg == null) {
                 throw new SecurityException("No pkg specified");
             }
-            int result = mService.checkSlicePermission(uri, pkg, pid, uid);
+            int result = mService.checkSlicePermission(uri, pkg, pid, uid, autoGrantPermissions);
             if (result == PERMISSION_DENIED) {
                 throw new SecurityException("User " + uid + " does not have slice permission for "
                         + uri + ".");
@@ -412,6 +413,8 @@
                         Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION
                                 | Intent.FLAG_GRANT_WRITE_URI_PERMISSION
                                 | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION);
+                // Notify a change has happened because we just granted a permission.
+                mContext.getContentResolver().notifyChange(uri, null);
             }
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
diff --git a/core/java/android/app/slice/SliceProvider.java b/core/java/android/app/slice/SliceProvider.java
index bbeb3849..fe5742d 100644
--- a/core/java/android/app/slice/SliceProvider.java
+++ b/core/java/android/app/slice/SliceProvider.java
@@ -149,10 +149,31 @@
     private static final boolean DEBUG = false;
 
     private static final long SLICE_BIND_ANR = 2000;
+    private final String[] mAutoGrantPermissions;
 
     private String mCallback;
     private SliceManager mSliceManager;
 
+    /**
+     * A version of constructing a SliceProvider that allows autogranting slice permissions
+     * to apps that hold specific platform permissions.
+     * <p>
+     * When an app tries to bind a slice from this provider that it does not have access to,
+     * This provider will check if the caller holds permissions to any of the autoGrantPermissions
+     * specified, if they do they will be granted persisted uri access to all slices of this
+     * provider.
+     *
+     * @param autoGrantPermissions List of permissions that holders are auto-granted access
+     *                             to slices.
+     */
+    public SliceProvider(@NonNull String... autoGrantPermissions) {
+        mAutoGrantPermissions = autoGrantPermissions;
+    }
+
+    public SliceProvider() {
+        mAutoGrantPermissions = new String[0];
+    }
+
     @Override
     public void attachInfo(Context context, ProviderInfo info) {
         super.attachInfo(context, info);
@@ -402,7 +423,7 @@
                 : getContext().getPackageManager().getNameForUid(callingUid);
         try {
             mSliceManager.enforceSlicePermission(sliceUri, pkg,
-                    callingPid, callingUid);
+                    callingPid, callingUid, mAutoGrantPermissions);
         } catch (SecurityException e) {
             return createPermissionSlice(getContext(), sliceUri, pkg);
         }
diff --git a/services/core/java/com/android/server/slice/SliceManagerService.java b/services/core/java/com/android/server/slice/SliceManagerService.java
index 0b7d9d0..fd0b6f1 100644
--- a/services/core/java/com/android/server/slice/SliceManagerService.java
+++ b/services/core/java/com/android/server/slice/SliceManagerService.java
@@ -168,7 +168,8 @@
     }
 
     @Override
-    public void pinSlice(String pkg, Uri uri, SliceSpec[] specs, IBinder token) throws RemoteException {
+    public void pinSlice(String pkg, Uri uri, SliceSpec[] specs, IBinder token)
+            throws RemoteException {
         verifyCaller(pkg);
         enforceAccess(pkg, uri);
         int user = Binder.getCallingUserHandle().getIdentifier();
@@ -210,7 +211,8 @@
     }
 
     @Override
-    public int checkSlicePermission(Uri uri, String pkg, int pid, int uid) throws RemoteException {
+    public int checkSlicePermission(Uri uri, String pkg, int pid, int uid,
+            String[] autoGrantPermissions) throws RemoteException {
         if (mContext.checkUriPermission(uri, pid, uid, Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
                 == PERMISSION_GRANTED) {
             return SliceManager.PERMISSION_GRANTED;
@@ -218,6 +220,11 @@
         if (hasFullSliceAccess(pkg, UserHandle.getUserId(uid))) {
             return SliceManager.PERMISSION_GRANTED;
         }
+        for (String perm : autoGrantPermissions) {
+            if (mContext.checkPermission(perm, pid, uid) == PERMISSION_GRANTED) {
+                return SliceManager.PERMISSION_USER_GRANTED;
+            }
+        }
         synchronized (mLock) {
             if (mUserGrants.contains(new SliceGrant(uri, pkg, UserHandle.getUserId(uid)))) {
                 return SliceManager.PERMISSION_USER_GRANTED;
diff --git a/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java
index 1073a80..5e2a364 100644
--- a/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java
@@ -15,8 +15,10 @@
 package com.android.server.slice;
 
 import static android.content.ContentProvider.maybeAddUserId;
+import static android.content.pm.PackageManager.PERMISSION_DENIED;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
@@ -35,9 +37,11 @@
 import android.net.Uri;
 import android.os.Binder;
 import android.os.IBinder;
+import android.os.Process;
 import android.os.RemoteException;
 import android.support.test.filters.SmallTest;
 import android.testing.AndroidTestingRunner;
+import android.testing.TestableContext;
 import android.testing.TestableLooper;
 import android.testing.TestableLooper.RunWithLooper;
 
@@ -63,6 +67,7 @@
     private SliceManagerService mService;
     private PinnedSliceState mCreatedSliceState;
     private IBinder mToken = new Binder();
+    private TestableContext mContextSpy;
 
     @Before
     public void setup() {
@@ -72,7 +77,8 @@
         mContext.addMockSystemService(AppOpsManager.class, mock(AppOpsManager.class));
         mContext.getTestablePermissions().setPermission(TEST_URI, PERMISSION_GRANTED);
 
-        mService = spy(new SliceManagerService(mContext, TestableLooper.get(this).getLooper()));
+        mContextSpy = spy(mContext);
+        mService = spy(new SliceManagerService(mContextSpy, TestableLooper.get(this).getLooper()));
         mCreatedSliceState = mock(PinnedSliceState.class);
         doReturn(mCreatedSliceState).when(mService).createPinnedSlice(eq(TEST_URI), anyString());
     }
@@ -103,4 +109,23 @@
         verify(mCreatedSliceState, never()).destroy();
     }
 
+    @Test
+    public void testCheckAutoGrantPermissions() throws RemoteException {
+        String[] testPerms = new String[] {
+                "perm1",
+                "perm2",
+        };
+        when(mContextSpy.checkUriPermission(any(), anyInt(), anyInt(), anyInt()))
+                .thenReturn(PERMISSION_DENIED);
+        when(mContextSpy.checkPermission("perm1", Process.myPid(), Process.myUid()))
+                .thenReturn(PERMISSION_DENIED);
+        when(mContextSpy.checkPermission("perm2", Process.myPid(), Process.myUid()))
+                .thenReturn(PERMISSION_GRANTED);
+        mService.checkSlicePermission(TEST_URI, mContext.getPackageName(), Process.myPid(),
+                Process.myUid(), testPerms);
+
+        verify(mContextSpy).checkPermission(eq("perm1"), eq(Process.myPid()), eq(Process.myUid()));
+        verify(mContextSpy).checkPermission(eq("perm2"), eq(Process.myPid()), eq(Process.myUid()));
+    }
+
 }
\ No newline at end of file