blob: 0c5f225d515eba8bf6ecb31d906c538c6ee81bff [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 Monk8f5f7ff2017-10-17 14:12:42 -040020import android.annotation.SystemService;
21import android.content.Context;
Jason Monk74f5e362017-12-06 08:56:33 -050022import android.net.Uri;
Jason Monk8f5f7ff2017-10-17 14:12:42 -040023import android.os.Handler;
Jason Monk74f5e362017-12-06 08:56:33 -050024import android.os.RemoteException;
Jason Monk8f5f7ff2017-10-17 14:12:42 -040025import android.os.ServiceManager;
26import android.os.ServiceManager.ServiceNotFoundException;
Jason Monke2c64512017-12-11 15:14:54 -050027import android.util.ArrayMap;
28import android.util.Pair;
29
30import java.util.Arrays;
31import java.util.List;
32import java.util.concurrent.Executor;
Jason Monk8f5f7ff2017-10-17 14:12:42 -040033
34/**
Jason Monke2c64512017-12-11 15:14:54 -050035 * Class to handle interactions with {@link Slice}s.
36 * <p>
37 * The SliceManager manages permissions and pinned state for slices.
Jason Monk8f5f7ff2017-10-17 14:12:42 -040038 */
39@SystemService(Context.SLICE_SERVICE)
40public class SliceManager {
41
42 private final ISliceManager mService;
43 private final Context mContext;
Jason Monke2c64512017-12-11 15:14:54 -050044 private final ArrayMap<Pair<Uri, SliceCallback>, ISliceListener> mListenerLookup =
45 new ArrayMap<>();
Jason Monk8f5f7ff2017-10-17 14:12:42 -040046
Jason Monke2c64512017-12-11 15:14:54 -050047 /**
48 * @hide
49 */
Jason Monk8f5f7ff2017-10-17 14:12:42 -040050 public SliceManager(Context context, Handler handler) throws ServiceNotFoundException {
51 mContext = context;
52 mService = ISliceManager.Stub.asInterface(
53 ServiceManager.getServiceOrThrow(Context.SLICE_SERVICE));
54 }
Jason Monk74f5e362017-12-06 08:56:33 -050055
56 /**
Jason Monke2c64512017-12-11 15:14:54 -050057 * Adds a callback to a specific slice uri.
58 * <p>
59 * This is a convenience that performs a few slice actions at once. It will put
60 * the slice in a pinned state since there is a callback attached. It will also
61 * listen for content changes, when a content change observes, the android system
62 * will bind the new slice and provide it to all registered {@link SliceCallback}s.
63 *
64 * @param uri The uri of the slice being listened to.
65 * @param callback The listener that should receive the callbacks.
66 * @param specs The list of supported {@link SliceSpec}s of the callback.
67 * @see SliceProvider#onSlicePinned(Uri)
Jason Monk74f5e362017-12-06 08:56:33 -050068 */
Jason Monke2c64512017-12-11 15:14:54 -050069 public void registerSliceCallback(@NonNull Uri uri, @NonNull SliceCallback callback,
70 @NonNull List<SliceSpec> specs) {
71 registerSliceCallback(uri, callback, specs, Handler.getMain());
72 }
73
74 /**
75 * Adds a callback to a specific slice uri.
76 * <p>
77 * This is a convenience that performs a few slice actions at once. It will put
78 * the slice in a pinned state since there is a callback attached. It will also
79 * listen for content changes, when a content change observes, the android system
80 * will bind the new slice and provide it to all registered {@link SliceCallback}s.
81 *
82 * @param uri The uri of the slice being listened to.
83 * @param callback The listener that should receive the callbacks.
84 * @param specs The list of supported {@link SliceSpec}s of the callback.
85 * @see SliceProvider#onSlicePinned(Uri)
86 */
87 public void registerSliceCallback(@NonNull Uri uri, @NonNull SliceCallback callback,
88 @NonNull List<SliceSpec> specs, Handler handler) {
Jason Monk74f5e362017-12-06 08:56:33 -050089 try {
Jason Monke2c64512017-12-11 15:14:54 -050090 mService.addSliceListener(uri, mContext.getPackageName(),
91 getListener(uri, callback, new ISliceListener.Stub() {
92 @Override
93 public void onSliceUpdated(Slice s) throws RemoteException {
94 handler.post(() -> callback.onSliceUpdated(s));
95 }
96 }), specs.toArray(new SliceSpec[specs.size()]));
Jason Monk74f5e362017-12-06 08:56:33 -050097 } catch (RemoteException e) {
98 throw e.rethrowFromSystemServer();
99 }
100 }
101
102 /**
Jason Monke2c64512017-12-11 15:14:54 -0500103 * Adds a callback to a specific slice uri.
104 * <p>
105 * This is a convenience that performs a few slice actions at once. It will put
106 * the slice in a pinned state since there is a callback attached. It will also
107 * listen for content changes, when a content change observes, the android system
108 * will bind the new slice and provide it to all registered {@link SliceCallback}s.
109 *
110 * @param uri The uri of the slice being listened to.
111 * @param callback The listener that should receive the callbacks.
112 * @param specs The list of supported {@link SliceSpec}s of the callback.
113 * @see SliceProvider#onSlicePinned(Uri)
Jason Monk74f5e362017-12-06 08:56:33 -0500114 */
Jason Monke2c64512017-12-11 15:14:54 -0500115 public void registerSliceCallback(@NonNull Uri uri, @NonNull SliceCallback callback,
116 @NonNull List<SliceSpec> specs, Executor executor) {
Jason Monk74f5e362017-12-06 08:56:33 -0500117 try {
Jason Monke2c64512017-12-11 15:14:54 -0500118 mService.addSliceListener(uri, mContext.getPackageName(),
119 getListener(uri, callback, new ISliceListener.Stub() {
120 @Override
121 public void onSliceUpdated(Slice s) throws RemoteException {
122 executor.execute(() -> callback.onSliceUpdated(s));
123 }
124 }), specs.toArray(new SliceSpec[specs.size()]));
125 } catch (RemoteException e) {
126 throw e.rethrowFromSystemServer();
127 }
128 }
129
130 private ISliceListener getListener(Uri uri, SliceCallback callback,
131 ISliceListener listener) {
132 Pair<Uri, SliceCallback> key = new Pair<>(uri, callback);
133 if (mListenerLookup.containsKey(key)) {
134 try {
135 mService.removeSliceListener(uri, mContext.getPackageName(),
136 mListenerLookup.get(key));
137 } catch (RemoteException e) {
138 throw e.rethrowFromSystemServer();
139 }
140 }
141 mListenerLookup.put(key, listener);
142 return listener;
143 }
144
145 /**
146 * Removes a callback for a specific slice uri.
147 * <p>
148 * Removes the app from the pinned state (if there are no other apps/callbacks pinning it)
149 * in addition to removing the callback.
150 *
151 * @param uri The uri of the slice being listened to
152 * @param callback The listener that should no longer receive callbacks.
153 * @see #registerSliceCallback
154 */
155 public void unregisterSliceCallback(@NonNull Uri uri, @NonNull SliceCallback callback) {
156 try {
157 mService.removeSliceListener(uri, mContext.getPackageName(),
158 mListenerLookup.remove(new Pair<>(uri, callback)));
Jason Monk74f5e362017-12-06 08:56:33 -0500159 } catch (RemoteException e) {
160 throw e.rethrowFromSystemServer();
161 }
162 }
163
164 /**
Jason Monke2c64512017-12-11 15:14:54 -0500165 * Ensures that a slice is in a pinned state.
166 * <p>
167 * Pinned state is not persisted across reboots, so apps are expected to re-pin any slices
168 * they still care about after a reboot.
169 *
170 * @param uri The uri of the slice being pinned.
171 * @param specs The list of supported {@link SliceSpec}s of the callback.
172 * @see SliceProvider#onSlicePinned(Uri)
Jason Monk74f5e362017-12-06 08:56:33 -0500173 */
Jason Monke2c64512017-12-11 15:14:54 -0500174 public void pinSlice(@NonNull Uri uri, @NonNull List<SliceSpec> specs) {
Jason Monk74f5e362017-12-06 08:56:33 -0500175 try {
Jason Monke2c64512017-12-11 15:14:54 -0500176 mService.pinSlice(mContext.getPackageName(), uri,
177 specs.toArray(new SliceSpec[specs.size()]));
Jason Monk74f5e362017-12-06 08:56:33 -0500178 } catch (RemoteException e) {
179 throw e.rethrowFromSystemServer();
180 }
181 }
182
183 /**
Jason Monke2c64512017-12-11 15:14:54 -0500184 * Remove a pin for a slice.
185 * <p>
186 * If the slice has no other pins/callbacks then the slice will be unpinned.
187 *
188 * @param uri The uri of the slice being unpinned.
189 * @see #pinSlice
190 * @see SliceProvider#onSliceUnpinned(Uri)
Jason Monk74f5e362017-12-06 08:56:33 -0500191 */
Jason Monke2c64512017-12-11 15:14:54 -0500192 public void unpinSlice(@NonNull Uri uri) {
Jason Monk74f5e362017-12-06 08:56:33 -0500193 try {
194 mService.unpinSlice(mContext.getPackageName(), uri);
195 } catch (RemoteException e) {
196 throw e.rethrowFromSystemServer();
197 }
198 }
199
200 /**
Jason Monke2c64512017-12-11 15:14:54 -0500201 * @hide
Jason Monk74f5e362017-12-06 08:56:33 -0500202 */
203 public boolean hasSliceAccess() {
204 try {
205 return mService.hasSliceAccess(mContext.getPackageName());
206 } catch (RemoteException e) {
207 throw e.rethrowFromSystemServer();
208 }
209 }
210
211 /**
Jason Monke2c64512017-12-11 15:14:54 -0500212 * Get the current set of specs for a pinned slice.
213 * <p>
214 * This is the set of specs supported for a specific pinned slice. It will take
215 * into account all clients and returns only specs supported by all.
216 * @see SliceSpec
Jason Monk74f5e362017-12-06 08:56:33 -0500217 */
Jason Monke2c64512017-12-11 15:14:54 -0500218 public @NonNull List<SliceSpec> getPinnedSpecs(Uri uri) {
Jason Monk74f5e362017-12-06 08:56:33 -0500219 try {
Jason Monke2c64512017-12-11 15:14:54 -0500220 return Arrays.asList(mService.getPinnedSpecs(uri, mContext.getPackageName()));
Jason Monk74f5e362017-12-06 08:56:33 -0500221 } catch (RemoteException e) {
222 throw e.rethrowFromSystemServer();
223 }
224 }
225
226 /**
Jason Monke2c64512017-12-11 15:14:54 -0500227 * Class that listens to changes in {@link Slice}s.
Jason Monk74f5e362017-12-06 08:56:33 -0500228 */
Jason Monke2c64512017-12-11 15:14:54 -0500229 public interface SliceCallback {
Jason Monk74f5e362017-12-06 08:56:33 -0500230
231 /**
Jason Monke2c64512017-12-11 15:14:54 -0500232 * Called when slice is updated.
233 *
234 * @param s The updated slice.
235 * @see #registerSliceCallback
Jason Monk74f5e362017-12-06 08:56:33 -0500236 */
Jason Monke2c64512017-12-11 15:14:54 -0500237 void onSliceUpdated(Slice s);
Jason Monk74f5e362017-12-06 08:56:33 -0500238 }
Jason Monk8f5f7ff2017-10-17 14:12:42 -0400239}