Give SliceManagerService a concept of pinned slices.

Allow launcher/assistant to pin slices. Also allow other apps
with access to a slice to pin it dynamically by adding a listener
to it. Dynamic pinnings can expire when the host app dies because
the binder will no longer be alive.

Public docs and unhiding will come in a later CL with CTS tests.

Bug: 68378571
Test: runtest --path frameworks/base/services/tests/uiservicestests
Change-Id: I1dd9a839f547fc949922b766ba4b54e6d50f3125
diff --git a/core/java/android/app/slice/SliceManager.java b/core/java/android/app/slice/SliceManager.java
index e99f676..f8e19c1 100644
--- a/core/java/android/app/slice/SliceManager.java
+++ b/core/java/android/app/slice/SliceManager.java
@@ -17,8 +17,11 @@
 package android.app.slice;
 
 import android.annotation.SystemService;
+import android.app.slice.ISliceListener.Stub;
 import android.content.Context;
+import android.net.Uri;
 import android.os.Handler;
+import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.ServiceManager.ServiceNotFoundException;
 
@@ -36,4 +39,93 @@
         mService = ISliceManager.Stub.asInterface(
                 ServiceManager.getServiceOrThrow(Context.SLICE_SERVICE));
     }
+
+    /**
+     */
+    public void addSliceListener(Uri uri, SliceListener listener, SliceSpec[] specs) {
+        try {
+            mService.addSliceListener(uri, mContext.getPackageName(), listener.mStub, specs);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     */
+    public void removeSliceListener(Uri uri, SliceListener listener) {
+        try {
+            mService.removeSliceListener(uri, mContext.getPackageName(), listener.mStub);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     */
+    public void pinSlice(Uri uri, SliceSpec[] specs) {
+        try {
+            mService.pinSlice(mContext.getPackageName(), uri, specs);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     */
+    public void unpinSlice(Uri uri) {
+        try {
+            mService.unpinSlice(mContext.getPackageName(), uri);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     */
+    public boolean hasSliceAccess() {
+        try {
+            return mService.hasSliceAccess(mContext.getPackageName());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     */
+    public SliceSpec[] getPinnedSpecs(Uri uri) {
+        try {
+            return mService.getPinnedSpecs(uri, mContext.getPackageName());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     */
+    public abstract static class SliceListener {
+        private final Handler mHandler;
+
+        /**
+         */
+        public SliceListener() {
+            this(Handler.getMain());
+        }
+
+        /**
+         */
+        public SliceListener(Handler h) {
+            mHandler = h;
+        }
+
+        /**
+         */
+        public abstract void onSliceUpdated(Slice s);
+
+        private final ISliceListener.Stub mStub = new Stub() {
+            @Override
+            public void onSliceUpdated(Slice s) throws RemoteException {
+                mHandler.post(() -> SliceListener.this.onSliceUpdated(s));
+            }
+        };
+    }
 }