Merge changes from topics "SliceDesc", "SliceCleanup"

* changes:
  Add SliceManager#getSliceDescendants
  Update slice APIs
diff --git a/api/current.txt b/api/current.txt
index c179ca8..a958482 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -7068,8 +7068,8 @@
 
   public final class Slice implements android.os.Parcelable {
     ctor protected Slice(android.os.Parcel);
-    method public static android.app.slice.Slice bindSlice(android.content.ContentResolver, android.net.Uri, java.util.List<android.app.slice.SliceSpec>);
-    method public static android.app.slice.Slice bindSlice(android.content.Context, android.content.Intent, java.util.List<android.app.slice.SliceSpec>);
+    method public static deprecated android.app.slice.Slice bindSlice(android.content.ContentResolver, android.net.Uri, java.util.List<android.app.slice.SliceSpec>);
+    method public static deprecated android.app.slice.Slice bindSlice(android.content.Context, android.content.Intent, java.util.List<android.app.slice.SliceSpec>);
     method public int describeContents();
     method public java.util.List<java.lang.String> getHints();
     method public java.util.List<android.app.slice.SliceItem> getItems();
@@ -7155,7 +7155,10 @@
   }
 
   public class SliceManager {
+    method public android.app.slice.Slice bindSlice(android.net.Uri, java.util.List<android.app.slice.SliceSpec>);
+    method public android.app.slice.Slice bindSlice(android.content.Intent, java.util.List<android.app.slice.SliceSpec>);
     method public java.util.List<android.app.slice.SliceSpec> getPinnedSpecs(android.net.Uri);
+    method public java.util.Collection<android.net.Uri> getSliceDescendants(android.net.Uri);
     method public void pinSlice(android.net.Uri, java.util.List<android.app.slice.SliceSpec>);
     method public void registerSliceCallback(android.net.Uri, android.app.slice.SliceManager.SliceCallback, java.util.List<android.app.slice.SliceSpec>);
     method public void registerSliceCallback(android.net.Uri, android.app.slice.SliceManager.SliceCallback, java.util.List<android.app.slice.SliceSpec>, android.os.Handler);
@@ -7174,7 +7177,7 @@
     method public final java.lang.String getType(android.net.Uri);
     method public final android.net.Uri insert(android.net.Uri, android.content.ContentValues);
     method public android.app.slice.Slice onBindSlice(android.net.Uri, java.util.List<android.app.slice.SliceSpec>);
-    method public deprecated android.app.slice.Slice onBindSlice(android.net.Uri);
+    method public java.util.Collection<android.net.Uri> onGetSliceDescendants(android.net.Uri);
     method public android.net.Uri onMapIntentToUri(android.content.Intent);
     method public void onSlicePinned(android.net.Uri);
     method public void onSliceUnpinned(android.net.Uri);
diff --git a/core/java/android/app/slice/Slice.java b/core/java/android/app/slice/Slice.java
index b8fb2e3..27cd6e5 100644
--- a/core/java/android/app/slice/Slice.java
+++ b/core/java/android/app/slice/Slice.java
@@ -21,12 +21,10 @@
 import android.annotation.StringDef;
 import android.app.PendingIntent;
 import android.app.RemoteInput;
-import android.content.ContentProvider;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.IContentProvider;
 import android.content.Intent;
-import android.content.pm.ResolveInfo;
 import android.graphics.drawable.Icon;
 import android.net.Uri;
 import android.os.Bundle;
@@ -553,16 +551,11 @@
     }
 
     /**
-     * Turns a slice Uri into slice content.
-     *
-     * @param resolver ContentResolver to be used.
-     * @param uri The URI to a slice provider
-     * @param supportedSpecs List of supported specs.
-     * @return The Slice provided by the app or null if none is given.
-     * @see Slice
+     * @deprecated TO BE REMOVED.
      */
-    public static @Nullable Slice bindSlice(ContentResolver resolver, @NonNull Uri uri,
-            List<SliceSpec> supportedSpecs) {
+    @Deprecated
+    public static @Nullable Slice bindSlice(ContentResolver resolver,
+            @NonNull Uri uri, @NonNull List<SliceSpec> supportedSpecs) {
         Preconditions.checkNotNull(uri, "uri");
         IContentProvider provider = resolver.acquireProvider(uri);
         if (provider == null) {
@@ -590,60 +583,11 @@
     }
 
     /**
-     * Turns a slice intent into slice content. Expects an explicit intent. If there is no
-     * {@link ContentProvider} associated with the given intent this will throw
-     * {@link IllegalArgumentException}.
-     *
-     * @param context The context to use.
-     * @param intent The intent associated with a slice.
-     * @param supportedSpecs List of supported specs.
-     * @return The Slice provided by the app or null if none is given.
-     * @see Slice
-     * @see SliceProvider#onMapIntentToUri(Intent)
-     * @see Intent
+     * @deprecated TO BE REMOVED.
      */
+    @Deprecated
     public static @Nullable Slice bindSlice(Context context, @NonNull Intent intent,
-            List<SliceSpec> supportedSpecs) {
-        Preconditions.checkNotNull(intent, "intent");
-        Preconditions.checkArgument(intent.getComponent() != null || intent.getPackage() != null,
-                "Slice intent must be explicit " + intent);
-        ContentResolver resolver = context.getContentResolver();
-
-        // Check if the intent has data for the slice uri on it and use that
-        final Uri intentData = intent.getData();
-        if (intentData != null && SliceProvider.SLICE_TYPE.equals(resolver.getType(intentData))) {
-            return bindSlice(resolver, intentData, supportedSpecs);
-        }
-        // Otherwise ask the app
-        List<ResolveInfo> providers =
-                context.getPackageManager().queryIntentContentProviders(intent, 0);
-        if (providers == null) {
-            throw new IllegalArgumentException("Unable to resolve intent " + intent);
-        }
-        String authority = providers.get(0).providerInfo.authority;
-        Uri uri = new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
-                .authority(authority).build();
-        IContentProvider provider = resolver.acquireProvider(uri);
-        if (provider == null) {
-            throw new IllegalArgumentException("Unknown URI " + uri);
-        }
-        try {
-            Bundle extras = new Bundle();
-            extras.putParcelable(SliceProvider.EXTRA_INTENT, intent);
-            extras.putParcelableArrayList(SliceProvider.EXTRA_SUPPORTED_SPECS,
-                    new ArrayList<>(supportedSpecs));
-            final Bundle res = provider.call(resolver.getPackageName(),
-                    SliceProvider.METHOD_MAP_INTENT, null, extras);
-            if (res == null) {
-                return null;
-            }
-            return res.getParcelable(SliceProvider.EXTRA_SLICE);
-        } catch (RemoteException e) {
-            // Arbitrary and not worth documenting, as Activity
-            // Manager will kill this process shortly anyway.
-            return null;
-        } finally {
-            resolver.releaseProvider(provider);
-        }
+            @NonNull List<SliceSpec> supportedSpecs) {
+        return context.getSystemService(SliceManager.class).bindSlice(intent, supportedSpecs);
     }
 }
diff --git a/core/java/android/app/slice/SliceManager.java b/core/java/android/app/slice/SliceManager.java
index 0c5f225d..74864cb 100644
--- a/core/java/android/app/slice/SliceManager.java
+++ b/core/java/android/app/slice/SliceManager.java
@@ -17,17 +17,29 @@
 package android.app.slice;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.SystemService;
+import android.content.ContentResolver;
 import android.content.Context;
+import android.content.IContentProvider;
+import android.content.Intent;
+import android.content.pm.ResolveInfo;
 import android.net.Uri;
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.ServiceManager.ServiceNotFoundException;
 import android.util.ArrayMap;
+import android.util.Log;
 import android.util.Pair;
 
+import com.android.internal.util.Preconditions;
+
+import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
 import java.util.List;
 import java.util.concurrent.Executor;
 
@@ -39,6 +51,8 @@
 @SystemService(Context.SLICE_SERVICE)
 public class SliceManager {
 
+    private static final String TAG = "SliceManager";
+
     private final ISliceManager mService;
     private final Context mContext;
     private final ArrayMap<Pair<Uri, SliceCallback>, ISliceListener> mListenerLookup =
@@ -224,6 +238,126 @@
     }
 
     /**
+     * Obtains a list of slices that are descendants of the specified Uri.
+     * <p>
+     * Not all slice providers will implement this functionality, in which case,
+     * an empty collection will be returned.
+     *
+     * @param uri The uri to look for descendants under.
+     * @return All slices within the space.
+     * @see SliceProvider#onGetSliceDescendants(Uri)
+     */
+    public @NonNull Collection<Uri> getSliceDescendants(@NonNull Uri uri) {
+        ContentResolver resolver = mContext.getContentResolver();
+        IContentProvider provider = resolver.acquireProvider(uri);
+        try {
+            Bundle extras = new Bundle();
+            extras.putParcelable(SliceProvider.EXTRA_BIND_URI, uri);
+            final Bundle res = provider.call(resolver.getPackageName(),
+                    SliceProvider.METHOD_GET_DESCENDANTS, null, extras);
+            return res.getParcelableArrayList(SliceProvider.EXTRA_SLICE_DESCENDANTS);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Unable to get slice descendants", e);
+        } finally {
+            resolver.releaseProvider(provider);
+        }
+        return Collections.emptyList();
+    }
+
+    /**
+     * Turns a slice Uri into slice content.
+     *
+     * @param uri The URI to a slice provider
+     * @param supportedSpecs List of supported specs.
+     * @return The Slice provided by the app or null if none is given.
+     * @see Slice
+     */
+    public @Nullable Slice bindSlice(@NonNull Uri uri, @NonNull List<SliceSpec> supportedSpecs) {
+        Preconditions.checkNotNull(uri, "uri");
+        ContentResolver resolver = mContext.getContentResolver();
+        IContentProvider provider = resolver.acquireProvider(uri);
+        if (provider == null) {
+            throw new IllegalArgumentException("Unknown URI " + uri);
+        }
+        try {
+            Bundle extras = new Bundle();
+            extras.putParcelable(SliceProvider.EXTRA_BIND_URI, uri);
+            extras.putParcelableArrayList(SliceProvider.EXTRA_SUPPORTED_SPECS,
+                    new ArrayList<>(supportedSpecs));
+            final Bundle res = provider.call(resolver.getPackageName(), SliceProvider.METHOD_SLICE,
+                    null, extras);
+            Bundle.setDefusable(res, true);
+            if (res == null) {
+                return null;
+            }
+            return res.getParcelable(SliceProvider.EXTRA_SLICE);
+        } catch (RemoteException e) {
+            // Arbitrary and not worth documenting, as Activity
+            // Manager will kill this process shortly anyway.
+            return null;
+        } finally {
+            resolver.releaseProvider(provider);
+        }
+    }
+
+    /**
+     * Turns a slice intent into slice content. Expects an explicit intent. If there is no
+     * {@link android.content.ContentProvider} associated with the given intent this will throw
+     * {@link IllegalArgumentException}.
+     *
+     * @param intent The intent associated with a slice.
+     * @param supportedSpecs List of supported specs.
+     * @return The Slice provided by the app or null if none is given.
+     * @see Slice
+     * @see SliceProvider#onMapIntentToUri(Intent)
+     * @see Intent
+     */
+    public @Nullable Slice bindSlice(@NonNull Intent intent,
+            @NonNull List<SliceSpec> supportedSpecs) {
+        Preconditions.checkNotNull(intent, "intent");
+        Preconditions.checkArgument(intent.getComponent() != null || intent.getPackage() != null,
+                "Slice intent must be explicit " + intent);
+        ContentResolver resolver = mContext.getContentResolver();
+
+        // Check if the intent has data for the slice uri on it and use that
+        final Uri intentData = intent.getData();
+        if (intentData != null && SliceProvider.SLICE_TYPE.equals(resolver.getType(intentData))) {
+            return bindSlice(intentData, supportedSpecs);
+        }
+        // Otherwise ask the app
+        List<ResolveInfo> providers =
+                mContext.getPackageManager().queryIntentContentProviders(intent, 0);
+        if (providers == null) {
+            throw new IllegalArgumentException("Unable to resolve intent " + intent);
+        }
+        String authority = providers.get(0).providerInfo.authority;
+        Uri uri = new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
+                .authority(authority).build();
+        IContentProvider provider = resolver.acquireProvider(uri);
+        if (provider == null) {
+            throw new IllegalArgumentException("Unknown URI " + uri);
+        }
+        try {
+            Bundle extras = new Bundle();
+            extras.putParcelable(SliceProvider.EXTRA_INTENT, intent);
+            extras.putParcelableArrayList(SliceProvider.EXTRA_SUPPORTED_SPECS,
+                    new ArrayList<>(supportedSpecs));
+            final Bundle res = provider.call(resolver.getPackageName(),
+                    SliceProvider.METHOD_MAP_INTENT, null, extras);
+            if (res == null) {
+                return null;
+            }
+            return res.getParcelable(SliceProvider.EXTRA_SLICE);
+        } catch (RemoteException e) {
+            // Arbitrary and not worth documenting, as Activity
+            // Manager will kill this process shortly anyway.
+            return null;
+        } finally {
+            resolver.releaseProvider(provider);
+        }
+    }
+
+    /**
      * Class that listens to changes in {@link Slice}s.
      */
     public interface SliceCallback {
diff --git a/core/java/android/app/slice/SliceProvider.java b/core/java/android/app/slice/SliceProvider.java
index 8483931..aa41f14 100644
--- a/core/java/android/app/slice/SliceProvider.java
+++ b/core/java/android/app/slice/SliceProvider.java
@@ -36,6 +36,9 @@
 import android.os.UserHandle;
 import android.util.Log;
 
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
 import java.util.List;
 import java.util.concurrent.CountDownLatch;
 
@@ -113,11 +116,19 @@
     /**
      * @hide
      */
+    public static final String METHOD_GET_DESCENDANTS = "get_descendants";
+    /**
+     * @hide
+     */
     public static final String EXTRA_INTENT = "slice_intent";
     /**
      * @hide
      */
     public static final String EXTRA_SLICE = "slice";
+    /**
+     * @hide
+     */
+    public static final String EXTRA_SLICE_DESCENDANTS = "slice_descendants";
 
     private static final boolean DEBUG = false;
 
@@ -139,14 +150,6 @@
      * @see {@link Slice#HINT_PARTIAL}
      */
     public Slice onBindSlice(Uri sliceUri, List<SliceSpec> supportedSpecs) {
-        return onBindSlice(sliceUri);
-    }
-
-    /**
-     * @deprecated migrating to {@link #onBindSlice(Uri, List)}
-     */
-    @Deprecated
-    public Slice onBindSlice(Uri sliceUri) {
         return null;
     }
 
@@ -183,6 +186,20 @@
     }
 
     /**
+     * Obtains a list of slices that are descendants of the specified Uri.
+     * <p>
+     * Implementing this is optional for a SliceProvider, but does provide a good
+     * discovery mechanism for finding slice Uris.
+     *
+     * @param uri The uri to look for descendants under.
+     * @return All slices within the space.
+     * @see SliceManager#getSliceDescendants(Uri)
+     */
+    public @NonNull Collection<Uri> onGetSliceDescendants(@NonNull Uri uri) {
+        return Collections.emptyList();
+    }
+
+    /**
      * This method must be overridden if an {@link IntentFilter} is specified on the SliceProvider.
      * In that case, this method can be called and is expected to return a non-null Uri representing
      * a slice. Otherwise this will throw {@link UnsupportedOperationException}.
@@ -290,10 +307,35 @@
                         "Slice binding requires the permission BIND_SLICE");
             }
             handleUnpinSlice(uri);
+        } else if (method.equals(METHOD_GET_DESCENDANTS)) {
+            Uri uri = extras.getParcelable(EXTRA_BIND_URI);
+            Bundle b = new Bundle();
+            b.putParcelableArrayList(EXTRA_SLICE_DESCENDANTS,
+                    new ArrayList<>(handleGetDescendants(uri)));
+            return b;
         }
         return super.call(method, arg, extras);
     }
 
+    private Collection<Uri> handleGetDescendants(Uri uri) {
+        if (Looper.myLooper() == Looper.getMainLooper()) {
+            return onGetSliceDescendants(uri);
+        } else {
+            CountDownLatch latch = new CountDownLatch(1);
+            Collection<Uri>[] output = new Collection[1];
+            Handler.getMain().post(() -> {
+                output[0] = onGetSliceDescendants(uri);
+                latch.countDown();
+            });
+            try {
+                latch.await();
+                return output[0];
+            } catch (InterruptedException e) {
+                throw new RuntimeException(e);
+            }
+        }
+    }
+
     private void handlePinSlice(Uri sliceUri) {
         if (Looper.myLooper() == Looper.getMainLooper()) {
             onSlicePinned(sliceUri);