Home | History | Annotate | Download | only in pm
      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 
     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      */
    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 
    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 
    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      */
    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 
    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      */
    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      */
    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 
    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