• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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