blob: ac5365c35f49742e79e6d7eef5f720e6e0c22231 [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
18import android.Manifest.permission;
Mady Mellor3b0a72f2017-10-19 10:12:09 -070019import android.annotation.NonNull;
Jason Monkd9edfa942017-09-25 12:38:53 -040020import android.content.ContentProvider;
21import android.content.ContentResolver;
22import android.content.ContentValues;
Jason Monkb40dad52017-11-01 16:01:40 -040023import android.content.Intent;
Mady Mellor3b0a72f2017-10-19 10:12:09 -070024import android.content.IntentFilter;
Jason Monkd9edfa942017-09-25 12:38:53 -040025import android.database.ContentObserver;
26import android.database.Cursor;
27import android.net.Uri;
Jason Monkb40dad52017-11-01 16:01:40 -040028import android.os.Binder;
Jason Monkd9edfa942017-09-25 12:38:53 -040029import android.os.Bundle;
30import android.os.CancellationSignal;
31import android.os.Handler;
32import android.os.Looper;
Jason Monkb40dad52017-11-01 16:01:40 -040033import android.os.Process;
Jason Monkd18651f2017-10-05 14:18:49 -040034import android.os.StrictMode;
35import android.os.StrictMode.ThreadPolicy;
Jason Monkb40dad52017-11-01 16:01:40 -040036import android.os.UserHandle;
Jason Monkd9edfa942017-09-25 12:38:53 -040037import android.util.Log;
38
Jason Monk2af19982017-11-07 19:38:27 -050039import java.util.List;
Jason Monkd9edfa942017-09-25 12:38:53 -040040import java.util.concurrent.CountDownLatch;
41
42/**
Mady Mellor3b0a72f2017-10-19 10:12:09 -070043 * A SliceProvider allows an app to provide content to be displayed in system spaces. This content
44 * is templated and can contain actions, and the behavior of how it is surfaced is specific to the
45 * system surface.
46 * <p>
47 * Slices are not currently live content. They are bound once and shown to the user. If the content
48 * changes due to a callback from user interaction, then
49 * {@link ContentResolver#notifyChange(Uri, ContentObserver)} should be used to notify the system.
50 * </p>
51 * <p>
52 * The provider needs to be declared in the manifest to provide the authority for the app. The
53 * authority for most slices is expected to match the package of the application.
54 * </p>
Jason Monkd9edfa942017-09-25 12:38:53 -040055 *
Jason Monkd9edfa942017-09-25 12:38:53 -040056 * <pre class="prettyprint">
57 * {@literal
58 * <provider
59 * android:name="com.android.mypkg.MySliceProvider"
60 * android:authorities="com.android.mypkg" />}
61 * </pre>
Mady Mellor3b0a72f2017-10-19 10:12:09 -070062 * <p>
63 * Slices can be identified by a Uri or by an Intent. To link an Intent with a slice, the provider
64 * must have an {@link IntentFilter} matching the slice intent. When a slice is being requested via
65 * an intent, {@link #onMapIntentToUri(Intent)} can be called and is expected to return an
66 * appropriate Uri representing the slice.
67 *
68 * <pre class="prettyprint">
69 * {@literal
70 * <provider
71 * android:name="com.android.mypkg.MySliceProvider"
72 * android:authorities="com.android.mypkg">
73 * <intent-filter>
74 * <action android:name="android.intent.action.MY_SLICE_INTENT" />
75 * </intent-filter>
76 * </provider>}
77 * </pre>
Jason Monkd9edfa942017-09-25 12:38:53 -040078 *
79 * @see Slice
Jason Monkd9edfa942017-09-25 12:38:53 -040080 */
81public abstract class SliceProvider extends ContentProvider {
Jason Monkd18651f2017-10-05 14:18:49 -040082 /**
83 * This is the Android platform's MIME type for a slice: URI
84 * containing a slice implemented through {@link SliceProvider}.
85 */
86 public static final String SLICE_TYPE = "vnd.android.slice";
87
Jason Monkd9edfa942017-09-25 12:38:53 -040088 private static final String TAG = "SliceProvider";
89 /**
90 * @hide
91 */
92 public static final String EXTRA_BIND_URI = "slice_uri";
93 /**
94 * @hide
95 */
Jason Monk2af19982017-11-07 19:38:27 -050096 public static final String EXTRA_SUPPORTED_SPECS = "supported_specs";
97 /**
98 * @hide
99 */
Jason Monkd9edfa942017-09-25 12:38:53 -0400100 public static final String METHOD_SLICE = "bind_slice";
101 /**
102 * @hide
103 */
Mady Mellor3b0a72f2017-10-19 10:12:09 -0700104 public static final String METHOD_MAP_INTENT = "map_slice";
105 /**
106 * @hide
107 */
108 public static final String EXTRA_INTENT = "slice_intent";
109 /**
110 * @hide
111 */
Jason Monkd9edfa942017-09-25 12:38:53 -0400112 public static final String EXTRA_SLICE = "slice";
113
114 private static final boolean DEBUG = false;
115
116 /**
117 * Implemented to create a slice. Will be called on the main thread.
Jason Monkd18651f2017-10-05 14:18:49 -0400118 * <p>
119 * onBindSlice should return as quickly as possible so that the UI tied
120 * to this slice can be responsive. No network or other IO will be allowed
121 * during onBindSlice. Any loading that needs to be done should happen
122 * off the main thread with a call to {@link ContentResolver#notifyChange(Uri, ContentObserver)}
123 * when the app is ready to provide the complete data in onBindSlice.
124 * <p>
Jason Monk2af19982017-11-07 19:38:27 -0500125 * The slice returned should have a spec that is compatible with one of
126 * the supported specs.
Jason Monkd18651f2017-10-05 14:18:49 -0400127 *
Jason Monk2af19982017-11-07 19:38:27 -0500128 * @param sliceUri Uri to bind.
129 * @param supportedSpecs List of supported specs.
Jason Monkd9edfa942017-09-25 12:38:53 -0400130 * @see {@link Slice}.
Jason Monkd18651f2017-10-05 14:18:49 -0400131 * @see {@link Slice#HINT_PARTIAL}
Jason Monkd9edfa942017-09-25 12:38:53 -0400132 */
Jason Monk2af19982017-11-07 19:38:27 -0500133 public Slice onBindSlice(Uri sliceUri, List<SliceSpec> supportedSpecs) {
134 return onBindSlice(sliceUri);
135 }
136
137 /**
138 * @deprecated migrating to {@link #onBindSlice(Uri, List)}
139 */
140 @Deprecated
141 public Slice onBindSlice(Uri sliceUri) {
142 return null;
143 }
Jason Monkd9edfa942017-09-25 12:38:53 -0400144
Mady Mellor3b0a72f2017-10-19 10:12:09 -0700145 /**
146 * This method must be overridden if an {@link IntentFilter} is specified on the SliceProvider.
147 * In that case, this method can be called and is expected to return a non-null Uri representing
148 * a slice. Otherwise this will throw {@link UnsupportedOperationException}.
149 *
150 * @return Uri representing the slice associated with the provided intent.
151 * @see {@link Slice}
Mady Mellor3b0a72f2017-10-19 10:12:09 -0700152 */
153 public @NonNull Uri onMapIntentToUri(Intent intent) {
154 throw new UnsupportedOperationException(
155 "This provider has not implemented intent to uri mapping");
156 }
157
Jason Monkd9edfa942017-09-25 12:38:53 -0400158 @Override
159 public final int update(Uri uri, ContentValues values, String selection,
160 String[] selectionArgs) {
161 if (DEBUG) Log.d(TAG, "update " + uri);
162 return 0;
163 }
164
165 @Override
166 public final int delete(Uri uri, String selection, String[] selectionArgs) {
167 if (DEBUG) Log.d(TAG, "delete " + uri);
168 return 0;
169 }
170
171 @Override
172 public final Cursor query(Uri uri, String[] projection, String selection,
173 String[] selectionArgs, String sortOrder) {
174 if (DEBUG) Log.d(TAG, "query " + uri);
175 return null;
176 }
177
178 @Override
179 public final Cursor query(Uri uri, String[] projection, String selection, String[]
180 selectionArgs, String sortOrder, CancellationSignal cancellationSignal) {
181 if (DEBUG) Log.d(TAG, "query " + uri);
182 return null;
183 }
184
185 @Override
186 public final Cursor query(Uri uri, String[] projection, Bundle queryArgs,
187 CancellationSignal cancellationSignal) {
188 if (DEBUG) Log.d(TAG, "query " + uri);
189 return null;
190 }
191
192 @Override
193 public final Uri insert(Uri uri, ContentValues values) {
194 if (DEBUG) Log.d(TAG, "insert " + uri);
195 return null;
196 }
197
198 @Override
199 public final String getType(Uri uri) {
200 if (DEBUG) Log.d(TAG, "getType " + uri);
Jason Monkd18651f2017-10-05 14:18:49 -0400201 return SLICE_TYPE;
Jason Monkd9edfa942017-09-25 12:38:53 -0400202 }
203
204 @Override
Jason Monkd18651f2017-10-05 14:18:49 -0400205 public Bundle call(String method, String arg, Bundle extras) {
Jason Monkd9edfa942017-09-25 12:38:53 -0400206 if (method.equals(METHOD_SLICE)) {
Jason Monkd9edfa942017-09-25 12:38:53 -0400207 Uri uri = extras.getParcelable(EXTRA_BIND_URI);
Jason Monkb40dad52017-11-01 16:01:40 -0400208 if (!UserHandle.isSameApp(Binder.getCallingUid(), Process.myUid())) {
209 getContext().enforceUriPermission(uri, permission.BIND_SLICE,
210 permission.BIND_SLICE, Binder.getCallingPid(), Binder.getCallingUid(),
211 Intent.FLAG_GRANT_WRITE_URI_PERMISSION,
212 "Slice binding requires the permission BIND_SLICE");
213 }
Jason Monk2af19982017-11-07 19:38:27 -0500214 List<SliceSpec> supportedSpecs = extras.getParcelableArrayList(EXTRA_SUPPORTED_SPECS);
Jason Monkd9edfa942017-09-25 12:38:53 -0400215
Jason Monk2af19982017-11-07 19:38:27 -0500216 Slice s = handleBindSlice(uri, supportedSpecs);
Jason Monkd9edfa942017-09-25 12:38:53 -0400217 Bundle b = new Bundle();
218 b.putParcelable(EXTRA_SLICE, s);
219 return b;
Mady Mellor3b0a72f2017-10-19 10:12:09 -0700220 } else if (method.equals(METHOD_MAP_INTENT)) {
221 getContext().enforceCallingPermission(permission.BIND_SLICE,
222 "Slice binding requires the permission BIND_SLICE");
223 Intent intent = extras.getParcelable(EXTRA_INTENT);
224 Uri uri = onMapIntentToUri(intent);
Jason Monk2af19982017-11-07 19:38:27 -0500225 List<SliceSpec> supportedSpecs = extras.getParcelableArrayList(EXTRA_SUPPORTED_SPECS);
Mady Mellor3b0a72f2017-10-19 10:12:09 -0700226 Bundle b = new Bundle();
227 if (uri != null) {
Jason Monk2af19982017-11-07 19:38:27 -0500228 Slice s = handleBindSlice(uri, supportedSpecs);
Mady Mellor3b0a72f2017-10-19 10:12:09 -0700229 b.putParcelable(EXTRA_SLICE, s);
230 } else {
231 b.putParcelable(EXTRA_SLICE, null);
232 }
233 return b;
Jason Monkd9edfa942017-09-25 12:38:53 -0400234 }
235 return super.call(method, arg, extras);
236 }
237
Jason Monk2af19982017-11-07 19:38:27 -0500238 private Slice handleBindSlice(Uri sliceUri, List<SliceSpec> supportedSpecs) {
Lucas Dupine0653fa2017-10-23 14:31:45 -0700239 if (Looper.myLooper() == Looper.getMainLooper()) {
Jason Monk2af19982017-11-07 19:38:27 -0500240 return onBindSliceStrict(sliceUri, supportedSpecs);
Lucas Dupine0653fa2017-10-23 14:31:45 -0700241 } else {
242 CountDownLatch latch = new CountDownLatch(1);
243 Slice[] output = new Slice[1];
244 Handler.getMain().post(() -> {
Jason Monk2af19982017-11-07 19:38:27 -0500245 output[0] = onBindSliceStrict(sliceUri, supportedSpecs);
Jason Monkd18651f2017-10-05 14:18:49 -0400246 latch.countDown();
Lucas Dupine0653fa2017-10-23 14:31:45 -0700247 });
248 try {
249 latch.await();
250 return output[0];
251 } catch (InterruptedException e) {
252 throw new RuntimeException(e);
Jason Monkd18651f2017-10-05 14:18:49 -0400253 }
Lucas Dupine0653fa2017-10-23 14:31:45 -0700254 }
255 }
256
Jason Monk2af19982017-11-07 19:38:27 -0500257 private Slice onBindSliceStrict(Uri sliceUri, List<SliceSpec> supportedSpecs) {
Lucas Dupine0653fa2017-10-23 14:31:45 -0700258 ThreadPolicy oldPolicy = StrictMode.getThreadPolicy();
Jason Monkd9edfa942017-09-25 12:38:53 -0400259 try {
Lucas Dupine0653fa2017-10-23 14:31:45 -0700260 StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
261 .detectAll()
262 .penaltyDeath()
263 .build());
Jason Monk2af19982017-11-07 19:38:27 -0500264 return onBindSlice(sliceUri, supportedSpecs);
Lucas Dupine0653fa2017-10-23 14:31:45 -0700265 } finally {
266 StrictMode.setThreadPolicy(oldPolicy);
Jason Monkd9edfa942017-09-25 12:38:53 -0400267 }
268 }
269}