summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Todd Kennedy <toddke@google.com> 2016-11-16 23:05:30 +0000
committer Android (Google) Code Review <android-gerrit@google.com> 2016-11-16 23:05:34 +0000
commit3e12f413d40e55da3d8a964740bc2aa9899c622b (patch)
tree5e94ca6467b84ded8651af50e2312a5901a554b4
parent2eb0a8b4d5f1e9c2d569c3cf07d450ebc1ab41e5 (diff)
parent01ad0c7e403794b272494f187d91f57bdfa07c9d (diff)
Merge "Implement 2-phase resolution"
-rw-r--r--core/java/android/content/Intent.java12
-rw-r--r--core/java/android/content/pm/EphemeralRequest.java48
-rw-r--r--core/java/android/content/pm/EphemeralResolveInfo.java22
-rw-r--r--core/java/android/content/pm/EphemeralResponse.java51
-rw-r--r--core/java/android/content/pm/PackageManagerInternal.java13
-rw-r--r--core/java/android/content/pm/ResolveInfo.java3
-rw-r--r--services/core/java/com/android/server/am/ActivityStarter.java78
-rw-r--r--services/core/java/com/android/server/pm/EphemeralResolver.java249
-rw-r--r--services/core/java/com/android/server/pm/EphemeralResolverConnection.java66
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java116
10 files changed, 459 insertions, 199 deletions
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index a9e987a45b2b..c87de9ade034 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -3835,6 +3835,18 @@ public class Intent implements Parcelable, Cloneable {
public static final String EXTRA_EPHEMERAL_FAILURE = "android.intent.extra.EPHEMERAL_FAILURE";
/**
+ * The host name that triggered an ephemeral resolution.
+ * @hide
+ */
+ public static final String EXTRA_EPHEMERAL_HOSTNAME = "android.intent.extra.EPHEMERAL_HOSTNAME";
+
+ /**
+ * An opaque token to track ephemeral resolution.
+ * @hide
+ */
+ public static final String EXTRA_EPHEMERAL_TOKEN = "android.intent.extra.EPHEMERAL_TOKEN";
+
+ /**
* A Bundle forming a mapping of potential target package names to different extras Bundles
* to add to the default intent extras in {@link #EXTRA_INTENT} when used with
* {@link #ACTION_CHOOSER}. Each key should be a package name. The package need not
diff --git a/core/java/android/content/pm/EphemeralRequest.java b/core/java/android/content/pm/EphemeralRequest.java
new file mode 100644
index 000000000000..7f2b3ee1245b
--- /dev/null
+++ b/core/java/android/content/pm/EphemeralRequest.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm;
+
+import android.content.Intent;
+
+/**
+ * Information needed to make an ephemeral application resolution request.
+ * @hide
+ */
+public final class EphemeralRequest {
+ /** Response from the first phase of ephemeral application resolution */
+ public final EphemeralResponse responseObj;
+ /** The original intent that triggered ephemeral application resolution */
+ public final Intent origIntent;
+ /** Resolved type of the intent */
+ public final String resolvedType;
+ /** The intent that would launch if there were no ephemeral applications */
+ public final Intent launchIntent;
+ /** The name of the package requesting the ephemeral application */
+ public final String callingPackage;
+ /** ID of the user requesting the ephemeral application */
+ public final int userId;
+
+ public EphemeralRequest(EphemeralResponse responseObj, Intent origIntent,
+ String resolvedType, Intent launchIntent, String callingPackage, int userId) {
+ this.responseObj = responseObj;
+ this.origIntent = origIntent;
+ this.resolvedType = resolvedType;
+ this.launchIntent = launchIntent;
+ this.callingPackage = callingPackage;
+ this.userId = userId;
+ }
+} \ No newline at end of file
diff --git a/core/java/android/content/pm/EphemeralResolveInfo.java b/core/java/android/content/pm/EphemeralResolveInfo.java
index 3bed06b4816f..f6200886cd71 100644
--- a/core/java/android/content/pm/EphemeralResolveInfo.java
+++ b/core/java/android/content/pm/EphemeralResolveInfo.java
@@ -137,28 +137,6 @@ public final class EphemeralResolveInfo implements Parcelable {
}
};
- /** @hide */
- public static final class EphemeralResolveIntentInfo extends IntentFilter {
- private final EphemeralResolveInfo mResolveInfo;
- private final String mSplitName;
-
- public EphemeralResolveIntentInfo(@NonNull IntentFilter orig,
- @NonNull EphemeralResolveInfo resolveInfo,
- @Nullable String splitName) {
- super(orig);
- mResolveInfo = resolveInfo;
- mSplitName = splitName;
- }
-
- public EphemeralResolveInfo getEphemeralResolveInfo() {
- return mResolveInfo;
- }
-
- public String getSplitName() {
- return mSplitName;
- }
- }
-
/**
* Helper class to generate and store each of the digests and prefixes
* sent to the Ephemeral Resolver.
diff --git a/core/java/android/content/pm/EphemeralResponse.java b/core/java/android/content/pm/EphemeralResponse.java
new file mode 100644
index 000000000000..6e569f7d4667
--- /dev/null
+++ b/core/java/android/content/pm/EphemeralResponse.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.IntentFilter;
+
+/**
+ * Ephemeral application resolution response.
+ * @hide
+ */
+public final class EphemeralResponse extends IntentFilter {
+ /** Resolved information returned from the external ephemeral resolver */
+ public final EphemeralResolveInfo resolveInfo;
+ /** The resolved package. Copied from {@link #resolveInfo}. */
+ public final String packageName;
+ /** The resolve split. Copied from the matched filter in {@link #resolveInfo}. */
+ public final String splitName;
+ /** Whether or not ephemeral resolution needs the second phase */
+ public final boolean needsPhase2;
+ /** Opaque token to track the ephemeral application resolution */
+ public final String token;
+
+ public EphemeralResponse(@NonNull EphemeralResolveInfo resolveInfo,
+ @NonNull IntentFilter orig,
+ @Nullable String splitName,
+ @NonNull String token,
+ boolean needsPhase2) {
+ super(orig);
+ this.resolveInfo = resolveInfo;
+ this.packageName = resolveInfo.getPackageName();
+ this.splitName = splitName;
+ this.token = token;
+ this.needsPhase2 = needsPhase2;
+ }
+} \ No newline at end of file
diff --git a/core/java/android/content/pm/PackageManagerInternal.java b/core/java/android/content/pm/PackageManagerInternal.java
index 2aa7ac6ff5b6..ad0a6b25c4f1 100644
--- a/core/java/android/content/pm/PackageManagerInternal.java
+++ b/core/java/android/content/pm/PackageManagerInternal.java
@@ -17,6 +17,7 @@
package android.content.pm;
import android.content.ComponentName;
+import android.content.Intent;
import android.content.pm.PackageManager.NameNotFoundException;
import android.util.SparseArray;
@@ -208,4 +209,16 @@ public abstract class PackageManagerInternal {
*/
public abstract String getNameForUid(int uid);
+ /**
+ * Request to perform the second phase of ephemeral resolution.
+ * @param responseObj The response of the first phase of ephemeral resolution
+ * @param origIntent The original intent that triggered ephemeral resolution
+ * @param resolvedType The resolved type of the intent
+ * @param launchIntent The intent that would launch if there was no ephemeral application
+ * @param callingPackage The name of the package requesting the ephemeral application
+ * @param userId The ID of the user that triggered ephemeral resolution
+ */
+ public abstract void requestEphemeralResolutionPhaseTwo(EphemeralResponse responseObj,
+ Intent origIntent, String resolvedType, Intent launchIntent, String callingPackage,
+ int userId);
}
diff --git a/core/java/android/content/pm/ResolveInfo.java b/core/java/android/content/pm/ResolveInfo.java
index 86dbe8a64ed8..f8b4570be6cc 100644
--- a/core/java/android/content/pm/ResolveInfo.java
+++ b/core/java/android/content/pm/ResolveInfo.java
@@ -18,7 +18,6 @@ package android.content.pm;
import android.content.ComponentName;
import android.content.IntentFilter;
-import android.content.pm.EphemeralResolveInfo.EphemeralResolveIntentInfo;
import android.graphics.drawable.Drawable;
import android.os.Parcel;
import android.os.Parcelable;
@@ -66,7 +65,7 @@ public class ResolveInfo implements Parcelable {
* only be set in specific circumstances.
* @hide
*/
- public EphemeralResolveIntentInfo ephemeralIntentInfo;
+ public EphemeralResponse ephemeralResponse;
/**
* The IntentFilter that was matched for this ResolveInfo.
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index 709c3d0dba5e..ccd94cb475a3 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -118,6 +118,7 @@ import android.view.Display;
import com.android.internal.app.HeavyWeightSwitcherActivity;
import com.android.internal.app.IVoiceInteractor;
import com.android.server.am.ActivityStackSupervisor.PendingActivityLaunch;
+import com.android.server.pm.EphemeralResolver;
import com.android.server.wm.WindowManagerService;
import java.util.ArrayList;
@@ -135,9 +136,6 @@ class ActivityStarter {
private static final String TAG_CONFIGURATION = TAG + POSTFIX_CONFIGURATION;
private static final String TAG_USER_LEAVING = TAG + POSTFIX_USER_LEAVING;
- // TODO b/30204367 remove when the platform fully supports ephemeral applications
- private static final boolean USE_DEFAULT_EPHEMERAL_LAUNCHER = false;
-
private final ActivityManagerService mService;
private final ActivityStackSupervisor mSupervisor;
private ActivityStartInterceptor mInterceptor;
@@ -457,13 +455,21 @@ class ActivityStarter {
// Instead, launch the ephemeral installer. Once the installer is finished, it
// starts either the intent we resolved here [on install error] or the ephemeral
// app [on install success].
- if (rInfo != null && rInfo.ephemeralIntentInfo != null) {
+ if (rInfo != null && rInfo.ephemeralResponse != null) {
final String packageName =
- rInfo.ephemeralIntentInfo.getEphemeralResolveInfo().getPackageName();
- final String splitName = rInfo.ephemeralIntentInfo.getSplitName();
- intent = buildEphemeralInstallerIntent(intent, ephemeralIntent,
- packageName, splitName, callingPackage, resolvedType,
- userId);
+ rInfo.ephemeralResponse.resolveInfo.getPackageName();
+ final String splitName = rInfo.ephemeralResponse.splitName;
+ final boolean needsPhaseTwo = rInfo.ephemeralResponse.needsPhase2;
+ final String token = rInfo.ephemeralResponse.token;
+ if (needsPhaseTwo) {
+ // request phase two resolution
+ mService.getPackageManagerInternalLocked().requestEphemeralResolutionPhaseTwo(
+ rInfo.ephemeralResponse, ephemeralIntent, resolvedType, intent,
+ callingPackage, userId);
+ }
+ intent = EphemeralResolver.buildEphemeralInstallerIntent(intent, ephemeralIntent,
+ callingPackage, resolvedType, userId, packageName, splitName, token,
+ needsPhaseTwo);
resolvedType = null;
callingUid = realCallingUid;
callingPid = realCallingPid;
@@ -522,60 +528,6 @@ class ActivityStarter {
return err;
}
- /**
- * Builds and returns an intent to launch the ephemeral installer.
- */
- private Intent buildEphemeralInstallerIntent(Intent launchIntent, Intent origIntent,
- String ephemeralPackageName, String ephemeralSplitName, String callingPackage,
- String resolvedType, int userId) {
- final Intent nonEphemeralIntent = new Intent(origIntent);
- nonEphemeralIntent.setFlags(nonEphemeralIntent.getFlags() | Intent.FLAG_IGNORE_EPHEMERAL);
- // Intent that is launched if the ephemeral package couldn't be installed
- // for any reason.
- final IIntentSender failureIntentTarget = mService.getIntentSenderLocked(
- ActivityManager.INTENT_SENDER_ACTIVITY, callingPackage,
- Binder.getCallingUid(), userId, null /*token*/, null /*resultWho*/, 1,
- new Intent[]{ nonEphemeralIntent }, new String[]{ resolvedType },
- PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT
- | PendingIntent.FLAG_IMMUTABLE, null /*bOptions*/);
-
- final Intent ephemeralIntent;
- if (USE_DEFAULT_EPHEMERAL_LAUNCHER) {
- // Force the intent to be directed to the ephemeral package
- ephemeralIntent = new Intent(origIntent);
- ephemeralIntent.setPackage(ephemeralPackageName);
- } else {
- // Success intent goes back to the installer
- ephemeralIntent = new Intent(launchIntent);
- }
-
- // Intent that is eventually launched if the ephemeral package was
- // installed successfully. This will actually be launched by a platform
- // broadcast receiver.
- final IIntentSender successIntentTarget = mService.getIntentSenderLocked(
- ActivityManager.INTENT_SENDER_ACTIVITY, callingPackage,
- Binder.getCallingUid(), userId, null /*token*/, null /*resultWho*/, 0,
- new Intent[]{ ephemeralIntent }, new String[]{ resolvedType },
- PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT
- | PendingIntent.FLAG_IMMUTABLE, null /*bOptions*/);
-
- // Finally build the actual intent to launch the ephemeral installer
- int flags = launchIntent.getFlags();
- final Intent intent = new Intent();
- intent.setFlags(flags
- | Intent.FLAG_ACTIVITY_NEW_TASK
- | Intent.FLAG_ACTIVITY_CLEAR_TASK
- | Intent.FLAG_ACTIVITY_NO_HISTORY
- | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
- intent.putExtra(Intent.EXTRA_PACKAGE_NAME, ephemeralPackageName);
- intent.putExtra(Intent.EXTRA_SPLIT_NAME, ephemeralSplitName);
- intent.putExtra(Intent.EXTRA_EPHEMERAL_FAILURE, new IntentSender(failureIntentTarget));
- intent.putExtra(Intent.EXTRA_EPHEMERAL_SUCCESS, new IntentSender(successIntentTarget));
- // TODO: Remove when the platform has fully implemented ephemeral apps
- intent.setData(origIntent.getData().buildUpon().clearQuery().build());
- return intent;
- }
-
void postStartActivityUncheckedProcessing(
ActivityRecord r, int result, int prevFocusedStackId, ActivityRecord sourceRecord,
ActivityStack targetStack) {
diff --git a/services/core/java/com/android/server/pm/EphemeralResolver.java b/services/core/java/com/android/server/pm/EphemeralResolver.java
new file mode 100644
index 000000000000..82f796daceb0
--- /dev/null
+++ b/services/core/java/com/android/server/pm/EphemeralResolver.java
@@ -0,0 +1,249 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import android.app.ActivityManager;
+import android.app.ActivityManagerNative;
+import android.app.PendingIntent;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.IIntentSender;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.IntentSender;
+import android.content.pm.ActivityInfo;
+import android.content.pm.EphemeralIntentFilter;
+import android.content.pm.EphemeralRequest;
+import android.content.pm.EphemeralResolveInfo;
+import android.content.pm.EphemeralResponse;
+import android.content.pm.EphemeralResolveInfo.EphemeralDigest;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.util.Log;
+import android.util.Slog;
+
+import com.android.server.pm.EphemeralResolverConnection.PhaseTwoCallback;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.UUID;
+
+/** @hide */
+public abstract class EphemeralResolver {
+
+ /** TODO b/30204367 remove when the platform fully supports ephemeral applications */
+ public static final boolean USE_DEFAULT_EPHEMERAL_LAUNCHER = false;
+
+ public static EphemeralResponse doEphemeralResolutionPhaseOne(Context context,
+ EphemeralResolverConnection connection, EphemeralRequest requestObj) {
+ final Intent intent = requestObj.origIntent;
+ final EphemeralDigest digest =
+ new EphemeralDigest(intent.getData().getHost(), 5 /*maxDigests*/);
+ final int[] shaPrefix = digest.getDigestPrefix();
+ final List<EphemeralResolveInfo> ephemeralResolveInfoList =
+ connection.getEphemeralResolveInfoList(shaPrefix);
+ if (ephemeralResolveInfoList == null || ephemeralResolveInfoList.size() == 0) {
+ // No hash prefix match; there are no ephemeral apps for this domain.
+ return null;
+ }
+
+ final String token = UUID.randomUUID().toString();
+ return EphemeralResolver.filterEphemeralIntent(ephemeralResolveInfoList,
+ intent, requestObj.resolvedType, requestObj.userId,
+ intent.getPackage(), digest, token);
+ }
+
+ public static void doEphemeralResolutionPhaseTwo(Context context,
+ EphemeralResolverConnection connection, EphemeralRequest requestObj,
+ ActivityInfo ephemeralInstaller, Handler callbackHandler) {
+ final Intent intent = requestObj.origIntent;
+ final EphemeralDigest digest =
+ new EphemeralDigest(intent.getData().getHost(), 5 /*maxDigests*/);
+ final int[] shaPrefix = digest.getDigestPrefix();
+ final byte[][] digestBytes = digest.getDigestBytes();
+
+ final PhaseTwoCallback callback = new PhaseTwoCallback() {
+ @Override
+ void onPhaseTwoResolved(List<EphemeralResolveInfo> ephemeralResolveInfoList,
+ int sequence) {
+ final String packageName;
+ final String splitName;
+ if (ephemeralResolveInfoList != null
+ && ephemeralResolveInfoList.size() > 0) {
+ final EphemeralResponse ephemeralIntentInfo =
+ EphemeralResolver.filterEphemeralIntent(
+ ephemeralResolveInfoList, intent, null /*resolvedType*/,
+ 0 /*userId*/, intent.getPackage(), digest,
+ requestObj.responseObj.token);
+ if (ephemeralIntentInfo != null
+ && ephemeralIntentInfo.resolveInfo != null) {
+ packageName = ephemeralIntentInfo.resolveInfo.getPackageName();
+ splitName = ephemeralIntentInfo.splitName;
+ } else {
+ packageName = null;
+ splitName = null;
+ }
+ } else {
+ packageName = null;
+ splitName = null;
+ }
+ final Intent installerIntent = buildEphemeralInstallerIntent(
+ requestObj.launchIntent,
+ requestObj.origIntent,
+ requestObj.callingPackage,
+ requestObj.resolvedType,
+ requestObj.userId,
+ packageName,
+ splitName,
+ requestObj.responseObj.token,
+ false /*needsPhaseTwo*/);
+ installerIntent.setComponent(new ComponentName(
+ ephemeralInstaller.packageName, ephemeralInstaller.name));
+ context.startActivity(installerIntent);
+ }
+ };
+ connection.getEphemeralIntentFilterList(
+ shaPrefix, callback, callbackHandler, 0 /*sequence*/);
+ }
+
+ /**
+ * Builds and returns an intent to launch the ephemeral installer.
+ */
+ public static Intent buildEphemeralInstallerIntent(Intent launchIntent, Intent origIntent,
+ String callingPackage, String resolvedType, int userId, String ephemeralPackageName,
+ String ephemeralSplitName, String token, boolean needsPhaseTwo) {
+ // Construct the intent that launches the ephemeral installer
+ int flags = launchIntent.getFlags();
+ final Intent intent = new Intent();
+ intent.setFlags(flags
+ | Intent.FLAG_ACTIVITY_NEW_TASK
+ | Intent.FLAG_ACTIVITY_CLEAR_TASK
+ | Intent.FLAG_ACTIVITY_NO_HISTORY
+ | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+ // TODO: Remove when the platform has fully implemented ephemeral apps
+ intent.setData(origIntent.getData().buildUpon().clearQuery().build());
+ intent.putExtra(Intent.EXTRA_EPHEMERAL_TOKEN, token);
+ intent.putExtra(Intent.EXTRA_EPHEMERAL_HOSTNAME, origIntent.getData().getHost());
+
+ if (!needsPhaseTwo) {
+ // We have all of the data we need; just start the installer without a second phase
+ final Intent nonEphemeralIntent = new Intent(origIntent);
+ nonEphemeralIntent.setFlags(
+ nonEphemeralIntent.getFlags() | Intent.FLAG_IGNORE_EPHEMERAL);
+ // Intent that is launched if the ephemeral package couldn't be installed
+ // for any reason.
+ try {
+ final IIntentSender failureIntentTarget = ActivityManagerNative.getDefault()
+ .getIntentSender(
+ ActivityManager.INTENT_SENDER_ACTIVITY, callingPackage,
+ null /*token*/, null /*resultWho*/, 1 /*requestCode*/,
+ new Intent[] { nonEphemeralIntent },
+ new String[] { resolvedType },
+ PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT
+ | PendingIntent.FLAG_IMMUTABLE,
+ null /*bOptions*/, userId);
+ intent.putExtra(Intent.EXTRA_EPHEMERAL_FAILURE,
+ new IntentSender(failureIntentTarget));
+ } catch (RemoteException ignore) { /* ignore; same process */ }
+
+ final Intent ephemeralIntent;
+ if (EphemeralResolver.USE_DEFAULT_EPHEMERAL_LAUNCHER) {
+ // Force the intent to be directed to the ephemeral package
+ ephemeralIntent = new Intent(origIntent);
+ ephemeralIntent.setPackage(ephemeralPackageName);
+ } else {
+ // Success intent goes back to the installer
+ ephemeralIntent = new Intent(launchIntent);
+ }
+
+ // Intent that is eventually launched if the ephemeral package was
+ // installed successfully. This will actually be launched by a platform
+ // broadcast receiver.
+ try {
+ final IIntentSender successIntentTarget = ActivityManagerNative.getDefault()
+ .getIntentSender(
+ ActivityManager.INTENT_SENDER_ACTIVITY, callingPackage,
+ null /*token*/, null /*resultWho*/, 0 /*requestCode*/,
+ new Intent[] { ephemeralIntent },
+ new String[] { resolvedType },
+ PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT
+ | PendingIntent.FLAG_IMMUTABLE,
+ null /*bOptions*/, userId);
+ intent.putExtra(Intent.EXTRA_EPHEMERAL_SUCCESS,
+ new IntentSender(successIntentTarget));
+ } catch (RemoteException ignore) { /* ignore; same process */ }
+
+ intent.putExtra(Intent.EXTRA_PACKAGE_NAME, ephemeralPackageName);
+ intent.putExtra(Intent.EXTRA_SPLIT_NAME, ephemeralSplitName);
+ }
+
+ return intent;
+ }
+
+ private static EphemeralResponse filterEphemeralIntent(
+ List<EphemeralResolveInfo> ephemeralResolveInfoList,
+ Intent intent, String resolvedType, int userId, String packageName,
+ EphemeralDigest digest, String token) {
+ final int[] shaPrefix = digest.getDigestPrefix();
+ final byte[][] digestBytes = digest.getDigestBytes();
+ // Go in reverse order so we match the narrowest scope first.
+ for (int i = shaPrefix.length - 1; i >= 0 ; --i) {
+ for (EphemeralResolveInfo ephemeralInfo : ephemeralResolveInfoList) {
+ if (!Arrays.equals(digestBytes[i], ephemeralInfo.getDigestBytes())) {
+ continue;
+ }
+ if (packageName != null
+ && !packageName.equals(ephemeralInfo.getPackageName())) {
+ continue;
+ }
+ final List<EphemeralIntentFilter> ephemeralFilters =
+ ephemeralInfo.getIntentFilters();
+ // No filters; we need to start phase two
+ if (ephemeralFilters == null || ephemeralFilters.isEmpty()) {
+ return new EphemeralResponse(ephemeralInfo,
+ new IntentFilter(Intent.ACTION_VIEW) /*intentFilter*/,
+ null /*splitName*/, token, true /*needsPhase2*/);
+ }
+ // We have a domain match; resolve the filters to see if anything matches.
+ final PackageManagerService.EphemeralIntentResolver ephemeralResolver =
+ new PackageManagerService.EphemeralIntentResolver();
+ for (int j = ephemeralFilters.size() - 1; j >= 0; --j) {
+ final EphemeralIntentFilter ephemeralFilter = ephemeralFilters.get(j);
+ final List<IntentFilter> splitFilters = ephemeralFilter.getFilters();
+ if (splitFilters == null || splitFilters.isEmpty()) {
+ continue;
+ }
+ for (int k = splitFilters.size() - 1; k >= 0; --k) {
+ final EphemeralResponse intentInfo =
+ new EphemeralResponse(ephemeralInfo,
+ splitFilters.get(k), ephemeralFilter.getSplitName(),
+ token, false /*needsPhase2*/);
+ ephemeralResolver.addFilter(intentInfo);
+ }
+ }
+ List<EphemeralResponse> matchedResolveInfoList = ephemeralResolver
+ .queryIntent(intent, resolvedType, false /*defaultOnly*/, userId);
+ if (!matchedResolveInfoList.isEmpty()) {
+ return matchedResolveInfoList.get(0);
+ }
+ }
+ }
+ // Hash or filter mis-match; no ephemeral apps for this domain.
+ return null;
+ }
+}
diff --git a/services/core/java/com/android/server/pm/EphemeralResolverConnection.java b/services/core/java/com/android/server/pm/EphemeralResolverConnection.java
index ecc03d7c5fbb..20d9813e250a 100644
--- a/services/core/java/com/android/server/pm/EphemeralResolverConnection.java
+++ b/services/core/java/com/android/server/pm/EphemeralResolverConnection.java
@@ -23,8 +23,10 @@ import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.pm.EphemeralResolveInfo;
+import android.content.pm.EphemeralResponse;
import android.os.Build;
import android.os.Bundle;
+import android.os.Handler;
import android.os.IBinder;
import android.os.IRemoteCallback;
import android.os.RemoteException;
@@ -54,8 +56,6 @@ final class EphemeralResolverConnection {
private final Object mLock = new Object();
private final GetEphemeralResolveInfoCaller mGetEphemeralResolveInfoCaller =
new GetEphemeralResolveInfoCaller();
- private final GetEphemeralIntentFilterCaller mGetEphemeralIntentFilterCaller =
- new GetEphemeralIntentFilterCaller();
private final ServiceConnection mServiceConnection = new MyServiceConnection();
private final Context mContext;
/** Intent used to bind to the service */
@@ -84,19 +84,27 @@ final class EphemeralResolverConnection {
return null;
}
- public final List<EphemeralResolveInfo> getEphemeralIntentFilterList(int digestPrefix[]) {
- throwIfCalledOnMainThread();
+ public final void getEphemeralIntentFilterList(int digestPrefix[], PhaseTwoCallback callback,
+ Handler callbackHandler, final int sequence) {
+ final IRemoteCallback remoteCallback = new IRemoteCallback.Stub() {
+ @Override
+ public void sendResult(Bundle data) throws RemoteException {
+ final ArrayList<EphemeralResolveInfo> ephemeralResolveInfoList =
+ data.getParcelableArrayList(EphemeralResolverService.EXTRA_RESOLVE_INFO);
+ callbackHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ callback.onPhaseTwoResolved(ephemeralResolveInfoList, sequence);
+ }
+ });
+ }
+ };
try {
- return mGetEphemeralIntentFilterCaller.getEphemeralIntentFilterList(
- getRemoteInstanceLazy(), digestPrefix);
+ getRemoteInstanceLazy()
+ .getEphemeralIntentFilterList(remoteCallback, digestPrefix, sequence);
} catch (RemoteException re) {
} catch (TimeoutException te) {
- } finally {
- synchronized (mLock) {
- mLock.notifyAll();
- }
}
- return null;
}
public void dump(FileDescriptor fd, PrintWriter pw, String prefix) {
@@ -161,6 +169,14 @@ final class EphemeralResolverConnection {
}
}
+ /**
+ * Asynchronous callback when results come back from ephemeral resolution phase two.
+ */
+ public abstract static class PhaseTwoCallback {
+ abstract void onPhaseTwoResolved(List<EphemeralResolveInfo> ephemeralResolveInfoList,
+ int sequence);
+ }
+
private final class MyServiceConnection implements ServiceConnection {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
@@ -205,32 +221,4 @@ final class EphemeralResolverConnection {
return getResultTimed(sequence);
}
}
-
- private static final class GetEphemeralIntentFilterCaller
- extends TimedRemoteCaller<List<EphemeralResolveInfo>> {
- private final IRemoteCallback mCallback;
-
- public GetEphemeralIntentFilterCaller() {
- super(TimedRemoteCaller.DEFAULT_CALL_TIMEOUT_MILLIS);
- mCallback = new IRemoteCallback.Stub() {
- @Override
- public void sendResult(Bundle data) throws RemoteException {
- final ArrayList<EphemeralResolveInfo> resolveList =
- data.getParcelableArrayList(
- EphemeralResolverService.EXTRA_RESOLVE_INFO);
- int sequence =
- data.getInt(EphemeralResolverService.EXTRA_SEQUENCE, -1);
- onRemoteMethodResult(resolveList, sequence);
- }
- };
- }
-
- public List<EphemeralResolveInfo> getEphemeralIntentFilterList(
- IEphemeralResolver target, int digestPrefix[])
- throws RemoteException, TimeoutException {
- final int sequence = onBeforeRemoteCall();
- target.getEphemeralIntentFilterList(mCallback, digestPrefix, sequence);
- return getResultTimed(sequence);
- }
- }
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 271107f0b4e3..ee3f42b7808e 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -121,10 +121,9 @@ import android.content.pm.ApplicationInfo;
import android.content.pm.AppsQueryHelper;
import android.content.pm.ComponentInfo;
import android.content.pm.EphemeralApplicationInfo;
-import android.content.pm.EphemeralIntentFilter;
+import android.content.pm.EphemeralRequest;
import android.content.pm.EphemeralResolveInfo;
-import android.content.pm.EphemeralResolveInfo.EphemeralDigest;
-import android.content.pm.EphemeralResolveInfo.EphemeralResolveIntentInfo;
+import android.content.pm.EphemeralResponse;
import android.content.pm.FeatureInfo;
import android.content.pm.IOnPermissionsChangeListener;
import android.content.pm.IPackageDataObserver;
@@ -1069,6 +1068,7 @@ public class PackageManagerService extends IPackageManager.Stub {
static final int START_INTENT_FILTER_VERIFICATIONS = 17;
static final int INTENT_FILTER_VERIFIED = 18;
static final int WRITE_PACKAGE_LIST = 19;
+ static final int EPHEMERAL_RESOLUTION_PHASE_TWO = 20;
static final int WRITE_SETTINGS_DELAY = 10*1000; // 10 seconds
@@ -1639,6 +1639,13 @@ public class PackageManagerService extends IPackageManager.Stub {
break;
}
+ case EPHEMERAL_RESOLUTION_PHASE_TWO: {
+ EphemeralResolver.doEphemeralResolutionPhaseTwo(mContext,
+ mEphemeralResolverConnection,
+ (EphemeralRequest) msg.obj,
+ mEphemeralInstallerActivity,
+ mHandler);
+ }
}
}
}
@@ -4951,60 +4958,13 @@ public class PackageManagerService extends IPackageManager.Stub {
return true;
}
- private static EphemeralResolveIntentInfo getEphemeralIntentInfo(
- Context context, EphemeralResolverConnection resolverConnection, Intent intent,
- String resolvedType, int userId, String packageName) {
- final EphemeralDigest digest =
- new EphemeralDigest(intent.getData().getHost(), 5 /*maxDigests*/);
- final int[] shaPrefix = digest.getDigestPrefix();
- final byte[][] digestBytes = digest.getDigestBytes();
- final List<EphemeralResolveInfo> ephemeralResolveInfoList =
- resolverConnection.getEphemeralResolveInfoList(shaPrefix);
- if (ephemeralResolveInfoList == null || ephemeralResolveInfoList.size() == 0) {
- // No hash prefix match; there are no ephemeral apps for this domain.
- return null;
- }
-
- // Go in reverse order so we match the narrowest scope first.
- for (int i = shaPrefix.length - 1; i >= 0 ; --i) {
- for (EphemeralResolveInfo ephemeralApplication : ephemeralResolveInfoList) {
- if (!Arrays.equals(digestBytes[i], ephemeralApplication.getDigestBytes())) {
- continue;
- }
- final List<EphemeralIntentFilter> ephemeralFilters =
- ephemeralApplication.getIntentFilters();
- // No filters; this should never happen.
- if (ephemeralFilters.isEmpty()) {
- continue;
- }
- if (packageName != null
- && !packageName.equals(ephemeralApplication.getPackageName())) {
- continue;
- }
- // We have a domain match; resolve the filters to see if anything matches.
- final EphemeralIntentResolver ephemeralResolver = new EphemeralIntentResolver();
- for (int j = ephemeralFilters.size() - 1; j >= 0; --j) {
- final EphemeralIntentFilter ephemeralFilter = ephemeralFilters.get(j);
- final List<IntentFilter> splitFilters = ephemeralFilter.getFilters();
- if (splitFilters == null || splitFilters.isEmpty()) {
- continue;
- }
- for (int k = splitFilters.size() - 1; k >= 0; --k) {
- final EphemeralResolveIntentInfo intentInfo =
- new EphemeralResolveIntentInfo(splitFilters.get(k),
- ephemeralApplication, ephemeralFilter.getSplitName());
- ephemeralResolver.addFilter(intentInfo);
- }
- }
- List<EphemeralResolveIntentInfo> matchedResolveInfoList = ephemeralResolver
- .queryIntent(intent, resolvedType, false /*defaultOnly*/, userId);
- if (!matchedResolveInfoList.isEmpty()) {
- return matchedResolveInfoList.get(0);
- }
- }
- }
- // Hash or filter mis-match; no ephemeral apps for this domain.
- return null;
+ private void requestEphemeralResolutionPhaseTwo(EphemeralResponse responseObj,
+ Intent origIntent, String resolvedType, Intent launchIntent, String callingPackage,
+ int userId) {
+ final Message msg = mHandler.obtainMessage(EPHEMERAL_RESOLUTION_PHASE_TWO,
+ new EphemeralRequest(responseObj, origIntent, resolvedType, launchIntent,
+ callingPackage, userId));
+ mHandler.sendMessage(msg);
}
private ResolveInfo chooseBestActivity(Intent intent, String resolvedType,
@@ -5480,15 +5440,17 @@ public class PackageManagerService extends IPackageManager.Stub {
}
if (addEphemeral) {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "resolveEphemeral");
- final EphemeralResolveIntentInfo intentInfo = getEphemeralIntentInfo(
- mContext, mEphemeralResolverConnection, intent, resolvedType, userId,
- matchEphemeralPackage ? pkgName : null);
+ final EphemeralRequest requestObject = new EphemeralRequest(
+ null /*responseObj*/, intent /*origIntent*/, resolvedType,
+ null /*launchIntent*/, null /*callingPackage*/, userId);
+ final EphemeralResponse intentInfo = EphemeralResolver.doEphemeralResolutionPhaseOne(
+ mContext, mEphemeralResolverConnection, requestObject);
if (intentInfo != null) {
if (DEBUG_EPHEMERAL) {
Slog.v(TAG, "Adding ephemeral installer to the ResolveInfo list");
}
final ResolveInfo ephemeralInstaller = new ResolveInfo(mEphemeralInstallerInfo);
- ephemeralInstaller.ephemeralIntentInfo = intentInfo;
+ ephemeralInstaller.ephemeralResponse = intentInfo;
// make sure this resolver is the default
ephemeralInstaller.isDefault = true;
ephemeralInstaller.match = IntentFilter.MATCH_CATEGORY_SCHEME_SPECIFIC_PART
@@ -11475,8 +11437,8 @@ public class PackageManagerService extends IPackageManager.Stub {
private int mFlags;
}
- private static final class EphemeralIntentResolver
- extends IntentResolver<EphemeralResolveIntentInfo, EphemeralResolveIntentInfo> {
+ static final class EphemeralIntentResolver
+ extends IntentResolver<EphemeralResponse, EphemeralResponse> {
/**
* The result that has the highest defined order. Ordering applies on a
* per-package basis. Mapping is from package name to Pair of order and
@@ -11491,46 +11453,46 @@ public class PackageManagerService extends IPackageManager.Stub {
final ArrayMap<String, Pair<Integer, EphemeralResolveInfo>> mOrderResult = new ArrayMap<>();
@Override
- protected EphemeralResolveIntentInfo[] newArray(int size) {
- return new EphemeralResolveIntentInfo[size];
+ protected EphemeralResponse[] newArray(int size) {
+ return new EphemeralResponse[size];
}
@Override
- protected boolean isPackageForFilter(String packageName, EphemeralResolveIntentInfo info) {
+ protected boolean isPackageForFilter(String packageName, EphemeralResponse responseObj) {
return true;
}
@Override
- protected EphemeralResolveIntentInfo newResult(EphemeralResolveIntentInfo info, int match,
+ protected EphemeralResponse newResult(EphemeralResponse responseObj, int match,
int userId) {
if (!sUserManager.exists(userId)) {
return null;
}
- final String packageName = info.getEphemeralResolveInfo().getPackageName();
- final Integer order = info.getOrder();
+ final String packageName = responseObj.resolveInfo.getPackageName();
+ final Integer order = responseObj.getOrder();
final Pair<Integer, EphemeralResolveInfo> lastOrderResult =
mOrderResult.get(packageName);
// ordering is enabled and this item's order isn't high enough
if (lastOrderResult != null && lastOrderResult.first >= order) {
return null;
}
- final EphemeralResolveInfo res = info.getEphemeralResolveInfo();
+ final EphemeralResolveInfo res = responseObj.resolveInfo;
if (order > 0) {
// non-zero order, enable ordering
mOrderResult.put(packageName, new Pair<>(order, res));
}
- return info;
+ return responseObj;
}
@Override
- protected void filterResults(List<EphemeralResolveIntentInfo> results) {
+ protected void filterResults(List<EphemeralResponse> results) {
// only do work if ordering is enabled [most of the time it won't be]
if (mOrderResult.size() == 0) {
return;
}
int resultSize = results.size();
for (int i = 0; i < resultSize; i++) {
- final EphemeralResolveInfo info = results.get(i).getEphemeralResolveInfo();
+ final EphemeralResolveInfo info = results.get(i).resolveInfo;
final String packageName = info.getPackageName();
final Pair<Integer, EphemeralResolveInfo> savedInfo = mOrderResult.get(packageName);
if (savedInfo == null) {
@@ -21313,6 +21275,14 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName());
public String getNameForUid(int uid) {
return PackageManagerService.this.getNameForUid(uid);
}
+
+ @Override
+ public void requestEphemeralResolutionPhaseTwo(EphemeralResponse responseObj,
+ Intent origIntent, String resolvedType, Intent launchIntent,
+ String callingPackage, int userId) {
+ PackageManagerService.this.requestEphemeralResolutionPhaseTwo(
+ responseObj, origIntent, resolvedType, launchIntent, callingPackage, userId);
+ }
}
@Override