blob: 5e530eedd818f8bc0b0c4c0b69c7465d1a9578b5 [file] [log] [blame]
Jason Monkd9edfa942017-09-25 12:38:53 -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 */
Jason Monkd18651f2017-10-05 14:18:49 -040016package android.app.slice;
Jason Monkd9edfa942017-09-25 12:38:53 -040017
Jason Monkb14dde072018-05-25 15:13:16 -040018import static android.app.slice.Slice.SUBTYPE_COLOR;
19
Mady Mellor3b0a72f2017-10-19 10:12:09 -070020import android.annotation.NonNull;
Jason Monke8f8be72018-01-21 10:10:35 -050021import android.app.PendingIntent;
22import android.content.ComponentName;
Jason Monkd9edfa942017-09-25 12:38:53 -040023import android.content.ContentProvider;
24import android.content.ContentResolver;
25import android.content.ContentValues;
Jason Monke8f8be72018-01-21 10:10:35 -050026import android.content.Context;
Jason Monkb40dad52017-11-01 16:01:40 -040027import android.content.Intent;
Mady Mellor3b0a72f2017-10-19 10:12:09 -070028import android.content.IntentFilter;
Jason Monke8f8be72018-01-21 10:10:35 -050029import android.content.pm.PackageManager;
30import android.content.pm.PackageManager.NameNotFoundException;
31import android.content.pm.ProviderInfo;
Jason Monkd9edfa942017-09-25 12:38:53 -040032import android.database.ContentObserver;
33import android.database.Cursor;
Jason Monkb14dde072018-05-25 15:13:16 -040034import android.graphics.drawable.Icon;
Jason Monkd9edfa942017-09-25 12:38:53 -040035import android.net.Uri;
Jason Monkb40dad52017-11-01 16:01:40 -040036import android.os.Binder;
Jason Monkd9edfa942017-09-25 12:38:53 -040037import android.os.Bundle;
38import android.os.CancellationSignal;
39import android.os.Handler;
Jason Monkb40dad52017-11-01 16:01:40 -040040import android.os.Process;
Jason Monkd18651f2017-10-05 14:18:49 -040041import android.os.StrictMode;
42import android.os.StrictMode.ThreadPolicy;
Jason Monk199286b2018-04-12 19:47:50 -040043import android.util.ArraySet;
Jason Monkd9edfa942017-09-25 12:38:53 -040044import android.util.Log;
Jason Monkb14dde072018-05-25 15:13:16 -040045import android.util.TypedValue;
46import android.view.ContextThemeWrapper;
Jason Monkd9edfa942017-09-25 12:38:53 -040047
Jason Monk5f8cc272018-01-16 17:57:20 -050048import java.util.ArrayList;
Jason Monk106387f2018-03-06 16:32:28 -050049import java.util.Arrays;
Jason Monk5f8cc272018-01-16 17:57:20 -050050import java.util.Collection;
51import java.util.Collections;
Jason Monk2af19982017-11-07 19:38:27 -050052import java.util.List;
Jason Monk2d3932e2018-03-08 11:31:26 -050053import java.util.Set;
Jason Monkd9edfa942017-09-25 12:38:53 -040054
55/**
Mady Mellor3b0a72f2017-10-19 10:12:09 -070056 * A SliceProvider allows an app to provide content to be displayed in system spaces. This content
57 * is templated and can contain actions, and the behavior of how it is surfaced is specific to the
58 * system surface.
59 * <p>
60 * Slices are not currently live content. They are bound once and shown to the user. If the content
61 * changes due to a callback from user interaction, then
62 * {@link ContentResolver#notifyChange(Uri, ContentObserver)} should be used to notify the system.
63 * </p>
64 * <p>
65 * The provider needs to be declared in the manifest to provide the authority for the app. The
66 * authority for most slices is expected to match the package of the application.
67 * </p>
Jason Monkd9edfa942017-09-25 12:38:53 -040068 *
Jason Monkd9edfa942017-09-25 12:38:53 -040069 * <pre class="prettyprint">
70 * {@literal
71 * <provider
Jason Monkad594512018-03-08 11:35:55 -050072 * android:name="com.example.mypkg.MySliceProvider"
73 * android:authorities="com.example.mypkg" />}
Jason Monkd9edfa942017-09-25 12:38:53 -040074 * </pre>
Mady Mellor3b0a72f2017-10-19 10:12:09 -070075 * <p>
76 * Slices can be identified by a Uri or by an Intent. To link an Intent with a slice, the provider
77 * must have an {@link IntentFilter} matching the slice intent. When a slice is being requested via
78 * an intent, {@link #onMapIntentToUri(Intent)} can be called and is expected to return an
79 * appropriate Uri representing the slice.
80 *
81 * <pre class="prettyprint">
82 * {@literal
83 * <provider
Jason Monkad594512018-03-08 11:35:55 -050084 * android:name="com.example.mypkg.MySliceProvider"
85 * android:authorities="com.example.mypkg">
Mady Mellor3b0a72f2017-10-19 10:12:09 -070086 * <intent-filter>
Jason Monkad594512018-03-08 11:35:55 -050087 * <action android:name="com.example.mypkg.intent.action.MY_SLICE_INTENT" />
Jason Monk5e676a22018-03-08 14:18:55 -050088 * <category android:name="android.app.slice.category.SLICE" />
Mady Mellor3b0a72f2017-10-19 10:12:09 -070089 * </intent-filter>
90 * </provider>}
91 * </pre>
Jason Monkd9edfa942017-09-25 12:38:53 -040092 *
93 * @see Slice
Jason Monkd9edfa942017-09-25 12:38:53 -040094 */
95public abstract class SliceProvider extends ContentProvider {
Jason Monkd18651f2017-10-05 14:18:49 -040096 /**
Jason Monka9b3d732018-01-22 15:37:00 -050097 * This is the Android platform's MIME type for a URI
Jason Monkd18651f2017-10-05 14:18:49 -040098 * containing a slice implemented through {@link SliceProvider}.
99 */
100 public static final String SLICE_TYPE = "vnd.android.slice";
101
Jason Monkd9edfa942017-09-25 12:38:53 -0400102 private static final String TAG = "SliceProvider";
103 /**
104 * @hide
105 */
106 public static final String EXTRA_BIND_URI = "slice_uri";
107 /**
108 * @hide
109 */
Jason Monk2af19982017-11-07 19:38:27 -0500110 public static final String EXTRA_SUPPORTED_SPECS = "supported_specs";
111 /**
112 * @hide
113 */
Jason Monkd9edfa942017-09-25 12:38:53 -0400114 public static final String METHOD_SLICE = "bind_slice";
115 /**
116 * @hide
117 */
Mady Mellor3b0a72f2017-10-19 10:12:09 -0700118 public static final String METHOD_MAP_INTENT = "map_slice";
119 /**
120 * @hide
121 */
Jason Monk7b8fef22018-01-30 16:04:14 -0500122 public static final String METHOD_MAP_ONLY_INTENT = "map_only";
123 /**
124 * @hide
125 */
Jason Monk74f5e362017-12-06 08:56:33 -0500126 public static final String METHOD_PIN = "pin";
127 /**
128 * @hide
129 */
130 public static final String METHOD_UNPIN = "unpin";
131 /**
132 * @hide
133 */
Jason Monk5f8cc272018-01-16 17:57:20 -0500134 public static final String METHOD_GET_DESCENDANTS = "get_descendants";
135 /**
136 * @hide
137 */
Jason Monk7f01f3b2018-05-15 15:46:26 -0400138 public static final String METHOD_GET_PERMISSIONS = "get_permissions";
139 /**
140 * @hide
141 */
Mady Mellor3b0a72f2017-10-19 10:12:09 -0700142 public static final String EXTRA_INTENT = "slice_intent";
143 /**
144 * @hide
145 */
Jason Monkd9edfa942017-09-25 12:38:53 -0400146 public static final String EXTRA_SLICE = "slice";
Jason Monk5f8cc272018-01-16 17:57:20 -0500147 /**
148 * @hide
149 */
150 public static final String EXTRA_SLICE_DESCENDANTS = "slice_descendants";
Jason Monke8f8be72018-01-21 10:10:35 -0500151 /**
152 * @hide
153 */
154 public static final String EXTRA_PKG = "pkg";
155 /**
156 * @hide
157 */
158 public static final String EXTRA_PROVIDER_PKG = "provider_pkg";
Jason Monk7f01f3b2018-05-15 15:46:26 -0400159 /**
160 * @hide
161 */
162 public static final String EXTRA_RESULT = "result";
Jason Monkd9edfa942017-09-25 12:38:53 -0400163
164 private static final boolean DEBUG = false;
165
Jason Monk66cffd52018-03-12 16:42:48 -0400166 private static final long SLICE_BIND_ANR = 2000;
Jason Monk42e03f82018-03-30 11:26:56 -0400167 private final String[] mAutoGrantPermissions;
Jason Monke8f8be72018-01-21 10:10:35 -0500168
Jason Monk66cffd52018-03-12 16:42:48 -0400169 private String mCallback;
170 private SliceManager mSliceManager;
Jason Monke8f8be72018-01-21 10:10:35 -0500171
Jason Monk42e03f82018-03-30 11:26:56 -0400172 /**
173 * A version of constructing a SliceProvider that allows autogranting slice permissions
174 * to apps that hold specific platform permissions.
175 * <p>
176 * When an app tries to bind a slice from this provider that it does not have access to,
177 * This provider will check if the caller holds permissions to any of the autoGrantPermissions
178 * specified, if they do they will be granted persisted uri access to all slices of this
179 * provider.
180 *
181 * @param autoGrantPermissions List of permissions that holders are auto-granted access
182 * to slices.
183 */
184 public SliceProvider(@NonNull String... autoGrantPermissions) {
185 mAutoGrantPermissions = autoGrantPermissions;
186 }
187
188 public SliceProvider() {
189 mAutoGrantPermissions = new String[0];
190 }
191
Jason Monke8f8be72018-01-21 10:10:35 -0500192 @Override
193 public void attachInfo(Context context, ProviderInfo info) {
194 super.attachInfo(context, info);
195 mSliceManager = context.getSystemService(SliceManager.class);
196 }
197
Jason Monkd9edfa942017-09-25 12:38:53 -0400198 /**
Jason Monk66cffd52018-03-12 16:42:48 -0400199 * Implemented to create a slice.
Jason Monkd18651f2017-10-05 14:18:49 -0400200 * <p>
201 * onBindSlice should return as quickly as possible so that the UI tied
202 * to this slice can be responsive. No network or other IO will be allowed
203 * during onBindSlice. Any loading that needs to be done should happen
Jason Monk66cffd52018-03-12 16:42:48 -0400204 * in the background with a call to {@link ContentResolver#notifyChange(Uri, ContentObserver)}
Jason Monkd18651f2017-10-05 14:18:49 -0400205 * when the app is ready to provide the complete data in onBindSlice.
206 * <p>
Jason Monk2af19982017-11-07 19:38:27 -0500207 * The slice returned should have a spec that is compatible with one of
208 * the supported specs.
Jason Monkd18651f2017-10-05 14:18:49 -0400209 *
Jason Monk2af19982017-11-07 19:38:27 -0500210 * @param sliceUri Uri to bind.
211 * @param supportedSpecs List of supported specs.
Andrew Solovay27e43462018-12-12 15:38:06 -0800212 * @see Slice
Aurimas Liutikas7f695332018-05-31 21:07:32 -0700213 * @see Slice#HINT_PARTIAL
Jason Monkd9edfa942017-09-25 12:38:53 -0400214 */
Jason Monk2d3932e2018-03-08 11:31:26 -0500215 public Slice onBindSlice(Uri sliceUri, Set<SliceSpec> supportedSpecs) {
216 return onBindSlice(sliceUri, new ArrayList<>(supportedSpecs));
217 }
218
219 /**
220 * @deprecated TO BE REMOVED
Jeff Sharkey3990ee12018-04-11 10:19:55 -0600221 * @removed
Jason Monk2d3932e2018-03-08 11:31:26 -0500222 */
223 @Deprecated
Jason Monk2af19982017-11-07 19:38:27 -0500224 public Slice onBindSlice(Uri sliceUri, List<SliceSpec> supportedSpecs) {
Jason Monk2af19982017-11-07 19:38:27 -0500225 return null;
226 }
Jason Monkd9edfa942017-09-25 12:38:53 -0400227
Mady Mellor3b0a72f2017-10-19 10:12:09 -0700228 /**
Jason Monke2c64512017-12-11 15:14:54 -0500229 * Called to inform an app that a slice has been pinned.
230 * <p>
231 * Pinning is a way that slice hosts use to notify apps of which slices
232 * they care about updates for. When a slice is pinned the content is
233 * expected to be relatively fresh and kept up to date.
234 * <p>
235 * Being pinned does not provide any escalated privileges for the slice
236 * provider. So apps should do things such as turn on syncing or schedule
237 * a job in response to a onSlicePinned.
238 * <p>
239 * Pinned state is not persisted through a reboot, and apps can expect a
240 * new call to onSlicePinned for any slices that should remain pinned
241 * after a reboot occurs.
242 *
243 * @param sliceUri The uri of the slice being unpinned.
244 * @see #onSliceUnpinned(Uri)
Jason Monk74f5e362017-12-06 08:56:33 -0500245 */
246 public void onSlicePinned(Uri sliceUri) {
247 }
248
249 /**
Jason Monke2c64512017-12-11 15:14:54 -0500250 * Called to inform an app that a slices is no longer pinned.
251 * <p>
252 * This means that no other apps on the device care about updates to this
253 * slice anymore and therefore it is not important to be updated. Any syncs
254 * or jobs related to this slice should be cancelled.
255 * @see #onSlicePinned(Uri)
Jason Monk74f5e362017-12-06 08:56:33 -0500256 */
257 public void onSliceUnpinned(Uri sliceUri) {
258 }
259
260 /**
Jason Monk5f8cc272018-01-16 17:57:20 -0500261 * Obtains a list of slices that are descendants of the specified Uri.
262 * <p>
263 * Implementing this is optional for a SliceProvider, but does provide a good
264 * discovery mechanism for finding slice Uris.
265 *
266 * @param uri The uri to look for descendants under.
267 * @return All slices within the space.
268 * @see SliceManager#getSliceDescendants(Uri)
269 */
270 public @NonNull Collection<Uri> onGetSliceDescendants(@NonNull Uri uri) {
271 return Collections.emptyList();
272 }
273
274 /**
Mady Mellor3b0a72f2017-10-19 10:12:09 -0700275 * This method must be overridden if an {@link IntentFilter} is specified on the SliceProvider.
276 * In that case, this method can be called and is expected to return a non-null Uri representing
277 * a slice. Otherwise this will throw {@link UnsupportedOperationException}.
278 *
Jason Monk5e676a22018-03-08 14:18:55 -0500279 * Any intent filter added to a slice provider should also contain
280 * {@link SliceManager#CATEGORY_SLICE}, because otherwise it will not be detected by
281 * {@link SliceManager#mapIntentToUri(Intent)}.
282 *
Mady Mellor3b0a72f2017-10-19 10:12:09 -0700283 * @return Uri representing the slice associated with the provided intent.
Jason Monk5e676a22018-03-08 14:18:55 -0500284 * @see Slice
285 * @see SliceManager#mapIntentToUri(Intent)
Mady Mellor3b0a72f2017-10-19 10:12:09 -0700286 */
287 public @NonNull Uri onMapIntentToUri(Intent intent) {
288 throw new UnsupportedOperationException(
289 "This provider has not implemented intent to uri mapping");
290 }
291
Jason Monkac112382018-03-23 15:06:35 -0400292 /**
293 * Called when an app requests a slice it does not have write permission
294 * to the uri for.
295 * <p>
296 * The return value will be the action on a slice that prompts the user that
297 * the calling app wants to show slices from this app. The default implementation
298 * launches a dialog that allows the user to grant access to this slice. Apps
299 * that do not want to allow this user grant, can override this and instead
300 * launch their own dialog with different behavior.
301 *
302 * @param sliceUri the Uri of the slice attempting to be bound.
303 * @see #getCallingPackage()
304 */
305 public @NonNull PendingIntent onCreatePermissionRequest(Uri sliceUri) {
306 return createPermissionIntent(getContext(), sliceUri, getCallingPackage());
307 }
308
Jason Monkd9edfa942017-09-25 12:38:53 -0400309 @Override
310 public final int update(Uri uri, ContentValues values, String selection,
311 String[] selectionArgs) {
312 if (DEBUG) Log.d(TAG, "update " + uri);
313 return 0;
314 }
315
316 @Override
317 public final int delete(Uri uri, String selection, String[] selectionArgs) {
318 if (DEBUG) Log.d(TAG, "delete " + uri);
319 return 0;
320 }
321
322 @Override
323 public final Cursor query(Uri uri, String[] projection, String selection,
324 String[] selectionArgs, String sortOrder) {
325 if (DEBUG) Log.d(TAG, "query " + uri);
326 return null;
327 }
328
329 @Override
330 public final Cursor query(Uri uri, String[] projection, String selection, String[]
331 selectionArgs, String sortOrder, CancellationSignal cancellationSignal) {
332 if (DEBUG) Log.d(TAG, "query " + uri);
333 return null;
334 }
335
336 @Override
337 public final Cursor query(Uri uri, String[] projection, Bundle queryArgs,
338 CancellationSignal cancellationSignal) {
339 if (DEBUG) Log.d(TAG, "query " + uri);
340 return null;
341 }
342
343 @Override
344 public final Uri insert(Uri uri, ContentValues values) {
345 if (DEBUG) Log.d(TAG, "insert " + uri);
346 return null;
347 }
348
349 @Override
350 public final String getType(Uri uri) {
351 if (DEBUG) Log.d(TAG, "getType " + uri);
Jason Monkd18651f2017-10-05 14:18:49 -0400352 return SLICE_TYPE;
Jason Monkd9edfa942017-09-25 12:38:53 -0400353 }
354
355 @Override
Jason Monkd18651f2017-10-05 14:18:49 -0400356 public Bundle call(String method, String arg, Bundle extras) {
Jason Monkd9edfa942017-09-25 12:38:53 -0400357 if (method.equals(METHOD_SLICE)) {
Pinyao Ting2b415a42019-08-08 15:35:20 -0700358 Uri uri = getUriWithoutUserId(validateIncomingUriOrNull(
359 extras.getParcelable(EXTRA_BIND_URI)));
Jason Monk2af19982017-11-07 19:38:27 -0500360 List<SliceSpec> supportedSpecs = extras.getParcelableArrayList(EXTRA_SUPPORTED_SPECS);
Jason Monkd9edfa942017-09-25 12:38:53 -0400361
Jason Monke8f8be72018-01-21 10:10:35 -0500362 String callingPackage = getCallingPackage();
Jason Monk3a1d2e972018-01-29 16:58:11 -0500363 int callingUid = Binder.getCallingUid();
364 int callingPid = Binder.getCallingPid();
Jason Monkac112382018-03-23 15:06:35 -0400365
Jason Monk3a1d2e972018-01-29 16:58:11 -0500366 Slice s = handleBindSlice(uri, supportedSpecs, callingPackage, callingUid, callingPid);
Jason Monkd9edfa942017-09-25 12:38:53 -0400367 Bundle b = new Bundle();
368 b.putParcelable(EXTRA_SLICE, s);
369 return b;
Mady Mellor3b0a72f2017-10-19 10:12:09 -0700370 } else if (method.equals(METHOD_MAP_INTENT)) {
Mady Mellor3b0a72f2017-10-19 10:12:09 -0700371 Intent intent = extras.getParcelable(EXTRA_INTENT);
Jason Monk74f5e362017-12-06 08:56:33 -0500372 if (intent == null) return null;
Pinyao Ting2b415a42019-08-08 15:35:20 -0700373 Uri uri = validateIncomingUriOrNull(onMapIntentToUri(intent));
Jason Monk2af19982017-11-07 19:38:27 -0500374 List<SliceSpec> supportedSpecs = extras.getParcelableArrayList(EXTRA_SUPPORTED_SPECS);
Mady Mellor3b0a72f2017-10-19 10:12:09 -0700375 Bundle b = new Bundle();
376 if (uri != null) {
Jason Monk3a1d2e972018-01-29 16:58:11 -0500377 Slice s = handleBindSlice(uri, supportedSpecs, getCallingPackage(),
378 Binder.getCallingUid(), Binder.getCallingPid());
Mady Mellor3b0a72f2017-10-19 10:12:09 -0700379 b.putParcelable(EXTRA_SLICE, s);
380 } else {
381 b.putParcelable(EXTRA_SLICE, null);
382 }
383 return b;
Jason Monk7b8fef22018-01-30 16:04:14 -0500384 } else if (method.equals(METHOD_MAP_ONLY_INTENT)) {
385 Intent intent = extras.getParcelable(EXTRA_INTENT);
386 if (intent == null) return null;
Pinyao Ting2b415a42019-08-08 15:35:20 -0700387 Uri uri = validateIncomingUriOrNull(onMapIntentToUri(intent));
Jason Monk7b8fef22018-01-30 16:04:14 -0500388 Bundle b = new Bundle();
389 b.putParcelable(EXTRA_SLICE, uri);
390 return b;
Jason Monk74f5e362017-12-06 08:56:33 -0500391 } else if (method.equals(METHOD_PIN)) {
Pinyao Ting2b415a42019-08-08 15:35:20 -0700392 Uri uri = getUriWithoutUserId(validateIncomingUriOrNull(
393 extras.getParcelable(EXTRA_BIND_URI)));
Jason Monke8f8be72018-01-21 10:10:35 -0500394 if (Binder.getCallingUid() != Process.SYSTEM_UID) {
395 throw new SecurityException("Only the system can pin/unpin slices");
Jason Monk74f5e362017-12-06 08:56:33 -0500396 }
397 handlePinSlice(uri);
398 } else if (method.equals(METHOD_UNPIN)) {
Pinyao Ting2b415a42019-08-08 15:35:20 -0700399 Uri uri = getUriWithoutUserId(validateIncomingUriOrNull(
400 extras.getParcelable(EXTRA_BIND_URI)));
Jason Monke8f8be72018-01-21 10:10:35 -0500401 if (Binder.getCallingUid() != Process.SYSTEM_UID) {
402 throw new SecurityException("Only the system can pin/unpin slices");
Jason Monk74f5e362017-12-06 08:56:33 -0500403 }
404 handleUnpinSlice(uri);
Jason Monk5f8cc272018-01-16 17:57:20 -0500405 } else if (method.equals(METHOD_GET_DESCENDANTS)) {
Pinyao Ting2b415a42019-08-08 15:35:20 -0700406 Uri uri = getUriWithoutUserId(
407 validateIncomingUriOrNull(extras.getParcelable(EXTRA_BIND_URI)));
Jason Monk5f8cc272018-01-16 17:57:20 -0500408 Bundle b = new Bundle();
409 b.putParcelableArrayList(EXTRA_SLICE_DESCENDANTS,
410 new ArrayList<>(handleGetDescendants(uri)));
411 return b;
Jason Monk7f01f3b2018-05-15 15:46:26 -0400412 } else if (method.equals(METHOD_GET_PERMISSIONS)) {
413 if (Binder.getCallingUid() != Process.SYSTEM_UID) {
414 throw new SecurityException("Only the system can get permissions");
415 }
416 Bundle b = new Bundle();
417 b.putStringArray(EXTRA_RESULT, mAutoGrantPermissions);
418 return b;
Jason Monkd9edfa942017-09-25 12:38:53 -0400419 }
420 return super.call(method, arg, extras);
421 }
422
Pinyao Ting2b415a42019-08-08 15:35:20 -0700423 private Uri validateIncomingUriOrNull(Uri uri) {
424 return uri == null ? null : validateIncomingUri(uri);
425 }
426
Jason Monk5f8cc272018-01-16 17:57:20 -0500427 private Collection<Uri> handleGetDescendants(Uri uri) {
Jason Monk66cffd52018-03-12 16:42:48 -0400428 mCallback = "onGetSliceDescendants";
Jason Monka66dfee2018-05-11 08:23:59 -0700429 return onGetSliceDescendants(uri);
Jason Monk5f8cc272018-01-16 17:57:20 -0500430 }
431
Jason Monk74f5e362017-12-06 08:56:33 -0500432 private void handlePinSlice(Uri sliceUri) {
Jason Monk66cffd52018-03-12 16:42:48 -0400433 mCallback = "onSlicePinned";
434 Handler.getMain().postDelayed(mAnr, SLICE_BIND_ANR);
435 try {
Jason Monk74f5e362017-12-06 08:56:33 -0500436 onSlicePinned(sliceUri);
Jason Monk66cffd52018-03-12 16:42:48 -0400437 } finally {
438 Handler.getMain().removeCallbacks(mAnr);
Jason Monk74f5e362017-12-06 08:56:33 -0500439 }
440 }
441
442 private void handleUnpinSlice(Uri sliceUri) {
Jason Monk66cffd52018-03-12 16:42:48 -0400443 mCallback = "onSliceUnpinned";
444 Handler.getMain().postDelayed(mAnr, SLICE_BIND_ANR);
445 try {
Jason Monk74f5e362017-12-06 08:56:33 -0500446 onSliceUnpinned(sliceUri);
Jason Monk66cffd52018-03-12 16:42:48 -0400447 } finally {
448 Handler.getMain().removeCallbacks(mAnr);
Jason Monk74f5e362017-12-06 08:56:33 -0500449 }
450 }
451
Jason Monke8f8be72018-01-21 10:10:35 -0500452 private Slice handleBindSlice(Uri sliceUri, List<SliceSpec> supportedSpecs,
Jason Monk3a1d2e972018-01-29 16:58:11 -0500453 String callingPkg, int callingUid, int callingPid) {
Jason Monke8f8be72018-01-21 10:10:35 -0500454 // This can be removed once Slice#bindSlice is removed and everyone is using
455 // SliceManager#bindSlice.
456 String pkg = callingPkg != null ? callingPkg
Jason Monk3a1d2e972018-01-29 16:58:11 -0500457 : getContext().getPackageManager().getNameForUid(callingUid);
Jason Monkac112382018-03-23 15:06:35 -0400458 try {
459 mSliceManager.enforceSlicePermission(sliceUri, pkg,
Jason Monk42e03f82018-03-30 11:26:56 -0400460 callingPid, callingUid, mAutoGrantPermissions);
Jason Monkac112382018-03-23 15:06:35 -0400461 } catch (SecurityException e) {
462 return createPermissionSlice(getContext(), sliceUri, pkg);
Jason Monke8f8be72018-01-21 10:10:35 -0500463 }
Jason Monk66cffd52018-03-12 16:42:48 -0400464 mCallback = "onBindSlice";
465 Handler.getMain().postDelayed(mAnr, SLICE_BIND_ANR);
466 try {
467 return onBindSliceStrict(sliceUri, supportedSpecs);
468 } finally {
469 Handler.getMain().removeCallbacks(mAnr);
Lucas Dupine0653fa2017-10-23 14:31:45 -0700470 }
471 }
472
Jason Monke8f8be72018-01-21 10:10:35 -0500473 /**
474 * @hide
475 */
Jason Monkac112382018-03-23 15:06:35 -0400476 public Slice createPermissionSlice(Context context, Uri sliceUri,
Jason Monke8f8be72018-01-21 10:10:35 -0500477 String callingPackage) {
Jason Monkac112382018-03-23 15:06:35 -0400478 PendingIntent action;
479 mCallback = "onCreatePermissionRequest";
480 Handler.getMain().postDelayed(mAnr, SLICE_BIND_ANR);
481 try {
482 action = onCreatePermissionRequest(sliceUri);
483 } finally {
484 Handler.getMain().removeCallbacks(mAnr);
485 }
Mady Mellor33c5a842018-03-19 16:23:15 -0700486 Slice.Builder parent = new Slice.Builder(sliceUri);
487 Slice.Builder childAction = new Slice.Builder(parent)
Jason Monkb14dde072018-05-25 15:13:16 -0400488 .addIcon(Icon.createWithResource(context,
489 com.android.internal.R.drawable.ic_permission), null,
490 Collections.emptyList())
Mady Mellor33c5a842018-03-19 16:23:15 -0700491 .addHints(Arrays.asList(Slice.HINT_TITLE, Slice.HINT_SHORTCUT))
492 .addAction(action, new Slice.Builder(parent).build(), null);
493
Jason Monkb14dde072018-05-25 15:13:16 -0400494 TypedValue tv = new TypedValue();
495 new ContextThemeWrapper(context, android.R.style.Theme_DeviceDefault_Light)
496 .getTheme().resolveAttribute(android.R.attr.colorAccent, tv, true);
497 int deviceDefaultAccent = tv.data;
498
Mady Mellor33c5a842018-03-19 16:23:15 -0700499 parent.addSubSlice(new Slice.Builder(sliceUri.buildUpon().appendPath("permission").build())
Jason Monkb14dde072018-05-25 15:13:16 -0400500 .addIcon(Icon.createWithResource(context,
501 com.android.internal.R.drawable.ic_arrow_forward), null,
502 Collections.emptyList())
Mady Mellor33c5a842018-03-19 16:23:15 -0700503 .addText(getPermissionString(context, callingPackage), null,
504 Collections.emptyList())
Jason Monkb14dde072018-05-25 15:13:16 -0400505 .addInt(deviceDefaultAccent, SUBTYPE_COLOR,
506 Collections.emptyList())
Mady Mellor33c5a842018-03-19 16:23:15 -0700507 .addSubSlice(childAction.build(), null)
508 .build(), null);
509 return parent.addHints(Arrays.asList(Slice.HINT_PERMISSION_REQUEST)).build();
Jason Monke8f8be72018-01-21 10:10:35 -0500510 }
511
512 /**
513 * @hide
514 */
515 public static PendingIntent createPermissionIntent(Context context, Uri sliceUri,
516 String callingPackage) {
517 Intent intent = new Intent(SliceManager.ACTION_REQUEST_SLICE_PERMISSION);
518 intent.setComponent(new ComponentName("com.android.systemui",
519 "com.android.systemui.SlicePermissionActivity"));
520 intent.putExtra(EXTRA_BIND_URI, sliceUri);
521 intent.putExtra(EXTRA_PKG, callingPackage);
522 intent.putExtra(EXTRA_PROVIDER_PKG, context.getPackageName());
523 // Unique pending intent.
524 intent.setData(sliceUri.buildUpon().appendQueryParameter("package", callingPackage)
525 .build());
526
527 return PendingIntent.getActivity(context, 0, intent, 0);
528 }
529
530 /**
531 * @hide
532 */
533 public static CharSequence getPermissionString(Context context, String callingPackage) {
534 PackageManager pm = context.getPackageManager();
535 try {
536 return context.getString(
537 com.android.internal.R.string.slices_permission_request,
538 pm.getApplicationInfo(callingPackage, 0).loadLabel(pm),
539 context.getApplicationInfo().loadLabel(pm));
540 } catch (NameNotFoundException e) {
541 // This shouldn't be possible since the caller is verified.
542 throw new RuntimeException("Unknown calling app", e);
543 }
544 }
545
Jason Monk66cffd52018-03-12 16:42:48 -0400546 private Slice onBindSliceStrict(Uri sliceUri, List<SliceSpec> supportedSpecs) {
Lucas Dupine0653fa2017-10-23 14:31:45 -0700547 ThreadPolicy oldPolicy = StrictMode.getThreadPolicy();
Jason Monkd9edfa942017-09-25 12:38:53 -0400548 try {
Lucas Dupine0653fa2017-10-23 14:31:45 -0700549 StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
550 .detectAll()
551 .penaltyDeath()
552 .build());
Jason Monk199286b2018-04-12 19:47:50 -0400553 return onBindSlice(sliceUri, new ArraySet<>(supportedSpecs));
Lucas Dupine0653fa2017-10-23 14:31:45 -0700554 } finally {
555 StrictMode.setThreadPolicy(oldPolicy);
Jason Monkd9edfa942017-09-25 12:38:53 -0400556 }
557 }
Jason Monk66cffd52018-03-12 16:42:48 -0400558
559 private final Runnable mAnr = () -> {
560 Process.sendSignal(Process.myPid(), Process.SIGNAL_QUIT);
561 Log.wtf(TAG, "Timed out while handling slice callback " + mCallback);
562 };
Jason Monkd9edfa942017-09-25 12:38:53 -0400563}