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