1 /* 2 * Copyright (C) 2016 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 com.android.server.pm; 18 19 import static android.content.Intent.FLAG_ACTIVITY_MATCH_EXTERNAL; 20 21 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.ACTION_INSTANT_APP_RESOLUTION_PHASE_ONE; 22 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.ACTION_INSTANT_APP_RESOLUTION_PHASE_TWO; 23 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_INSTANT_APP_LAUNCH_TOKEN; 24 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_INSTANT_APP_RESOLUTION_DELAY_MS; 25 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_INSTANT_APP_RESOLUTION_STATUS; 26 27 import android.annotation.IntDef; 28 import android.annotation.NonNull; 29 import android.annotation.Nullable; 30 import android.app.ActivityManager; 31 import android.app.ActivityOptions; 32 import android.app.PendingIntent; 33 import android.content.ComponentName; 34 import android.content.Context; 35 import android.content.IIntentSender; 36 import android.content.Intent; 37 import android.content.IntentFilter; 38 import android.content.IntentSender; 39 import android.content.pm.ActivityInfo; 40 import android.content.pm.AuxiliaryResolveInfo; 41 import android.content.pm.InstantAppIntentFilter; 42 import android.content.pm.InstantAppRequest; 43 import android.content.pm.InstantAppRequestInfo; 44 import android.content.pm.InstantAppResolveInfo; 45 import android.content.pm.InstantAppResolveInfo.InstantAppDigest; 46 import android.metrics.LogMaker; 47 import android.net.Uri; 48 import android.os.Build; 49 import android.os.Bundle; 50 import android.os.Handler; 51 import android.os.RemoteException; 52 import android.os.UserHandle; 53 import android.text.TextUtils; 54 import android.util.Log; 55 import android.util.Slog; 56 57 import com.android.internal.logging.MetricsLogger; 58 import com.android.internal.logging.nano.MetricsProto; 59 import com.android.server.pm.InstantAppResolverConnection.ConnectionException; 60 import com.android.server.pm.InstantAppResolverConnection.PhaseTwoCallback; 61 import com.android.server.pm.resolution.ComponentResolver; 62 63 import java.lang.annotation.Retention; 64 import java.lang.annotation.RetentionPolicy; 65 import java.util.ArrayList; 66 import java.util.Arrays; 67 import java.util.Collections; 68 import java.util.Iterator; 69 import java.util.List; 70 import java.util.Set; 71 72 /** @hide */ 73 public abstract class InstantAppResolver { 74 private static final boolean DEBUG_INSTANT = Build.IS_DEBUGGABLE; 75 private static final String TAG = "PackageManager"; 76 77 private static final int RESOLUTION_SUCCESS = 0; 78 private static final int RESOLUTION_FAILURE = 1; 79 /** Binding to the external service timed out */ 80 private static final int RESOLUTION_BIND_TIMEOUT = 2; 81 /** The call to retrieve an instant application response timed out */ 82 private static final int RESOLUTION_CALL_TIMEOUT = 3; 83 84 @IntDef(flag = true, prefix = { "RESOLUTION_" }, value = { 85 RESOLUTION_SUCCESS, 86 RESOLUTION_FAILURE, 87 RESOLUTION_BIND_TIMEOUT, 88 RESOLUTION_CALL_TIMEOUT, 89 }) 90 @Retention(RetentionPolicy.SOURCE) 91 public @interface ResolutionStatus {} 92 93 private static MetricsLogger sMetricsLogger; 94 getLogger()95 private static MetricsLogger getLogger() { 96 if (sMetricsLogger == null) { 97 sMetricsLogger = new MetricsLogger(); 98 } 99 return sMetricsLogger; 100 } 101 102 /** 103 * Returns an intent with potential PII removed from the original intent. Fields removed 104 * include extras and the host + path of the data, if defined. 105 */ sanitizeIntent(Intent origIntent)106 public static Intent sanitizeIntent(Intent origIntent) { 107 final Intent sanitizedIntent; 108 sanitizedIntent = new Intent(origIntent.getAction()); 109 Set<String> categories = origIntent.getCategories(); 110 if (categories != null) { 111 for (String category : categories) { 112 sanitizedIntent.addCategory(category); 113 } 114 } 115 Uri sanitizedUri = origIntent.getData() == null 116 ? null 117 : Uri.fromParts(origIntent.getScheme(), "", ""); 118 sanitizedIntent.setDataAndType(sanitizedUri, origIntent.getType()); 119 sanitizedIntent.addFlags(origIntent.getFlags()); 120 sanitizedIntent.setPackage(origIntent.getPackage()); 121 return sanitizedIntent; 122 } 123 124 /** 125 * Generate an {@link InstantAppDigest} from an {@link Intent} which contains hashes of the 126 * host. The object contains both secure and insecure hash array variants, and the secure 127 * version must be passed along to ensure the random data is consistent. 128 */ 129 @NonNull parseDigest(@onNull Intent origIntent)130 public static InstantAppDigest parseDigest(@NonNull Intent origIntent) { 131 if (origIntent.getData() != null && !TextUtils.isEmpty(origIntent.getData().getHost())) { 132 return new InstantAppResolveInfo.InstantAppDigest(origIntent.getData().getHost(), 133 5 /*maxDigests*/); 134 } else { 135 return InstantAppResolveInfo.InstantAppDigest.UNDEFINED; 136 } 137 } 138 doInstantAppResolutionPhaseOne(@onNull Computer computer, @NonNull UserManagerService userManager, InstantAppResolverConnection connection, InstantAppRequest requestObj)139 public static AuxiliaryResolveInfo doInstantAppResolutionPhaseOne(@NonNull Computer computer, 140 @NonNull UserManagerService userManager, InstantAppResolverConnection connection, 141 InstantAppRequest requestObj) { 142 final long startTime = System.currentTimeMillis(); 143 final String token = requestObj.token; 144 if (DEBUG_INSTANT) { 145 Log.d(TAG, "[" + token + "] Phase1; resolving"); 146 } 147 148 AuxiliaryResolveInfo resolveInfo = null; 149 @ResolutionStatus int resolutionStatus = RESOLUTION_SUCCESS; 150 Intent origIntent = requestObj.origIntent; 151 try { 152 final List<InstantAppResolveInfo> instantAppResolveInfoList = 153 connection.getInstantAppResolveInfoList(buildRequestInfo(requestObj)); 154 if (instantAppResolveInfoList != null && instantAppResolveInfoList.size() > 0) { 155 resolveInfo = InstantAppResolver.filterInstantAppIntent(computer, userManager, 156 instantAppResolveInfoList, origIntent, requestObj.resolvedType, 157 requestObj.userId, origIntent.getPackage(), token, 158 requestObj.hostDigestPrefixSecure); 159 } 160 } catch (ConnectionException e) { 161 if (e.failure == ConnectionException.FAILURE_BIND) { 162 resolutionStatus = RESOLUTION_BIND_TIMEOUT; 163 } else if (e.failure == ConnectionException.FAILURE_CALL) { 164 resolutionStatus = RESOLUTION_CALL_TIMEOUT; 165 } else { 166 resolutionStatus = RESOLUTION_FAILURE; 167 } 168 } 169 // Only log successful instant application resolution 170 if (requestObj.resolveForStart && resolutionStatus == RESOLUTION_SUCCESS) { 171 logMetrics(ACTION_INSTANT_APP_RESOLUTION_PHASE_ONE, startTime, token, 172 resolutionStatus); 173 } 174 if (DEBUG_INSTANT && resolveInfo == null) { 175 if (resolutionStatus == RESOLUTION_BIND_TIMEOUT) { 176 Log.d(TAG, "[" + token + "] Phase1; bind timed out"); 177 } else if (resolutionStatus == RESOLUTION_CALL_TIMEOUT) { 178 Log.d(TAG, "[" + token + "] Phase1; call timed out"); 179 } else if (resolutionStatus != RESOLUTION_SUCCESS) { 180 Log.d(TAG, "[" + token + "] Phase1; service connection error"); 181 } else { 182 Log.d(TAG, "[" + token + "] Phase1; No results matched"); 183 } 184 } 185 // if the match external flag is set, return an empty resolve info instead of a null result. 186 if (resolveInfo == null && (origIntent.getFlags() & FLAG_ACTIVITY_MATCH_EXTERNAL) != 0) { 187 return new AuxiliaryResolveInfo(token, false, createFailureIntent(origIntent, token), 188 null /* filters */, requestObj.hostDigestPrefixSecure); 189 } 190 return resolveInfo; 191 } 192 doInstantAppResolutionPhaseTwo(Context context, @NonNull Computer computer, @NonNull UserManagerService userManager, InstantAppResolverConnection connection, InstantAppRequest requestObj, ActivityInfo instantAppInstaller, Handler callbackHandler)193 public static void doInstantAppResolutionPhaseTwo(Context context, @NonNull Computer computer, 194 @NonNull UserManagerService userManager, InstantAppResolverConnection connection, 195 InstantAppRequest requestObj, ActivityInfo instantAppInstaller, 196 Handler callbackHandler) { 197 final long startTime = System.currentTimeMillis(); 198 final String token = requestObj.token; 199 if (DEBUG_INSTANT) { 200 Log.d(TAG, "[" + token + "] Phase2; resolving"); 201 } 202 final Intent origIntent = requestObj.origIntent; 203 final Intent sanitizedIntent = sanitizeIntent(origIntent); 204 205 final PhaseTwoCallback callback = new PhaseTwoCallback() { 206 @Override 207 void onPhaseTwoResolved(List<InstantAppResolveInfo> instantAppResolveInfoList, 208 long startTime) { 209 final Intent failureIntent; 210 if (instantAppResolveInfoList != null && instantAppResolveInfoList.size() > 0) { 211 final AuxiliaryResolveInfo instantAppIntentInfo = 212 InstantAppResolver.filterInstantAppIntent(computer, userManager, 213 instantAppResolveInfoList, origIntent, null /*resolvedType*/, 214 0 /*userId*/, origIntent.getPackage(), 215 token, requestObj.hostDigestPrefixSecure); 216 if (instantAppIntentInfo != null) { 217 failureIntent = instantAppIntentInfo.failureIntent; 218 } else { 219 failureIntent = null; 220 } 221 } else { 222 failureIntent = null; 223 } 224 final Intent installerIntent = buildEphemeralInstallerIntent( 225 requestObj.origIntent, 226 sanitizedIntent, 227 failureIntent, 228 requestObj.callingPackage, 229 requestObj.callingFeatureId, 230 requestObj.verificationBundle, 231 requestObj.resolvedType, 232 requestObj.userId, 233 requestObj.responseObj.installFailureActivity, 234 token, 235 false /*needsPhaseTwo*/, 236 requestObj.responseObj.filters); 237 installerIntent.setComponent(new ComponentName( 238 instantAppInstaller.packageName, instantAppInstaller.name)); 239 240 logMetrics(ACTION_INSTANT_APP_RESOLUTION_PHASE_TWO, startTime, token, 241 requestObj.responseObj.filters != null ? RESOLUTION_SUCCESS : RESOLUTION_FAILURE); 242 243 context.startActivity(installerIntent); 244 } 245 }; 246 try { 247 connection.getInstantAppIntentFilterList(buildRequestInfo(requestObj), callback, 248 callbackHandler, startTime); 249 } catch (ConnectionException e) { 250 @ResolutionStatus int resolutionStatus = RESOLUTION_FAILURE; 251 if (e.failure == ConnectionException.FAILURE_BIND) { 252 resolutionStatus = RESOLUTION_BIND_TIMEOUT; 253 } 254 logMetrics(ACTION_INSTANT_APP_RESOLUTION_PHASE_TWO, startTime, token, 255 resolutionStatus); 256 if (DEBUG_INSTANT) { 257 if (resolutionStatus == RESOLUTION_BIND_TIMEOUT) { 258 Log.d(TAG, "[" + token + "] Phase2; bind timed out"); 259 } else { 260 Log.d(TAG, "[" + token + "] Phase2; service connection error"); 261 } 262 } 263 } 264 } 265 266 /** 267 * Builds and returns an intent to launch the instant installer. 268 */ buildEphemeralInstallerIntent( @onNull Intent origIntent, @NonNull Intent sanitizedIntent, @Nullable Intent failureIntent, @NonNull String callingPackage, @Nullable String callingFeatureId, @Nullable Bundle verificationBundle, @NonNull String resolvedType, int userId, @Nullable ComponentName installFailureActivity, @Nullable String token, boolean needsPhaseTwo, List<AuxiliaryResolveInfo.AuxiliaryFilter> filters)269 public static Intent buildEphemeralInstallerIntent( 270 @NonNull Intent origIntent, 271 @NonNull Intent sanitizedIntent, 272 @Nullable Intent failureIntent, 273 @NonNull String callingPackage, 274 @Nullable String callingFeatureId, 275 @Nullable Bundle verificationBundle, 276 @NonNull String resolvedType, 277 int userId, 278 @Nullable ComponentName installFailureActivity, 279 @Nullable String token, 280 boolean needsPhaseTwo, 281 List<AuxiliaryResolveInfo.AuxiliaryFilter> filters) { 282 // Construct the intent that launches the instant installer 283 int flags = origIntent.getFlags(); 284 final Intent intent = new Intent(); 285 intent.setFlags(flags 286 | Intent.FLAG_ACTIVITY_NO_HISTORY 287 | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); 288 if (token != null) { 289 intent.putExtra(Intent.EXTRA_INSTANT_APP_TOKEN, token); 290 } 291 if (origIntent.getData() != null) { 292 intent.putExtra(Intent.EXTRA_INSTANT_APP_HOSTNAME, origIntent.getData().getHost()); 293 } 294 intent.putExtra(Intent.EXTRA_INSTANT_APP_ACTION, origIntent.getAction()); 295 intent.putExtra(Intent.EXTRA_INTENT, sanitizedIntent); 296 297 if (needsPhaseTwo) { 298 intent.setAction(Intent.ACTION_RESOLVE_INSTANT_APP_PACKAGE); 299 } else { 300 ActivityOptions options = ActivityOptions.makeBasic() 301 .setPendingIntentCreatorBackgroundActivityStartMode( 302 ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED); 303 // We have all of the data we need; just start the installer without a second phase 304 if (failureIntent != null || installFailureActivity != null) { 305 // Intent that is launched if the package couldn't be installed for any reason. 306 try { 307 final Intent onFailureIntent; 308 if (installFailureActivity != null) { 309 onFailureIntent = new Intent(); 310 onFailureIntent.setComponent(installFailureActivity); 311 if (filters != null && filters.size() == 1) { 312 onFailureIntent.putExtra(Intent.EXTRA_SPLIT_NAME, 313 filters.get(0).splitName); 314 } 315 onFailureIntent.putExtra(Intent.EXTRA_INTENT, origIntent); 316 } else { 317 onFailureIntent = failureIntent; 318 } 319 final IIntentSender failureIntentTarget = ActivityManager.getService() 320 .getIntentSenderWithFeature( 321 ActivityManager.INTENT_SENDER_ACTIVITY, callingPackage, 322 callingFeatureId, null /*token*/, null /*resultWho*/, 323 1 /*requestCode*/, 324 new Intent[] { onFailureIntent }, 325 new String[] { resolvedType }, 326 PendingIntent.FLAG_CANCEL_CURRENT 327 | PendingIntent.FLAG_ONE_SHOT 328 | PendingIntent.FLAG_IMMUTABLE, 329 options.toBundle(), userId); 330 IntentSender failureSender = new IntentSender(failureIntentTarget); 331 // TODO(b/72700831): remove populating old extra 332 intent.putExtra(Intent.EXTRA_INSTANT_APP_FAILURE, failureSender); 333 } catch (RemoteException ignore) { /* ignore; same process */ } 334 } 335 336 // Intent that is launched if the package was installed successfully. 337 final Intent successIntent = new Intent(origIntent); 338 successIntent.setLaunchToken(token); 339 try { 340 final IIntentSender successIntentTarget = ActivityManager.getService() 341 .getIntentSenderWithFeature( 342 ActivityManager.INTENT_SENDER_ACTIVITY, callingPackage, 343 callingFeatureId, null /*token*/, null /*resultWho*/, 344 0 /*requestCode*/, 345 new Intent[] { successIntent }, 346 new String[] { resolvedType }, 347 PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT 348 | PendingIntent.FLAG_IMMUTABLE, 349 options.toBundle(), userId); 350 IntentSender successSender = new IntentSender(successIntentTarget); 351 intent.putExtra(Intent.EXTRA_INSTANT_APP_SUCCESS, successSender); 352 } catch (RemoteException ignore) { /* ignore; same process */ } 353 if (verificationBundle != null) { 354 intent.putExtra(Intent.EXTRA_VERIFICATION_BUNDLE, verificationBundle); 355 } 356 intent.putExtra(Intent.EXTRA_CALLING_PACKAGE, callingPackage); 357 358 if (filters != null) { 359 Bundle resolvableFilters[] = new Bundle[filters.size()]; 360 for (int i = 0, max = filters.size(); i < max; i++) { 361 Bundle resolvableFilter = new Bundle(); 362 AuxiliaryResolveInfo.AuxiliaryFilter filter = filters.get(i); 363 resolvableFilter.putBoolean(Intent.EXTRA_UNKNOWN_INSTANT_APP, 364 filter.resolveInfo != null 365 && filter.resolveInfo.shouldLetInstallerDecide()); 366 resolvableFilter.putString(Intent.EXTRA_PACKAGE_NAME, filter.packageName); 367 resolvableFilter.putString(Intent.EXTRA_SPLIT_NAME, filter.splitName); 368 resolvableFilter.putLong(Intent.EXTRA_LONG_VERSION_CODE, filter.versionCode); 369 resolvableFilter.putBundle(Intent.EXTRA_INSTANT_APP_EXTRAS, filter.extras); 370 resolvableFilters[i] = resolvableFilter; 371 if (i == 0) { 372 // for backwards compat, always set the first result on the intent and add 373 // the int version code 374 intent.putExtras(resolvableFilter); 375 intent.putExtra(Intent.EXTRA_VERSION_CODE, (int) filter.versionCode); 376 } 377 } 378 intent.putExtra(Intent.EXTRA_INSTANT_APP_BUNDLES, resolvableFilters); 379 } 380 intent.setAction(Intent.ACTION_INSTALL_INSTANT_APP_PACKAGE); 381 } 382 return intent; 383 } 384 buildRequestInfo(InstantAppRequest request)385 private static InstantAppRequestInfo buildRequestInfo(InstantAppRequest request) { 386 return new InstantAppRequestInfo( 387 sanitizeIntent(request.origIntent), 388 // This must only expose the secured version of the host 389 request.hostDigestPrefixSecure, 390 UserHandle.of(request.userId), 391 request.isRequesterInstantApp, 392 request.token 393 ); 394 } 395 filterInstantAppIntent(@onNull Computer computer, @NonNull UserManagerService userManager, List<InstantAppResolveInfo> instantAppResolveInfoList, Intent origIntent, String resolvedType, int userId, String packageName, String token, int[] hostDigestPrefixSecure)396 private static AuxiliaryResolveInfo filterInstantAppIntent(@NonNull Computer computer, 397 @NonNull UserManagerService userManager, 398 List<InstantAppResolveInfo> instantAppResolveInfoList, Intent origIntent, 399 String resolvedType, int userId, String packageName, String token, 400 int[] hostDigestPrefixSecure) { 401 InstantAppDigest digest = InstantAppResolver.parseDigest(origIntent); 402 final int[] shaPrefix = digest.getDigestPrefix(); 403 final byte[][] digestBytes = digest.getDigestBytes(); 404 boolean requiresSecondPhase = false; 405 ArrayList<AuxiliaryResolveInfo.AuxiliaryFilter> filters = null; 406 boolean requiresPrefixMatch = origIntent.isWebIntent() || (shaPrefix.length > 0 407 && (origIntent.getFlags() & Intent.FLAG_ACTIVITY_MATCH_EXTERNAL) == 0); 408 for (InstantAppResolveInfo instantAppResolveInfo : instantAppResolveInfoList) { 409 if (requiresPrefixMatch && instantAppResolveInfo.shouldLetInstallerDecide()) { 410 Slog.d(TAG, "InstantAppResolveInfo with mShouldLetInstallerDecide=true when digest" 411 + " required; ignoring"); 412 continue; 413 } 414 byte[] filterDigestBytes = instantAppResolveInfo.getDigestBytes(); 415 // Only include matching digests if we have a prefix and we're either dealing with a 416 // prefixed request or the resolveInfo specifies digest details. 417 if (shaPrefix.length > 0 && (requiresPrefixMatch || filterDigestBytes.length > 0)) { 418 boolean matchFound = false; 419 // Go in reverse order so we match the narrowest scope first. 420 for (int i = shaPrefix.length - 1; i >= 0; --i) { 421 if (Arrays.equals(digestBytes[i], filterDigestBytes)) { 422 matchFound = true; 423 break; 424 } 425 } 426 if (!matchFound) { 427 continue; 428 } 429 } 430 // We matched a resolve info; resolve the filters to see if anything matches completely. 431 List<AuxiliaryResolveInfo.AuxiliaryFilter> matchFilters = computeResolveFilters( 432 computer, userManager, origIntent, resolvedType, userId, packageName, token, 433 instantAppResolveInfo); 434 if (matchFilters != null) { 435 if (matchFilters.isEmpty()) { 436 requiresSecondPhase = true; 437 } 438 if (filters == null) { 439 filters = new ArrayList<>(matchFilters); 440 } else { 441 filters.addAll(matchFilters); 442 } 443 } 444 } 445 if (filters != null && !filters.isEmpty()) { 446 return new AuxiliaryResolveInfo(token, requiresSecondPhase, 447 createFailureIntent(origIntent, token), filters, hostDigestPrefixSecure); 448 } 449 // Hash or filter mis-match; no instant apps for this domain. 450 return null; 451 } 452 453 /** 454 * Creates a failure intent for the installer to send in the case that the instant app cannot be 455 * launched for any reason. 456 */ createFailureIntent(Intent origIntent, String token)457 private static Intent createFailureIntent(Intent origIntent, String token) { 458 final Intent failureIntent = new Intent(origIntent); 459 failureIntent.setFlags(failureIntent.getFlags() | Intent.FLAG_IGNORE_EPHEMERAL); 460 failureIntent.setFlags(failureIntent.getFlags() & ~Intent.FLAG_ACTIVITY_MATCH_EXTERNAL); 461 failureIntent.setLaunchToken(token); 462 return failureIntent; 463 } 464 465 /** 466 * Returns one of three states: <p/> 467 * <ul> 468 * <li>{@code null} if there are no matches will not be; resolution is unnecessary.</li> 469 * <li>An empty list signifying that a 2nd phase of resolution is required.</li> 470 * <li>A populated list meaning that matches were found and should be sent directly to the 471 * installer</li> 472 * </ul> 473 * 474 */ computeResolveFilters( @onNull Computer computer, @NonNull UserManagerService userManager, Intent origIntent, String resolvedType, int userId, String packageName, String token, InstantAppResolveInfo instantAppInfo)475 private static List<AuxiliaryResolveInfo.AuxiliaryFilter> computeResolveFilters( 476 @NonNull Computer computer, @NonNull UserManagerService userManager, Intent origIntent, 477 String resolvedType, int userId, String packageName, String token, 478 InstantAppResolveInfo instantAppInfo) { 479 if (instantAppInfo.shouldLetInstallerDecide()) { 480 return Collections.singletonList( 481 new AuxiliaryResolveInfo.AuxiliaryFilter( 482 instantAppInfo, null /* splitName */, 483 instantAppInfo.getExtras())); 484 } 485 if (packageName != null 486 && !packageName.equals(instantAppInfo.getPackageName())) { 487 return null; 488 } 489 final List<InstantAppIntentFilter> instantAppFilters = 490 instantAppInfo.getIntentFilters(); 491 if (instantAppFilters == null || instantAppFilters.isEmpty()) { 492 // No filters on web intent; no matches, 2nd phase unnecessary. 493 if (origIntent.isWebIntent()) { 494 return null; 495 } 496 // No filters; we need to start phase two 497 if (DEBUG_INSTANT) { 498 Log.d(TAG, "No app filters; go to phase 2"); 499 } 500 return Collections.emptyList(); 501 } 502 final ComponentResolver.InstantAppIntentResolver instantAppResolver = 503 new ComponentResolver.InstantAppIntentResolver(userManager); 504 for (int j = instantAppFilters.size() - 1; j >= 0; --j) { 505 final InstantAppIntentFilter instantAppFilter = instantAppFilters.get(j); 506 final List<IntentFilter> splitFilters = instantAppFilter.getFilters(); 507 if (splitFilters == null || splitFilters.isEmpty()) { 508 continue; 509 } 510 for (int k = splitFilters.size() - 1; k >= 0; --k) { 511 IntentFilter filter = splitFilters.get(k); 512 Iterator<IntentFilter.AuthorityEntry> authorities = 513 filter.authoritiesIterator(); 514 // ignore http/s-only filters. 515 if ((authorities == null || !authorities.hasNext()) 516 && (filter.hasDataScheme("http") || filter.hasDataScheme("https")) 517 && filter.hasAction(Intent.ACTION_VIEW) 518 && filter.hasCategory(Intent.CATEGORY_BROWSABLE)) { 519 continue; 520 } 521 instantAppResolver.addFilter(computer, 522 new AuxiliaryResolveInfo.AuxiliaryFilter( 523 filter, 524 instantAppInfo, 525 instantAppFilter.getSplitName(), 526 instantAppInfo.getExtras() 527 )); 528 } 529 } 530 List<AuxiliaryResolveInfo.AuxiliaryFilter> matchedResolveInfoList = 531 instantAppResolver.queryIntent(computer, origIntent, resolvedType, 532 false /*defaultOnly*/, userId); 533 if (!matchedResolveInfoList.isEmpty()) { 534 if (DEBUG_INSTANT) { 535 Log.d(TAG, "[" + token + "] Found match(es); " + matchedResolveInfoList); 536 } 537 return matchedResolveInfoList; 538 } else if (DEBUG_INSTANT) { 539 Log.d(TAG, "[" + token + "] No matches found" 540 + " package: " + instantAppInfo.getPackageName() 541 + ", versionCode: " + instantAppInfo.getVersionCode()); 542 } 543 return null; 544 } 545 logMetrics(int action, long startTime, String token, @ResolutionStatus int status)546 private static void logMetrics(int action, long startTime, String token, 547 @ResolutionStatus int status) { 548 final LogMaker logMaker = new LogMaker(action) 549 .setType(MetricsProto.MetricsEvent.TYPE_ACTION) 550 .addTaggedData(FIELD_INSTANT_APP_RESOLUTION_DELAY_MS, 551 new Long(System.currentTimeMillis() - startTime)) 552 .addTaggedData(FIELD_INSTANT_APP_LAUNCH_TOKEN, token) 553 .addTaggedData(FIELD_INSTANT_APP_RESOLUTION_STATUS, new Integer(status)); 554 getLogger().write(logMaker); 555 } 556 } 557