blob: 30b2c9d21a6a47d5160ee34c3f92a2f01ed07860 [file] [log] [blame]
Todd Kennedy01ad0c72016-11-11 15:33:12 -08001/*
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
17package com.android.server.pm;
18
Patrick Baumann3e0b8ee2018-04-26 12:03:15 -070019import static android.content.Intent.FLAG_ACTIVITY_MATCH_EXTERNAL;
20
Todd Kennedy50d946c12017-03-17 13:55:38 -070021import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.ACTION_INSTANT_APP_RESOLUTION_PHASE_ONE;
22import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.ACTION_INSTANT_APP_RESOLUTION_PHASE_TWO;
23import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_INSTANT_APP_LAUNCH_TOKEN;
24import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_INSTANT_APP_RESOLUTION_DELAY_MS;
25import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_INSTANT_APP_RESOLUTION_STATUS;
26
Todd Kennedybdf2a802017-05-08 16:09:42 -070027import android.annotation.IntDef;
Todd Kennedye9910222017-02-21 16:00:11 -080028import android.annotation.NonNull;
29import android.annotation.Nullable;
Todd Kennedy01ad0c72016-11-11 15:33:12 -080030import android.app.ActivityManager;
Todd Kennedy01ad0c72016-11-11 15:33:12 -080031import android.app.PendingIntent;
32import android.content.ComponentName;
33import android.content.Context;
34import android.content.IIntentSender;
35import android.content.Intent;
36import android.content.IntentFilter;
37import android.content.IntentSender;
38import android.content.pm.ActivityInfo;
Todd Kennedye9910222017-02-21 16:00:11 -080039import android.content.pm.AuxiliaryResolveInfo;
Todd Kennedy1fb34042017-03-01 13:56:58 -080040import android.content.pm.InstantAppIntentFilter;
Todd Kennedycf827032018-07-03 13:17:22 -070041import android.content.pm.InstantAppRequest;
Todd Kennedy1fb34042017-03-01 13:56:58 -080042import android.content.pm.InstantAppResolveInfo;
43import android.content.pm.InstantAppResolveInfo.InstantAppDigest;
Todd Kennedy50d946c12017-03-17 13:55:38 -070044import android.metrics.LogMaker;
Patrick Baumann577d4022018-01-31 16:55:10 +000045import android.net.Uri;
Todd Kennedyd3c51062017-03-17 16:03:29 -070046import android.os.Build;
Chad Brubaker06068612017-04-06 09:43:47 -070047import android.os.Bundle;
Todd Kennedy01ad0c72016-11-11 15:33:12 -080048import android.os.Handler;
49import android.os.RemoteException;
50import android.util.Log;
Patrick Baumann577d4022018-01-31 16:55:10 +000051import android.util.Slog;
Todd Kennedy01ad0c72016-11-11 15:33:12 -080052
Todd Kennedy50d946c12017-03-17 13:55:38 -070053import com.android.internal.logging.MetricsLogger;
54import com.android.internal.logging.nano.MetricsProto;
Patrick Baumann43c97a02018-01-31 20:09:03 +000055import com.android.server.pm.InstantAppResolverConnection.ConnectionException;
56import com.android.server.pm.InstantAppResolverConnection.PhaseTwoCallback;
Todd Kennedy01ad0c72016-11-11 15:33:12 -080057
Todd Kennedybdf2a802017-05-08 16:09:42 -070058import java.lang.annotation.Retention;
59import java.lang.annotation.RetentionPolicy;
Patrick Baumann577d4022018-01-31 16:55:10 +000060import java.util.ArrayList;
Todd Kennedy01ad0c72016-11-11 15:33:12 -080061import java.util.Arrays;
Patrick Baumann577d4022018-01-31 16:55:10 +000062import java.util.Collections;
63import java.util.Iterator;
Todd Kennedy01ad0c72016-11-11 15:33:12 -080064import java.util.List;
Patrick Baumann577d4022018-01-31 16:55:10 +000065import java.util.Set;
Todd Kennedy01ad0c72016-11-11 15:33:12 -080066import java.util.UUID;
67
68/** @hide */
Todd Kennedy1fb34042017-03-01 13:56:58 -080069public abstract class InstantAppResolver {
Patrick Baumann43c97a02018-01-31 20:09:03 +000070 private static final boolean DEBUG_INSTANT = Build.IS_DEBUGGABLE;
Todd Kennedyd3c51062017-03-17 16:03:29 -070071 private static final String TAG = "PackageManager";
72
Todd Kennedybdf2a802017-05-08 16:09:42 -070073 private static final int RESOLUTION_SUCCESS = 0;
74 private static final int RESOLUTION_FAILURE = 1;
75 /** Binding to the external service timed out */
76 private static final int RESOLUTION_BIND_TIMEOUT = 2;
77 /** The call to retrieve an instant application response timed out */
78 private static final int RESOLUTION_CALL_TIMEOUT = 3;
79
80 @IntDef(flag = true, prefix = { "RESOLUTION_" }, value = {
81 RESOLUTION_SUCCESS,
82 RESOLUTION_FAILURE,
83 RESOLUTION_BIND_TIMEOUT,
84 RESOLUTION_CALL_TIMEOUT,
85 })
86 @Retention(RetentionPolicy.SOURCE)
87 public @interface ResolutionStatus {}
Todd Kennedy50d946c12017-03-17 13:55:38 -070088
89 private static MetricsLogger sMetricsLogger;
Patrick Baumann577d4022018-01-31 16:55:10 +000090
Todd Kennedy50d946c12017-03-17 13:55:38 -070091 private static MetricsLogger getLogger() {
92 if (sMetricsLogger == null) {
93 sMetricsLogger = new MetricsLogger();
94 }
95 return sMetricsLogger;
96 }
97
Patrick Baumann577d4022018-01-31 16:55:10 +000098 /**
99 * Returns an intent with potential PII removed from the original intent. Fields removed
100 * include extras and the host + path of the data, if defined.
101 */
102 public static Intent sanitizeIntent(Intent origIntent) {
103 final Intent sanitizedIntent;
104 sanitizedIntent = new Intent(origIntent.getAction());
105 Set<String> categories = origIntent.getCategories();
106 if (categories != null) {
107 for (String category : categories) {
108 sanitizedIntent.addCategory(category);
109 }
110 }
111 Uri sanitizedUri = origIntent.getData() == null
112 ? null
113 : Uri.fromParts(origIntent.getScheme(), "", "");
114 sanitizedIntent.setDataAndType(sanitizedUri, origIntent.getType());
115 sanitizedIntent.addFlags(origIntent.getFlags());
116 sanitizedIntent.setPackage(origIntent.getPackage());
117 return sanitizedIntent;
118 }
119
120 public static AuxiliaryResolveInfo doInstantAppResolutionPhaseOne(
Patrick Baumann43c97a02018-01-31 20:09:03 +0000121 InstantAppResolverConnection connection, InstantAppRequest requestObj) {
Todd Kennedy50d946c12017-03-17 13:55:38 -0700122 final long startTime = System.currentTimeMillis();
123 final String token = UUID.randomUUID().toString();
Patrick Baumann43c97a02018-01-31 20:09:03 +0000124 if (DEBUG_INSTANT) {
Todd Kennedybdf2a802017-05-08 16:09:42 -0700125 Log.d(TAG, "[" + token + "] Phase1; resolving");
Todd Kennedy46b4f2b2017-04-21 12:20:03 -0700126 }
Patrick Baumann577d4022018-01-31 16:55:10 +0000127 final Intent origIntent = requestObj.origIntent;
128 final Intent sanitizedIntent = sanitizeIntent(origIntent);
129
Todd Kennedybdf2a802017-05-08 16:09:42 -0700130 AuxiliaryResolveInfo resolveInfo = null;
131 @ResolutionStatus int resolutionStatus = RESOLUTION_SUCCESS;
132 try {
133 final List<InstantAppResolveInfo> instantAppResolveInfoList =
Patrick Baumann4db6bc12018-02-06 09:55:36 -0800134 connection.getInstantAppResolveInfoList(sanitizedIntent,
Hai Zhangbfa4f902018-08-21 11:33:33 -0700135 requestObj.digest.getDigestPrefixSecure(), requestObj.userId, token);
Todd Kennedybdf2a802017-05-08 16:09:42 -0700136 if (instantAppResolveInfoList != null && instantAppResolveInfoList.size() > 0) {
137 resolveInfo = InstantAppResolver.filterInstantAppIntent(
Patrick Baumann577d4022018-01-31 16:55:10 +0000138 instantAppResolveInfoList, origIntent, requestObj.resolvedType,
Patrick Baumann4db6bc12018-02-06 09:55:36 -0800139 requestObj.userId, origIntent.getPackage(), requestObj.digest, token);
Todd Kennedyd3c51062017-03-17 16:03:29 -0700140 }
Todd Kennedybdf2a802017-05-08 16:09:42 -0700141 } catch (ConnectionException e) {
142 if (e.failure == ConnectionException.FAILURE_BIND) {
143 resolutionStatus = RESOLUTION_BIND_TIMEOUT;
144 } else if (e.failure == ConnectionException.FAILURE_CALL) {
145 resolutionStatus = RESOLUTION_CALL_TIMEOUT;
146 } else {
147 resolutionStatus = RESOLUTION_FAILURE;
148 }
Todd Kennedy01ad0c72016-11-11 15:33:12 -0800149 }
Todd Kennedyf4c38552017-06-12 10:33:32 -0700150 // Only log successful instant application resolution
Todd Kennedy6ebabca2017-08-22 10:02:12 -0700151 if (requestObj.resolveForStart && resolutionStatus == RESOLUTION_SUCCESS) {
Todd Kennedyf4c38552017-06-12 10:33:32 -0700152 logMetrics(ACTION_INSTANT_APP_RESOLUTION_PHASE_ONE, startTime, token,
153 resolutionStatus);
154 }
Patrick Baumann43c97a02018-01-31 20:09:03 +0000155 if (DEBUG_INSTANT && resolveInfo == null) {
Todd Kennedybdf2a802017-05-08 16:09:42 -0700156 if (resolutionStatus == RESOLUTION_BIND_TIMEOUT) {
157 Log.d(TAG, "[" + token + "] Phase1; bind timed out");
158 } else if (resolutionStatus == RESOLUTION_CALL_TIMEOUT) {
159 Log.d(TAG, "[" + token + "] Phase1; call timed out");
160 } else if (resolutionStatus != RESOLUTION_SUCCESS) {
161 Log.d(TAG, "[" + token + "] Phase1; service connection error");
162 } else {
163 Log.d(TAG, "[" + token + "] Phase1; No results matched");
164 }
Todd Kennedy46b4f2b2017-04-21 12:20:03 -0700165 }
Patrick Baumanna6862c22018-05-03 13:16:01 -0700166 // if the match external flag is set, return an empty resolve info instead of a null result.
167 if (resolveInfo == null && (origIntent.getFlags() & FLAG_ACTIVITY_MATCH_EXTERNAL) != 0) {
168 return new AuxiliaryResolveInfo(token, false, createFailureIntent(origIntent, token),
169 null /* filters */);
170 }
Todd Kennedy50d946c12017-03-17 13:55:38 -0700171 return resolveInfo;
Todd Kennedy01ad0c72016-11-11 15:33:12 -0800172 }
173
Todd Kennedy1fb34042017-03-01 13:56:58 -0800174 public static void doInstantAppResolutionPhaseTwo(Context context,
Patrick Baumann43c97a02018-01-31 20:09:03 +0000175 InstantAppResolverConnection connection, InstantAppRequest requestObj,
Todd Kennedy1fb34042017-03-01 13:56:58 -0800176 ActivityInfo instantAppInstaller, Handler callbackHandler) {
Todd Kennedy50d946c12017-03-17 13:55:38 -0700177 final long startTime = System.currentTimeMillis();
Todd Kennedy46b4f2b2017-04-21 12:20:03 -0700178 final String token = requestObj.responseObj.token;
Patrick Baumann43c97a02018-01-31 20:09:03 +0000179 if (DEBUG_INSTANT) {
Todd Kennedybdf2a802017-05-08 16:09:42 -0700180 Log.d(TAG, "[" + token + "] Phase2; resolving");
Todd Kennedy46b4f2b2017-04-21 12:20:03 -0700181 }
Patrick Baumann577d4022018-01-31 16:55:10 +0000182 final Intent origIntent = requestObj.origIntent;
183 final Intent sanitizedIntent = sanitizeIntent(origIntent);
Todd Kennedy01ad0c72016-11-11 15:33:12 -0800184
185 final PhaseTwoCallback callback = new PhaseTwoCallback() {
186 @Override
Todd Kennedy1fb34042017-03-01 13:56:58 -0800187 void onPhaseTwoResolved(List<InstantAppResolveInfo> instantAppResolveInfoList,
Todd Kennedy50d946c12017-03-17 13:55:38 -0700188 long startTime) {
Todd Kennedy7dd99e32017-05-03 16:12:49 -0700189 final Intent failureIntent;
Todd Kennedy1fb34042017-03-01 13:56:58 -0800190 if (instantAppResolveInfoList != null && instantAppResolveInfoList.size() > 0) {
191 final AuxiliaryResolveInfo instantAppIntentInfo =
192 InstantAppResolver.filterInstantAppIntent(
Patrick Baumann577d4022018-01-31 16:55:10 +0000193 instantAppResolveInfoList, origIntent, null /*resolvedType*/,
Patrick Baumann4db6bc12018-02-06 09:55:36 -0800194 0 /*userId*/, origIntent.getPackage(), requestObj.digest,
195 token);
Patrick Baumann577d4022018-01-31 16:55:10 +0000196 if (instantAppIntentInfo != null) {
Todd Kennedy7dd99e32017-05-03 16:12:49 -0700197 failureIntent = instantAppIntentInfo.failureIntent;
Todd Kennedy01ad0c72016-11-11 15:33:12 -0800198 } else {
Todd Kennedy7dd99e32017-05-03 16:12:49 -0700199 failureIntent = null;
Todd Kennedy01ad0c72016-11-11 15:33:12 -0800200 }
201 } else {
Todd Kennedy7dd99e32017-05-03 16:12:49 -0700202 failureIntent = null;
Todd Kennedy01ad0c72016-11-11 15:33:12 -0800203 }
204 final Intent installerIntent = buildEphemeralInstallerIntent(
Todd Kennedy01ad0c72016-11-11 15:33:12 -0800205 requestObj.origIntent,
Patrick Baumann577d4022018-01-31 16:55:10 +0000206 sanitizedIntent,
Todd Kennedy7dd99e32017-05-03 16:12:49 -0700207 failureIntent,
Todd Kennedy01ad0c72016-11-11 15:33:12 -0800208 requestObj.callingPackage,
Chad Brubaker06068612017-04-06 09:43:47 -0700209 requestObj.verificationBundle,
Todd Kennedy01ad0c72016-11-11 15:33:12 -0800210 requestObj.resolvedType,
211 requestObj.userId,
Todd Kennedyd0084f72017-07-28 13:56:14 -0700212 requestObj.responseObj.installFailureActivity,
Todd Kennedy50d946c12017-03-17 13:55:38 -0700213 token,
Patrick Baumann577d4022018-01-31 16:55:10 +0000214 false /*needsPhaseTwo*/,
215 requestObj.responseObj.filters);
Todd Kennedy01ad0c72016-11-11 15:33:12 -0800216 installerIntent.setComponent(new ComponentName(
Todd Kennedy1fb34042017-03-01 13:56:58 -0800217 instantAppInstaller.packageName, instantAppInstaller.name));
Todd Kennedy50d946c12017-03-17 13:55:38 -0700218
219 logMetrics(ACTION_INSTANT_APP_RESOLUTION_PHASE_TWO, startTime, token,
Patrick Baumann577d4022018-01-31 16:55:10 +0000220 requestObj.responseObj.filters != null ? RESOLUTION_SUCCESS : RESOLUTION_FAILURE);
Todd Kennedy50d946c12017-03-17 13:55:38 -0700221
Todd Kennedy01ad0c72016-11-11 15:33:12 -0800222 context.startActivity(installerIntent);
223 }
224 };
Todd Kennedybdf2a802017-05-08 16:09:42 -0700225 try {
Patrick Baumann4db6bc12018-02-06 09:55:36 -0800226 connection.getInstantAppIntentFilterList(sanitizedIntent,
Hai Zhangbfa4f902018-08-21 11:33:33 -0700227 requestObj.digest.getDigestPrefixSecure(), requestObj.userId, token, callback,
228 callbackHandler, startTime);
Todd Kennedybdf2a802017-05-08 16:09:42 -0700229 } catch (ConnectionException e) {
230 @ResolutionStatus int resolutionStatus = RESOLUTION_FAILURE;
231 if (e.failure == ConnectionException.FAILURE_BIND) {
232 resolutionStatus = RESOLUTION_BIND_TIMEOUT;
233 }
234 logMetrics(ACTION_INSTANT_APP_RESOLUTION_PHASE_TWO, startTime, token,
235 resolutionStatus);
Patrick Baumann43c97a02018-01-31 20:09:03 +0000236 if (DEBUG_INSTANT) {
Todd Kennedybdf2a802017-05-08 16:09:42 -0700237 if (resolutionStatus == RESOLUTION_BIND_TIMEOUT) {
238 Log.d(TAG, "[" + token + "] Phase2; bind timed out");
239 } else {
240 Log.d(TAG, "[" + token + "] Phase2; service connection error");
241 }
242 }
243 }
Todd Kennedy01ad0c72016-11-11 15:33:12 -0800244 }
245
246 /**
Todd Kennedy1fb34042017-03-01 13:56:58 -0800247 * Builds and returns an intent to launch the instant installer.
Todd Kennedy01ad0c72016-11-11 15:33:12 -0800248 */
Todd Kennedy7dd99e32017-05-03 16:12:49 -0700249 public static Intent buildEphemeralInstallerIntent(
250 @NonNull Intent origIntent,
Patrick Baumann577d4022018-01-31 16:55:10 +0000251 @NonNull Intent sanitizedIntent,
252 @Nullable Intent failureIntent,
Todd Kennedye9910222017-02-21 16:00:11 -0800253 @NonNull String callingPackage,
Chad Brubaker06068612017-04-06 09:43:47 -0700254 @Nullable Bundle verificationBundle,
Todd Kennedye9910222017-02-21 16:00:11 -0800255 @NonNull String resolvedType,
256 int userId,
Todd Kennedyd0084f72017-07-28 13:56:14 -0700257 @Nullable ComponentName installFailureActivity,
Todd Kennedye9910222017-02-21 16:00:11 -0800258 @Nullable String token,
Patrick Baumann577d4022018-01-31 16:55:10 +0000259 boolean needsPhaseTwo,
260 List<AuxiliaryResolveInfo.AuxiliaryFilter> filters) {
Todd Kennedy1fb34042017-03-01 13:56:58 -0800261 // Construct the intent that launches the instant installer
Todd Kennedye9910222017-02-21 16:00:11 -0800262 int flags = origIntent.getFlags();
Patrick Baumann577d4022018-01-31 16:55:10 +0000263 final Intent intent = new Intent();
Todd Kennedy01ad0c72016-11-11 15:33:12 -0800264 intent.setFlags(flags
Todd Kennedy01ad0c72016-11-11 15:33:12 -0800265 | Intent.FLAG_ACTIVITY_NO_HISTORY
266 | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
Todd Kennedye9910222017-02-21 16:00:11 -0800267 if (token != null) {
Patrick Baumann159cd022018-01-11 13:25:05 -0800268 intent.putExtra(Intent.EXTRA_INSTANT_APP_TOKEN, token);
Todd Kennedye9910222017-02-21 16:00:11 -0800269 }
270 if (origIntent.getData() != null) {
Patrick Baumann159cd022018-01-11 13:25:05 -0800271 intent.putExtra(Intent.EXTRA_INSTANT_APP_HOSTNAME, origIntent.getData().getHost());
Todd Kennedye9910222017-02-21 16:00:11 -0800272 }
Patrick Baumannb4165d72017-12-04 11:29:24 -0800273 intent.putExtra(Intent.EXTRA_INSTANT_APP_ACTION, origIntent.getAction());
Patrick Baumann577d4022018-01-31 16:55:10 +0000274 intent.putExtra(Intent.EXTRA_INTENT, sanitizedIntent);
Todd Kennedy01ad0c72016-11-11 15:33:12 -0800275
Patrick Baumann577d4022018-01-31 16:55:10 +0000276 if (needsPhaseTwo) {
277 intent.setAction(Intent.ACTION_RESOLVE_INSTANT_APP_PACKAGE);
278 } else {
279 // We have all of the data we need; just start the installer without a second phase
Todd Kennedyd0084f72017-07-28 13:56:14 -0700280 if (failureIntent != null || installFailureActivity != null) {
Patrick Baumann577d4022018-01-31 16:55:10 +0000281 // Intent that is launched if the package couldn't be installed for any reason.
Todd Kennedy7dd99e32017-05-03 16:12:49 -0700282 try {
Todd Kennedyd0084f72017-07-28 13:56:14 -0700283 final Intent onFailureIntent;
284 if (installFailureActivity != null) {
285 onFailureIntent = new Intent();
286 onFailureIntent.setComponent(installFailureActivity);
Patrick Baumann577d4022018-01-31 16:55:10 +0000287 if (filters != null && filters.size() == 1) {
288 onFailureIntent.putExtra(Intent.EXTRA_SPLIT_NAME,
289 filters.get(0).splitName);
290 }
Todd Kennedyd0084f72017-07-28 13:56:14 -0700291 onFailureIntent.putExtra(Intent.EXTRA_INTENT, origIntent);
292 } else {
293 onFailureIntent = failureIntent;
294 }
Todd Kennedy7dd99e32017-05-03 16:12:49 -0700295 final IIntentSender failureIntentTarget = ActivityManager.getService()
296 .getIntentSender(
297 ActivityManager.INTENT_SENDER_ACTIVITY, callingPackage,
298 null /*token*/, null /*resultWho*/, 1 /*requestCode*/,
Todd Kennedyd0084f72017-07-28 13:56:14 -0700299 new Intent[] { onFailureIntent },
Todd Kennedy7dd99e32017-05-03 16:12:49 -0700300 new String[] { resolvedType },
Todd Kennedyd0084f72017-07-28 13:56:14 -0700301 PendingIntent.FLAG_CANCEL_CURRENT
302 | PendingIntent.FLAG_ONE_SHOT
Todd Kennedy7dd99e32017-05-03 16:12:49 -0700303 | PendingIntent.FLAG_IMMUTABLE,
304 null /*bOptions*/, userId);
Patrick Baumann159cd022018-01-11 13:25:05 -0800305 IntentSender failureSender = new IntentSender(failureIntentTarget);
306 // TODO(b/72700831): remove populating old extra
Patrick Baumann159cd022018-01-11 13:25:05 -0800307 intent.putExtra(Intent.EXTRA_INSTANT_APP_FAILURE, failureSender);
Todd Kennedy7dd99e32017-05-03 16:12:49 -0700308 } catch (RemoteException ignore) { /* ignore; same process */ }
309 }
Todd Kennedy01ad0c72016-11-11 15:33:12 -0800310
Todd Kennedye9910222017-02-21 16:00:11 -0800311 // Intent that is launched if the package was installed successfully.
312 final Intent successIntent = new Intent(origIntent);
Todd Kennedyb3b431302017-03-20 16:05:48 -0700313 successIntent.setLaunchToken(token);
Todd Kennedy01ad0c72016-11-11 15:33:12 -0800314 try {
Todd Kennedye9910222017-02-21 16:00:11 -0800315 final IIntentSender successIntentTarget = ActivityManager.getService()
Todd Kennedy01ad0c72016-11-11 15:33:12 -0800316 .getIntentSender(
317 ActivityManager.INTENT_SENDER_ACTIVITY, callingPackage,
318 null /*token*/, null /*resultWho*/, 0 /*requestCode*/,
Todd Kennedye9910222017-02-21 16:00:11 -0800319 new Intent[] { successIntent },
Todd Kennedy01ad0c72016-11-11 15:33:12 -0800320 new String[] { resolvedType },
321 PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT
322 | PendingIntent.FLAG_IMMUTABLE,
323 null /*bOptions*/, userId);
Patrick Baumann159cd022018-01-11 13:25:05 -0800324 IntentSender successSender = new IntentSender(successIntentTarget);
Patrick Baumann159cd022018-01-11 13:25:05 -0800325 intent.putExtra(Intent.EXTRA_INSTANT_APP_SUCCESS, successSender);
Todd Kennedy01ad0c72016-11-11 15:33:12 -0800326 } catch (RemoteException ignore) { /* ignore; same process */ }
Chad Brubaker06068612017-04-06 09:43:47 -0700327 if (verificationBundle != null) {
328 intent.putExtra(Intent.EXTRA_VERIFICATION_BUNDLE, verificationBundle);
329 }
Patrick Baumann577d4022018-01-31 16:55:10 +0000330 intent.putExtra(Intent.EXTRA_CALLING_PACKAGE, callingPackage);
Patrick Baumann860b8ba2018-01-31 01:33:50 +0000331
Patrick Baumann577d4022018-01-31 16:55:10 +0000332 if (filters != null) {
333 Bundle resolvableFilters[] = new Bundle[filters.size()];
334 for (int i = 0, max = filters.size(); i < max; i++) {
335 Bundle resolvableFilter = new Bundle();
336 AuxiliaryResolveInfo.AuxiliaryFilter filter = filters.get(i);
337 resolvableFilter.putBoolean(Intent.EXTRA_UNKNOWN_INSTANT_APP,
338 filter.resolveInfo != null
339 && filter.resolveInfo.shouldLetInstallerDecide());
340 resolvableFilter.putString(Intent.EXTRA_PACKAGE_NAME, filter.packageName);
341 resolvableFilter.putString(Intent.EXTRA_SPLIT_NAME, filter.splitName);
342 resolvableFilter.putLong(Intent.EXTRA_LONG_VERSION_CODE, filter.versionCode);
343 resolvableFilter.putBundle(Intent.EXTRA_INSTANT_APP_EXTRAS, filter.extras);
344 resolvableFilters[i] = resolvableFilter;
345 if (i == 0) {
346 // for backwards compat, always set the first result on the intent and add
347 // the int version code
348 intent.putExtras(resolvableFilter);
349 intent.putExtra(Intent.EXTRA_VERSION_CODE, (int) filter.versionCode);
350 }
351 }
352 intent.putExtra(Intent.EXTRA_INSTANT_APP_BUNDLES, resolvableFilters);
353 }
354 intent.setAction(Intent.ACTION_INSTALL_INSTANT_APP_PACKAGE);
355 }
Todd Kennedy01ad0c72016-11-11 15:33:12 -0800356 return intent;
357 }
358
Todd Kennedy1fb34042017-03-01 13:56:58 -0800359 private static AuxiliaryResolveInfo filterInstantAppIntent(
360 List<InstantAppResolveInfo> instantAppResolveInfoList,
Todd Kennedy7dd99e32017-05-03 16:12:49 -0700361 Intent origIntent, String resolvedType, int userId, String packageName,
Todd Kennedy1fb34042017-03-01 13:56:58 -0800362 InstantAppDigest digest, String token) {
Todd Kennedy01ad0c72016-11-11 15:33:12 -0800363 final int[] shaPrefix = digest.getDigestPrefix();
364 final byte[][] digestBytes = digest.getDigestBytes();
Patrick Baumann577d4022018-01-31 16:55:10 +0000365 boolean requiresSecondPhase = false;
Patrick Baumann577d4022018-01-31 16:55:10 +0000366 ArrayList<AuxiliaryResolveInfo.AuxiliaryFilter> filters = null;
Patrick Baumanna6862c22018-05-03 13:16:01 -0700367 boolean requiresPrefixMatch = origIntent.isWebIntent() || (shaPrefix.length > 0
368 && (origIntent.getFlags() & Intent.FLAG_ACTIVITY_MATCH_EXTERNAL) == 0);
Patrick Baumann577d4022018-01-31 16:55:10 +0000369 for (InstantAppResolveInfo instantAppResolveInfo : instantAppResolveInfoList) {
Patrick Baumanna6862c22018-05-03 13:16:01 -0700370 if (requiresPrefixMatch && instantAppResolveInfo.shouldLetInstallerDecide()) {
371 Slog.d(TAG, "InstantAppResolveInfo with mShouldLetInstallerDecide=true when digest"
372 + " required; ignoring");
Patrick Baumann577d4022018-01-31 16:55:10 +0000373 continue;
374 }
375 byte[] filterDigestBytes = instantAppResolveInfo.getDigestBytes();
376 // Only include matching digests if we have a prefix and we're either dealing with a
Patrick Baumanna6862c22018-05-03 13:16:01 -0700377 // prefixed request or the resolveInfo specifies digest details.
378 if (shaPrefix.length > 0 && (requiresPrefixMatch || filterDigestBytes.length > 0)) {
Patrick Baumann577d4022018-01-31 16:55:10 +0000379 boolean matchFound = false;
380 // Go in reverse order so we match the narrowest scope first.
381 for (int i = shaPrefix.length - 1; i >= 0; --i) {
382 if (Arrays.equals(digestBytes[i], filterDigestBytes)) {
383 matchFound = true;
384 break;
385 }
386 }
387 if (!matchFound) {
Patrick Baumann860b8ba2018-01-31 01:33:50 +0000388 continue;
389 }
Patrick Baumann577d4022018-01-31 16:55:10 +0000390 }
391 // We matched a resolve info; resolve the filters to see if anything matches completely.
392 List<AuxiliaryResolveInfo.AuxiliaryFilter> matchFilters = computeResolveFilters(
393 origIntent, resolvedType, userId, packageName, token, instantAppResolveInfo);
394 if (matchFilters != null) {
395 if (matchFilters.isEmpty()) {
396 requiresSecondPhase = true;
Patrick Baumann860b8ba2018-01-31 01:33:50 +0000397 }
Patrick Baumann577d4022018-01-31 16:55:10 +0000398 if (filters == null) {
399 filters = new ArrayList<>(matchFilters);
400 } else {
401 filters.addAll(matchFilters);
Todd Kennedy01ad0c72016-11-11 15:33:12 -0800402 }
Patrick Baumann3e8bd0f2018-01-08 11:23:38 -0800403 }
Patrick Baumann3e8bd0f2018-01-08 11:23:38 -0800404 }
Patrick Baumann577d4022018-01-31 16:55:10 +0000405 if (filters != null && !filters.isEmpty()) {
Patrick Baumanna6862c22018-05-03 13:16:01 -0700406 return new AuxiliaryResolveInfo(token, requiresSecondPhase,
407 createFailureIntent(origIntent, token), filters);
Patrick Baumann3e0b8ee2018-04-26 12:03:15 -0700408 }
Todd Kennedy1fb34042017-03-01 13:56:58 -0800409 // Hash or filter mis-match; no instant apps for this domain.
Todd Kennedy01ad0c72016-11-11 15:33:12 -0800410 return null;
411 }
Todd Kennedy50d946c12017-03-17 13:55:38 -0700412
Patrick Baumann577d4022018-01-31 16:55:10 +0000413 /**
Patrick Baumanna6862c22018-05-03 13:16:01 -0700414 * Creates a failure intent for the installer to send in the case that the instant app cannot be
415 * launched for any reason.
416 */
417 private static Intent createFailureIntent(Intent origIntent, String token) {
418 final Intent failureIntent = new Intent(origIntent);
419 failureIntent.setFlags(failureIntent.getFlags() | Intent.FLAG_IGNORE_EPHEMERAL);
420 failureIntent.setFlags(failureIntent.getFlags() & ~Intent.FLAG_ACTIVITY_MATCH_EXTERNAL);
421 failureIntent.setLaunchToken(token);
422 return failureIntent;
423 }
424
425 /**
Patrick Baumann577d4022018-01-31 16:55:10 +0000426 * Returns one of three states: <p/>
427 * <ul>
428 * <li>{@code null} if there are no matches will not be; resolution is unnecessary.</li>
429 * <li>An empty list signifying that a 2nd phase of resolution is required.</li>
430 * <li>A populated list meaning that matches were found and should be sent directly to the
431 * installer</li>
432 * </ul>
433 *
434 */
435 private static List<AuxiliaryResolveInfo.AuxiliaryFilter> computeResolveFilters(
436 Intent origIntent, String resolvedType, int userId, String packageName, String token,
437 InstantAppResolveInfo instantAppInfo) {
438 if (instantAppInfo.shouldLetInstallerDecide()) {
439 return Collections.singletonList(
440 new AuxiliaryResolveInfo.AuxiliaryFilter(
441 instantAppInfo, null /* splitName */,
442 instantAppInfo.getExtras()));
443 }
444 if (packageName != null
445 && !packageName.equals(instantAppInfo.getPackageName())) {
446 return null;
447 }
448 final List<InstantAppIntentFilter> instantAppFilters =
449 instantAppInfo.getIntentFilters();
450 if (instantAppFilters == null || instantAppFilters.isEmpty()) {
451 // No filters on web intent; no matches, 2nd phase unnecessary.
Patrick Baumann0da85372018-02-02 16:07:35 -0800452 if (origIntent.isWebIntent()) {
Patrick Baumann577d4022018-01-31 16:55:10 +0000453 return null;
454 }
455 // No filters; we need to start phase two
Patrick Baumann43c97a02018-01-31 20:09:03 +0000456 if (DEBUG_INSTANT) {
Patrick Baumann577d4022018-01-31 16:55:10 +0000457 Log.d(TAG, "No app filters; go to phase 2");
458 }
459 return Collections.emptyList();
460 }
Todd Kennedycf827032018-07-03 13:17:22 -0700461 final ComponentResolver.InstantAppIntentResolver instantAppResolver =
462 new ComponentResolver.InstantAppIntentResolver();
Patrick Baumann577d4022018-01-31 16:55:10 +0000463 for (int j = instantAppFilters.size() - 1; j >= 0; --j) {
464 final InstantAppIntentFilter instantAppFilter = instantAppFilters.get(j);
465 final List<IntentFilter> splitFilters = instantAppFilter.getFilters();
466 if (splitFilters == null || splitFilters.isEmpty()) {
467 continue;
468 }
469 for (int k = splitFilters.size() - 1; k >= 0; --k) {
470 IntentFilter filter = splitFilters.get(k);
471 Iterator<IntentFilter.AuthorityEntry> authorities =
472 filter.authoritiesIterator();
473 // ignore http/s-only filters.
474 if ((authorities == null || !authorities.hasNext())
475 && (filter.hasDataScheme("http") || filter.hasDataScheme("https"))
476 && filter.hasAction(Intent.ACTION_VIEW)
477 && filter.hasCategory(Intent.CATEGORY_BROWSABLE)) {
478 continue;
479 }
480 instantAppResolver.addFilter(
481 new AuxiliaryResolveInfo.AuxiliaryFilter(
482 filter,
483 instantAppInfo,
484 instantAppFilter.getSplitName(),
485 instantAppInfo.getExtras()
486 ));
487 }
488 }
489 List<AuxiliaryResolveInfo.AuxiliaryFilter> matchedResolveInfoList =
490 instantAppResolver.queryIntent(
491 origIntent, resolvedType, false /*defaultOnly*/, userId);
492 if (!matchedResolveInfoList.isEmpty()) {
Patrick Baumann43c97a02018-01-31 20:09:03 +0000493 if (DEBUG_INSTANT) {
Patrick Baumann577d4022018-01-31 16:55:10 +0000494 Log.d(TAG, "[" + token + "] Found match(es); " + matchedResolveInfoList);
495 }
496 return matchedResolveInfoList;
Patrick Baumann43c97a02018-01-31 20:09:03 +0000497 } else if (DEBUG_INSTANT) {
Patrick Baumann577d4022018-01-31 16:55:10 +0000498 Log.d(TAG, "[" + token + "] No matches found"
499 + " package: " + instantAppInfo.getPackageName()
500 + ", versionCode: " + instantAppInfo.getVersionCode());
501 }
502 return null;
503 }
504
Todd Kennedybdf2a802017-05-08 16:09:42 -0700505 private static void logMetrics(int action, long startTime, String token,
506 @ResolutionStatus int status) {
Todd Kennedy50d946c12017-03-17 13:55:38 -0700507 final LogMaker logMaker = new LogMaker(action)
508 .setType(MetricsProto.MetricsEvent.TYPE_ACTION)
509 .addTaggedData(FIELD_INSTANT_APP_RESOLUTION_DELAY_MS,
510 new Long(System.currentTimeMillis() - startTime))
511 .addTaggedData(FIELD_INSTANT_APP_LAUNCH_TOKEN, token)
512 .addTaggedData(FIELD_INSTANT_APP_RESOLUTION_STATUS, new Integer(status));
513 getLogger().write(logMaker);
514 }
Todd Kennedy01ad0c72016-11-11 15:33:12 -0800515}