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