1 /*
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 
17 package android.app;
18 
19 import android.annotation.MainThread;
20 import android.annotation.NonNull;
21 import android.annotation.Nullable;
22 import android.annotation.SystemApi;
23 import android.content.Context;
24 import android.content.Intent;
25 import android.content.pm.InstantAppRequestInfo;
26 import android.content.pm.InstantAppResolveInfo;
27 import android.os.Build;
28 import android.os.Bundle;
29 import android.os.Handler;
30 import android.os.IBinder;
31 import android.os.IRemoteCallback;
32 import android.os.Looper;
33 import android.os.Message;
34 import android.os.RemoteException;
35 import android.os.UserHandle;
36 import android.util.Log;
37 import android.util.Slog;
38 
39 import com.android.internal.annotations.VisibleForTesting;
40 import com.android.internal.os.SomeArgs;
41 
42 import java.util.Arrays;
43 import java.util.Collections;
44 import java.util.List;
45 
46 /**
47  * Base class for implementing the resolver service.
48  * @hide
49  */
50 @SystemApi
51 public abstract class InstantAppResolverService extends Service {
52     private static final boolean DEBUG_INSTANT = Build.IS_DEBUGGABLE;
53     private static final String TAG = "PackageManager";
54 
55     /** @hide */
56     public static final String EXTRA_RESOLVE_INFO = "android.app.extra.RESOLVE_INFO";
57     /** @hide */
58     public static final String EXTRA_SEQUENCE = "android.app.extra.SEQUENCE";
59     Handler mHandler;
60 
61     /**
62      * Called to retrieve resolve info for instant applications immediately.
63      *
64      * @param digestPrefix The hash prefix of the instant app's domain.
65      *
66      * @deprecated Should implement {@link #onGetInstantAppResolveInfo(InstantAppRequestInfo,
67      *             InstantAppResolutionCallback)}
68      */
69     @Deprecated
onGetInstantAppResolveInfo(@ullable int[] digestPrefix, @NonNull String token, @NonNull InstantAppResolutionCallback callback)70     public void onGetInstantAppResolveInfo(@Nullable int[] digestPrefix, @NonNull String token,
71             @NonNull InstantAppResolutionCallback callback) {
72         throw new IllegalStateException("Must define onGetInstantAppResolveInfo");
73     }
74 
75     /**
76      * Called to retrieve intent filters for instant applications from potentially expensive
77      * sources.
78      *
79      * @param digestPrefix The hash prefix of the instant app's domain.
80      *
81      * @deprecated Should implement {@link #onGetInstantAppIntentFilter(InstantAppRequestInfo,
82      *             InstantAppResolutionCallback)}
83      */
84     @Deprecated
onGetInstantAppIntentFilter(@ullable int[] digestPrefix, @NonNull String token, @NonNull InstantAppResolutionCallback callback)85     public void onGetInstantAppIntentFilter(@Nullable int[] digestPrefix, @NonNull String token,
86             @NonNull InstantAppResolutionCallback callback) {
87         throw new IllegalStateException("Must define onGetInstantAppIntentFilter");
88     }
89 
90     /**
91      * Called to retrieve resolve info for instant applications immediately. The response will be
92      * ignored if not provided within a reasonable time. {@link InstantAppResolveInfo}s provided
93      * in response to this method may be partial to request a second phase of resolution which will
94      * result in a subsequent call to
95      * {@link #onGetInstantAppIntentFilter(Intent, int[], String, InstantAppResolutionCallback)}
96      *
97      * @param sanitizedIntent The sanitized {@link Intent} used for resolution. A sanitized Intent
98      *                        is an intent with potential PII removed from the original intent.
99      *                        Fields removed include extras and the host + path of the data, if
100      *                        defined.
101      * @param hostDigestPrefix The hash prefix of the instant app's domain.
102      * @param token A unique identifier that will be provided in calls to
103      *              {@link #onGetInstantAppIntentFilter(Intent, int[], String,
104      *              InstantAppResolutionCallback)}
105      *              and provided to the installer via {@link Intent#EXTRA_INSTANT_APP_TOKEN} to
106      *              tie a single launch together.
107      * @param callback The {@link InstantAppResolutionCallback} to provide results to.
108      *
109      * @see InstantAppResolveInfo
110      *
111      * @deprecated Should implement {@link #onGetInstantAppResolveInfo(InstantAppRequestInfo,
112      *             InstantAppResolutionCallback)}
113      */
114     @Deprecated
onGetInstantAppResolveInfo(@onNull Intent sanitizedIntent, @Nullable int[] hostDigestPrefix, @NonNull String token, @NonNull InstantAppResolutionCallback callback)115     public void onGetInstantAppResolveInfo(@NonNull Intent sanitizedIntent,
116             @Nullable int[] hostDigestPrefix, @NonNull String token,
117             @NonNull InstantAppResolutionCallback callback) {
118         // if not overridden, forward to old methods and filter out non-web intents
119         if (sanitizedIntent.isWebIntent()) {
120             onGetInstantAppResolveInfo(hostDigestPrefix, token, callback);
121         } else {
122             callback.onInstantAppResolveInfo(Collections.emptyList());
123         }
124     }
125 
126     /**
127      * Called to retrieve intent filters for potentially matching instant applications. Unlike
128      * {@link #onGetInstantAppResolveInfo(Intent, int[], String, InstantAppResolutionCallback)},
129      * the response may take as long as necessary to respond. All {@link InstantAppResolveInfo}s
130      * provided in response to this method must be completely populated.
131      *
132      * @param sanitizedIntent The sanitized {@link Intent} used for resolution.
133      * @param hostDigestPrefix The hash prefix of the instant app's domain or null if no host is
134      *                         defined.
135      * @param token A unique identifier that was provided in
136      *              {@link #onGetInstantAppResolveInfo(Intent, int[], String,
137      *              InstantAppResolutionCallback)}
138      *              and provided to the currently visible installer via
139      *              {@link Intent#EXTRA_INSTANT_APP_TOKEN}.
140      * @param callback The {@link InstantAppResolutionCallback} to provide results to.
141      *
142      * @deprecated Should implement {@link #onGetInstantAppIntentFilter(InstantAppRequestInfo,
143      *             InstantAppResolutionCallback)}
144      */
145     @Deprecated
onGetInstantAppIntentFilter(@onNull Intent sanitizedIntent, @Nullable int[] hostDigestPrefix, @NonNull String token, @NonNull InstantAppResolutionCallback callback)146     public void onGetInstantAppIntentFilter(@NonNull Intent sanitizedIntent,
147             @Nullable int[] hostDigestPrefix,
148             @NonNull String token, @NonNull InstantAppResolutionCallback callback) {
149         Log.e(TAG, "New onGetInstantAppIntentFilter is not overridden");
150         // if not overridden, forward to old methods and filter out non-web intents
151         if (sanitizedIntent.isWebIntent()) {
152             onGetInstantAppIntentFilter(hostDigestPrefix, token, callback);
153         } else {
154             callback.onInstantAppResolveInfo(Collections.emptyList());
155         }
156     }
157 
158     /**
159      * Called to retrieve resolve info for instant applications immediately. The response will be
160      * ignored if not provided within a reasonable time. {@link InstantAppResolveInfo}s provided
161      * in response to this method may be partial to request a second phase of resolution which will
162      * result in a subsequent call to {@link #onGetInstantAppIntentFilter(Intent, int[], UserHandle,
163      * String, InstantAppResolutionCallback)}
164      *
165      * @param sanitizedIntent The sanitized {@link Intent} used for resolution. A sanitized Intent
166      *                        is an intent with potential PII removed from the original intent.
167      *                        Fields removed include extras and the host + path of the data, if
168      *                        defined.
169      * @param hostDigestPrefix The hash prefix of the instant app's domain.
170      * @param userHandle The user for which to resolve the instant app.
171      * @param token A unique identifier that will be provided in calls to {@link
172      *              #onGetInstantAppIntentFilter(Intent, int[], UserHandle, String,
173      *              InstantAppResolutionCallback)} and provided to the installer via {@link
174      *              Intent#EXTRA_INSTANT_APP_TOKEN} to tie a single launch together.
175      * @param callback The {@link InstantAppResolutionCallback} to provide results to.
176      *
177      * @see InstantAppResolveInfo
178      *
179      * @deprecated Should implement {@link #onGetInstantAppResolveInfo(InstantAppRequestInfo,
180      *             InstantAppResolutionCallback
181      */
182     @Deprecated
onGetInstantAppResolveInfo(@onNull Intent sanitizedIntent, @Nullable int[] hostDigestPrefix, @NonNull UserHandle userHandle, @NonNull String token, @NonNull InstantAppResolutionCallback callback)183     public void onGetInstantAppResolveInfo(@NonNull Intent sanitizedIntent,
184             @Nullable int[] hostDigestPrefix, @NonNull UserHandle userHandle,
185             @NonNull String token, @NonNull InstantAppResolutionCallback callback) {
186         // If not overridden, forward to the old method.
187         onGetInstantAppResolveInfo(sanitizedIntent, hostDigestPrefix, token, callback);
188     }
189 
190     /**
191      * Called to retrieve intent filters for potentially matching instant applications. Unlike
192      * {@link #onGetInstantAppResolveInfo(Intent, int[], UserHandle, String,
193      * InstantAppResolutionCallback)}, the response may take as long as necessary to respond. All
194      * {@link InstantAppResolveInfo}s provided in response to this method must be completely
195      * populated.
196      *
197      * @param sanitizedIntent The sanitized {@link Intent} used for resolution.
198      * @param hostDigestPrefix The hash prefix of the instant app's domain or null if no host is
199      *                         defined.
200      * @param userHandle The user for which to resolve the instant app.
201      * @param token A unique identifier that was provided in {@link #onGetInstantAppResolveInfo(
202      *              Intent, int[], UserHandle, String, InstantAppResolutionCallback)} and provided
203      *              to the currently visible installer via {@link Intent#EXTRA_INSTANT_APP_TOKEN}.
204      * @param callback The {@link InstantAppResolutionCallback} to provide results to.
205      *
206      * @deprecated Should implement {@link #onGetInstantAppIntentFilter(InstantAppRequestInfo,
207      *             InstantAppResolutionCallback)}
208      */
209     @Deprecated
onGetInstantAppIntentFilter(@onNull Intent sanitizedIntent, @Nullable int[] hostDigestPrefix, @NonNull UserHandle userHandle, @NonNull String token, @NonNull InstantAppResolutionCallback callback)210     public void onGetInstantAppIntentFilter(@NonNull Intent sanitizedIntent,
211             @Nullable int[] hostDigestPrefix, @NonNull UserHandle userHandle,
212             @NonNull String token, @NonNull InstantAppResolutionCallback callback) {
213         // If not overridden, forward to the old method.
214         onGetInstantAppIntentFilter(sanitizedIntent, hostDigestPrefix, token, callback);
215     }
216 
217     /**
218      * Called to retrieve resolve info for instant applications immediately. The response will be
219      * ignored if not provided within a reasonable time. {@link InstantAppResolveInfo}s provided
220      * in response to this method may be partial to request a second phase of resolution which will
221      * result in a subsequent call to {@link #onGetInstantAppIntentFilter(InstantAppRequestInfo,
222      * InstantAppResolutionCallback)}
223      *
224      * @param request The parameters for this resolution request
225      * @param callback The {@link InstantAppResolutionCallback} to provide results to.
226      *
227      * @see InstantAppResolveInfo
228      */
229     @MainThread
onGetInstantAppResolveInfo(@onNull InstantAppRequestInfo request, @NonNull InstantAppResolutionCallback callback)230     public void onGetInstantAppResolveInfo(@NonNull InstantAppRequestInfo request,
231             @NonNull InstantAppResolutionCallback callback) {
232         // If not overridden, forward to the old method.
233         onGetInstantAppResolveInfo(request.getIntent(), request.getHostDigestPrefix(),
234                 request.getUserHandle(), request.getToken(), callback);
235     }
236 
237     /**
238      * Called to retrieve intent filters for potentially matching instant applications. Unlike
239      * {@link #onGetInstantAppResolveInfo(InstantAppRequestInfo, InstantAppResolutionCallback)},
240      * the response may take as long as necessary to respond. All {@link InstantAppResolveInfo}s
241      * provided in response to this method must be completely populated.
242      *
243      * @param request The parameters for this resolution request
244      * @param callback The {@link InstantAppResolutionCallback} to provide results to.
245      */
246     @MainThread
onGetInstantAppIntentFilter(@onNull InstantAppRequestInfo request, @NonNull InstantAppResolutionCallback callback)247     public void onGetInstantAppIntentFilter(@NonNull InstantAppRequestInfo request,
248             @NonNull InstantAppResolutionCallback callback) {
249         // If not overridden, forward to the old method.
250         onGetInstantAppIntentFilter(request.getIntent(), request.getHostDigestPrefix(),
251                 request.getUserHandle(), request.getToken(), callback);
252     }
253 
254     /**
255      * Returns a {@link Looper} to perform service operations on.
256      */
getLooper()257     Looper getLooper() {
258         return getBaseContext().getMainLooper();
259     }
260 
261     @Override
attachBaseContext(Context base)262     public final void attachBaseContext(Context base) {
263         super.attachBaseContext(base);
264         mHandler = new ServiceHandler(getLooper());
265     }
266 
267     @Override
onBind(Intent intent)268     public final IBinder onBind(Intent intent) {
269         return new IInstantAppResolver.Stub() {
270             @Override
271             public void getInstantAppResolveInfoList(InstantAppRequestInfo request, int sequence,
272                     IRemoteCallback callback) {
273                 if (DEBUG_INSTANT) {
274                     Slog.v(TAG, "[" + request.getToken() + "] Phase1 called; posting");
275                 }
276                 final SomeArgs args = SomeArgs.obtain();
277                 args.arg1 = request;
278                 args.arg2 = callback;
279                 mHandler.obtainMessage(ServiceHandler.MSG_GET_INSTANT_APP_RESOLVE_INFO, sequence,
280                         0, args).sendToTarget();
281             }
282 
283             @Override
284             public void getInstantAppIntentFilterList(InstantAppRequestInfo request,
285                     IRemoteCallback callback) {
286                 if (DEBUG_INSTANT) {
287                     Slog.v(TAG, "[" + request.getToken() + "] Phase2 called; posting");
288                 }
289                 final SomeArgs args = SomeArgs.obtain();
290                 args.arg1 = request;
291                 args.arg2 = callback;
292                 mHandler.obtainMessage(ServiceHandler.MSG_GET_INSTANT_APP_INTENT_FILTER, args)
293                         .sendToTarget();
294             }
295         };
296     }
297 
298     /**
299      * Callback to post results from instant app resolution.
300      */
301     public static final class InstantAppResolutionCallback {
302         private final IRemoteCallback mCallback;
303         private final int mSequence;
304 
305         /** @hide **/
306         @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
307         public InstantAppResolutionCallback(int sequence, IRemoteCallback callback) {
308             mCallback = callback;
309             mSequence = sequence;
310         }
311 
312         public void onInstantAppResolveInfo(List<InstantAppResolveInfo> resolveInfo) {
313             final Bundle data = new Bundle();
314             data.putParcelableList(EXTRA_RESOLVE_INFO, resolveInfo);
315             data.putInt(EXTRA_SEQUENCE, mSequence);
316             try {
317                 mCallback.sendResult(data);
318             } catch (RemoteException e) {
319             }
320         }
321     }
322 
323     private final class ServiceHandler extends Handler {
324         public static final int MSG_GET_INSTANT_APP_RESOLVE_INFO = 1;
325         public static final int MSG_GET_INSTANT_APP_INTENT_FILTER = 2;
326         public ServiceHandler(Looper looper) {
327             super(looper, null /*callback*/, true /*async*/);
328         }
329 
330         @Override
331         @SuppressWarnings("unchecked")
332         public void handleMessage(Message message) {
333             final int action = message.what;
334             switch (action) {
335                 case MSG_GET_INSTANT_APP_RESOLVE_INFO: {
336                     final SomeArgs args = (SomeArgs) message.obj;
337                     final InstantAppRequestInfo request = (InstantAppRequestInfo) args.arg1;
338                     final IRemoteCallback callback = (IRemoteCallback) args.arg2;
339                     args.recycle();
340                     final int sequence = message.arg1;
341                     if (DEBUG_INSTANT) {
342                         Slog.d(TAG, "[" + request.getToken() + "] Phase1 request;"
343                                 + " prefix: " + Arrays.toString(request.getHostDigestPrefix())
344                                 + ", userId: " + request.getUserHandle().getIdentifier());
345                     }
346                     onGetInstantAppResolveInfo(request,
347                             new InstantAppResolutionCallback(sequence, callback));
348                 } break;
349 
350                 case MSG_GET_INSTANT_APP_INTENT_FILTER: {
351                     final SomeArgs args = (SomeArgs) message.obj;
352                     final InstantAppRequestInfo request = (InstantAppRequestInfo) args.arg1;
353                     final IRemoteCallback callback = (IRemoteCallback) args.arg2;
354                     args.recycle();
355                     if (DEBUG_INSTANT) {
356                         Slog.d(TAG, "[" + request.getToken() + "] Phase2 request;"
357                                 + " prefix: " + Arrays.toString(request.getHostDigestPrefix())
358                                 + ", userId: " + request.getUserHandle().getIdentifier());
359                     }
360                     onGetInstantAppIntentFilter(request,
361                             new InstantAppResolutionCallback(-1 /*sequence*/, callback));
362                 }
363                 break;
364 
365                 default: {
366                     throw new IllegalArgumentException("Unknown message: " + action);
367                 }
368             }
369         }
370     }
371 }
372