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