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