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