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