Implement new API
This is the first swack at the new, 2-phase API. Adds the new methods
to the resolver service and makes the split name explicit on the
installer intent.
The 2nd phase will not yet be invoked; that's coming in a follow-on
change.
Bug: 25119046
Test: build & install the sample resolver and run 'adb shell am start -a android.intent.action.VIEW -c android.intent.category.BROWSABLE -d "https://www.tripadvisor.com/Tourism-g33020-San_Jose_California-Vacations.html"'
Change-Id: I2df6fa64d46f17a86a2e32b19417632c594fb10f
diff --git a/api/system-current.txt b/api/system-current.txt
index 6622ea5..9aebe8f 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -4545,7 +4545,9 @@
ctor public EphemeralResolverService();
method public final void attachBaseContext(android.content.Context);
method public final android.os.IBinder onBind(android.content.Intent);
- method public abstract java.util.List<android.content.pm.EphemeralResolveInfo> onEphemeralResolveInfoList(int[], int);
+ method public abstract deprecated java.util.List<android.content.pm.EphemeralResolveInfo> onEphemeralResolveInfoList(int[], int);
+ method public java.util.List<android.content.pm.EphemeralResolveInfo> onGetEphemeralIntentFilter(int[]);
+ method public java.util.List<android.content.pm.EphemeralResolveInfo> onGetEphemeralResolveInfo(int[]);
field public static final java.lang.String EXTRA_RESOLVE_INFO = "android.app.extra.RESOLVE_INFO";
field public static final java.lang.String EXTRA_SEQUENCE = "android.app.extra.SEQUENCE";
}
@@ -9189,6 +9191,7 @@
field public static final java.lang.String EXTRA_SHORTCUT_INTENT = "android.intent.extra.shortcut.INTENT";
field public static final java.lang.String EXTRA_SHORTCUT_NAME = "android.intent.extra.shortcut.NAME";
field public static final java.lang.String EXTRA_SHUTDOWN_USERSPACE_ONLY = "android.intent.extra.SHUTDOWN_USERSPACE_ONLY";
+ field public static final java.lang.String EXTRA_SPLIT_NAME = "android.intent.extra.SPLIT_NAME";
field public static final java.lang.String EXTRA_STREAM = "android.intent.extra.STREAM";
field public static final java.lang.String EXTRA_SUBJECT = "android.intent.extra.SUBJECT";
field public static final java.lang.String EXTRA_TEMPLATE = "android.intent.extra.TEMPLATE";
@@ -9893,18 +9896,39 @@
field public int reqTouchScreen;
}
+ public final class EphemeralIntentFilter implements android.os.Parcelable {
+ ctor public EphemeralIntentFilter(java.lang.String, java.util.List<android.content.IntentFilter>);
+ method public int describeContents();
+ method public java.util.List<android.content.IntentFilter> getFilters();
+ method public java.lang.String getSplitName();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.content.pm.EphemeralIntentFilter> CREATOR;
+ }
+
public final class EphemeralResolveInfo implements android.os.Parcelable {
- ctor public EphemeralResolveInfo(android.net.Uri, java.lang.String, java.util.List<android.content.IntentFilter>);
+ ctor public deprecated EphemeralResolveInfo(android.net.Uri, java.lang.String, java.util.List<android.content.IntentFilter>);
+ ctor public EphemeralResolveInfo(android.content.pm.EphemeralResolveInfo.EphemeralDigest, java.lang.String, java.util.List<android.content.pm.EphemeralIntentFilter>);
+ ctor public EphemeralResolveInfo(java.lang.String, java.lang.String, java.util.List<android.content.pm.EphemeralIntentFilter>);
method public int describeContents();
method public byte[] getDigestBytes();
method public int getDigestPrefix();
- method public java.util.List<android.content.IntentFilter> getFilters();
+ method public deprecated java.util.List<android.content.IntentFilter> getFilters();
+ method public java.util.List<android.content.pm.EphemeralIntentFilter> getIntentFilters();
method public java.lang.String getPackageName();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.content.pm.EphemeralResolveInfo> CREATOR;
field public static final java.lang.String SHA_ALGORITHM = "SHA-256";
}
+ public static final class EphemeralResolveInfo.EphemeralDigest implements android.os.Parcelable {
+ ctor public EphemeralResolveInfo.EphemeralDigest(java.lang.String);
+ method public int describeContents();
+ method public byte[][] getDigestBytes();
+ method public int[] getDigestPrefix();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.content.pm.EphemeralResolveInfo.EphemeralDigest> CREATOR;
+ }
+
public final class FeatureGroupInfo implements android.os.Parcelable {
ctor public FeatureGroupInfo();
ctor public FeatureGroupInfo(android.content.pm.FeatureGroupInfo);
diff --git a/core/java/android/app/EphemeralResolverService.java b/core/java/android/app/EphemeralResolverService.java
index ba79108..d8d7340 100644
--- a/core/java/android/app/EphemeralResolverService.java
+++ b/core/java/android/app/EphemeralResolverService.java
@@ -40,6 +40,7 @@
public static final String EXTRA_RESOLVE_INFO = "android.app.extra.RESOLVE_INFO";
public static final String EXTRA_SEQUENCE = "android.app.extra.SEQUENCE";
private static final String EXTRA_PREFIX = "android.app.PREFIX";
+ private static final String EXTRA_HOSTNAME = "android.app.HOSTNAME";
private Handler mHandler;
/**
@@ -49,9 +50,29 @@
* @param prefixMask A mask that was applied to each digest prefix. This should
* be used when comparing against the digest prefixes as all bits might
* not be set.
+ * @deprecated use {@link #onGetEphemeralResolveInfo(int[])} instead
*/
+ @Deprecated
public abstract List<EphemeralResolveInfo> onEphemeralResolveInfoList(
- int digestPrefix[], int prefixMask);
+ int digestPrefix[], int prefix);
+
+ /**
+ * Called to retrieve resolve info for ephemeral applications.
+ *
+ * @param digestPrefix The hash prefix of the ephemeral's domain.
+ */
+ public List<EphemeralResolveInfo> onGetEphemeralResolveInfo(int digestPrefix[]) {
+ return onEphemeralResolveInfoList(digestPrefix, 0xFFFFF000);
+ }
+
+ /**
+ * Called to retrieve intent filters for ephemeral applications.
+ *
+ * @param digestPrefix The hash prefix of the ephemeral's domain.
+ */
+ public List<EphemeralResolveInfo> onGetEphemeralIntentFilter(int digestPrefix[]) {
+ throw new IllegalStateException("Must define");
+ }
@Override
public final void attachBaseContext(Context base) {
@@ -64,9 +85,20 @@
return new IEphemeralResolver.Stub() {
@Override
public void getEphemeralResolveInfoList(
- IRemoteCallback callback, int digestPrefix[], int prefixMask, int sequence) {
+ IRemoteCallback callback, int digestPrefix[], int sequence) {
final Message msg = mHandler.obtainMessage(
- ServiceHandler.MSG_GET_EPHEMERAL_RESOLVE_INFO, prefixMask, sequence, callback);
+ ServiceHandler.MSG_GET_EPHEMERAL_RESOLVE_INFO, sequence, 0, callback);
+ final Bundle data = new Bundle();
+ data.putIntArray(EXTRA_PREFIX, digestPrefix);
+ msg.setData(data);
+ msg.sendToTarget();
+ }
+
+ @Override
+ public void getEphemeralIntentFilterList(
+ IRemoteCallback callback, int digestPrefix[], int sequence) {
+ final Message msg = mHandler.obtainMessage(
+ ServiceHandler.MSG_GET_EPHEMERAL_INTENT_FILTER, sequence, 0, callback);
final Bundle data = new Bundle();
data.putIntArray(EXTRA_PREFIX, digestPrefix);
msg.setData(data);
@@ -77,6 +109,7 @@
private final class ServiceHandler extends Handler {
public static final int MSG_GET_EPHEMERAL_RESOLVE_INFO = 1;
+ public static final int MSG_GET_EPHEMERAL_INTENT_FILTER = 2;
public ServiceHandler(Looper looper) {
super(looper, null /*callback*/, true /*async*/);
@@ -91,9 +124,23 @@
final IRemoteCallback callback = (IRemoteCallback) message.obj;
final int[] digestPrefix = message.getData().getIntArray(EXTRA_PREFIX);
final List<EphemeralResolveInfo> resolveInfo =
- onEphemeralResolveInfoList(digestPrefix, message.arg1);
+ onGetEphemeralResolveInfo(digestPrefix);
final Bundle data = new Bundle();
- data.putInt(EXTRA_SEQUENCE, message.arg2);
+ data.putInt(EXTRA_SEQUENCE, message.arg1);
+ data.putParcelableList(EXTRA_RESOLVE_INFO, resolveInfo);
+ try {
+ callback.sendResult(data);
+ } catch (RemoteException e) {
+ }
+ } break;
+
+ case MSG_GET_EPHEMERAL_INTENT_FILTER: {
+ final IRemoteCallback callback = (IRemoteCallback) message.obj;
+ final int[] digestPrefix = message.getData().getIntArray(EXTRA_PREFIX);
+ final List<EphemeralResolveInfo> resolveInfo =
+ onGetEphemeralIntentFilter(digestPrefix);
+ final Bundle data = new Bundle();
+ data.putInt(EXTRA_SEQUENCE, message.arg1);
data.putParcelableList(EXTRA_RESOLVE_INFO, resolveInfo);
try {
callback.sendResult(data);
diff --git a/core/java/android/app/IEphemeralResolver.aidl b/core/java/android/app/IEphemeralResolver.aidl
index ee869ea..e0cef83 100644
--- a/core/java/android/app/IEphemeralResolver.aidl
+++ b/core/java/android/app/IEphemeralResolver.aidl
@@ -21,5 +21,8 @@
/** @hide */
oneway interface IEphemeralResolver {
void getEphemeralResolveInfoList(IRemoteCallback callback, in int[] digestPrefix,
- int prefixMask, int sequence);
+ int sequence);
+
+ void getEphemeralIntentFilterList(IRemoteCallback callback, in int[] digestPrefix,
+ int sequence);
}
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 9015c4b..a9e987a 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -1713,6 +1713,16 @@
public static final String EXTRA_PACKAGE_NAME = "android.intent.extra.PACKAGE_NAME";
/**
+ * Intent extra: An app split name.
+ * <p>
+ * Type: String
+ * </p>
+ * @hide
+ */
+ @SystemApi
+ public static final String EXTRA_SPLIT_NAME = "android.intent.extra.SPLIT_NAME";
+
+ /**
* Intent extra: An extra for specifying whether a result is needed.
* <p>
* Type: boolean
diff --git a/core/java/android/content/pm/EphemeralIntentFilter.java b/core/java/android/content/pm/EphemeralIntentFilter.java
new file mode 100644
index 0000000..0674e7c
--- /dev/null
+++ b/core/java/android/content/pm/EphemeralIntentFilter.java
@@ -0,0 +1,96 @@
+/*
+ * 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.annotation.SystemApi;
+import android.content.IntentFilter;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Information about an ephemeral application intent filter.
+ * @hide
+ */
+@SystemApi
+public final class EphemeralIntentFilter implements Parcelable {
+ private final String mSplitName;
+ /** The filters used to match domain */
+ private final List<IntentFilter> mFilters = new ArrayList<IntentFilter>();
+
+ public EphemeralIntentFilter(@Nullable String splitName, @NonNull List<IntentFilter> filters) {
+ if (filters == null || filters.size() == 0) {
+ throw new IllegalArgumentException();
+ }
+ mSplitName = splitName;
+ mFilters.addAll(filters);
+ }
+
+ EphemeralIntentFilter(Parcel in) {
+ mSplitName = in.readString();
+ in.readList(mFilters, null /*loader*/);
+ }
+
+ public String getSplitName() {
+ return mSplitName;
+ }
+
+ public List<IntentFilter> getFilters() {
+ return mFilters;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeString(mSplitName);
+ out.writeList(mFilters);
+ }
+
+ public static final Parcelable.Creator<EphemeralIntentFilter> CREATOR
+ = new Parcelable.Creator<EphemeralIntentFilter>() {
+ public EphemeralIntentFilter createFromParcel(Parcel in) {
+ return new EphemeralIntentFilter(in);
+ }
+
+ public EphemeralIntentFilter[] newArray(int size) {
+ return new EphemeralIntentFilter[size];
+ }
+ };
+
+ /** @hide */
+ public static final class EphemeralResolveIntentInfo extends IntentFilter {
+ private final EphemeralIntentFilter mResolveInfo;
+
+ public EphemeralResolveIntentInfo(@NonNull IntentFilter orig,
+ @NonNull EphemeralIntentFilter resolveInfo) {
+ super(orig);
+ this.mResolveInfo = resolveInfo;
+ }
+
+ public EphemeralIntentFilter getEphemeralResolveInfo() {
+ return mResolveInfo;
+ }
+ }
+}
diff --git a/core/java/android/content/pm/EphemeralResolveInfo.java b/core/java/android/content/pm/EphemeralResolveInfo.java
index fc3b958..3bed06b 100644
--- a/core/java/android/content/pm/EphemeralResolveInfo.java
+++ b/core/java/android/content/pm/EphemeralResolveInfo.java
@@ -17,6 +17,7 @@
package android.content.pm;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.content.IntentFilter;
import android.net.Uri;
@@ -41,27 +42,54 @@
private final EphemeralDigest mDigest;
private final String mPackageName;
/** The filters used to match domain */
- private final List<IntentFilter> mFilters = new ArrayList<IntentFilter>();
+ private final List<EphemeralIntentFilter> mFilters;
+ /** Filters only for legacy clients */
+ @Deprecated
+ private List<IntentFilter> mLegacyFilters;
+ @Deprecated
public EphemeralResolveInfo(@NonNull Uri uri, @NonNull String packageName,
@NonNull List<IntentFilter> filters) {
- // validate arguments
- if (uri == null
- || packageName == null
- || filters == null
- || filters.size() == 0) {
+ if (uri == null || packageName == null || filters == null || filters.isEmpty()) {
throw new IllegalArgumentException();
}
-
- mDigest = new EphemeralDigest(uri, 0xFFFFFFFF, -1);
- mFilters.addAll(filters);
+ mDigest = new EphemeralDigest(uri.getHost());
mPackageName = packageName;
+ mFilters = new ArrayList<EphemeralIntentFilter>();
+ mFilters.add(new EphemeralIntentFilter(packageName, filters));
+ mLegacyFilters = new ArrayList<IntentFilter>(filters.size());
+ mLegacyFilters.addAll(filters);
+ }
+
+ public EphemeralResolveInfo(@NonNull EphemeralDigest digest, @Nullable String packageName,
+ @Nullable List<EphemeralIntentFilter> filters) {
+ // validate arguments
+ if ((packageName == null && (filters != null && filters.size() != 0))
+ || (packageName != null && (filters == null || filters.size() == 0))) {
+ throw new IllegalArgumentException();
+ }
+ mDigest = digest;
+ if (filters != null) {
+ mFilters = new ArrayList<EphemeralIntentFilter>(filters.size());
+ mFilters.addAll(filters);
+ } else {
+ mFilters = null;
+ }
+ mPackageName = packageName;
+ }
+
+ public EphemeralResolveInfo(@NonNull String hostName, @Nullable String packageName,
+ @Nullable List<EphemeralIntentFilter> filters) {
+ this(new EphemeralDigest(hostName), packageName, filters);
}
EphemeralResolveInfo(Parcel in) {
mDigest = in.readParcelable(null /*loader*/);
mPackageName = in.readString();
+ mFilters = new ArrayList<EphemeralIntentFilter>();
in.readList(mFilters, null /*loader*/);
+ mLegacyFilters = new ArrayList<IntentFilter>();
+ in.readList(mLegacyFilters, null /*loader*/);
}
public byte[] getDigestBytes() {
@@ -76,7 +104,12 @@
return mPackageName;
}
+ @Deprecated
public List<IntentFilter> getFilters() {
+ return mLegacyFilters;
+ }
+
+ public List<EphemeralIntentFilter> getIntentFilters() {
return mFilters;
}
@@ -90,6 +123,7 @@
out.writeParcelable(mDigest, flags);
out.writeString(mPackageName);
out.writeList(mFilters);
+ out.writeList(mLegacyFilters);
}
public static final Parcelable.Creator<EphemeralResolveInfo> CREATOR
@@ -106,16 +140,23 @@
/** @hide */
public static final class EphemeralResolveIntentInfo extends IntentFilter {
private final EphemeralResolveInfo mResolveInfo;
+ private final String mSplitName;
public EphemeralResolveIntentInfo(@NonNull IntentFilter orig,
- @NonNull EphemeralResolveInfo resolveInfo) {
+ @NonNull EphemeralResolveInfo resolveInfo,
+ @Nullable String splitName) {
super(orig);
- this.mResolveInfo = resolveInfo;
+ mResolveInfo = resolveInfo;
+ mSplitName = splitName;
}
public EphemeralResolveInfo getEphemeralResolveInfo() {
return mResolveInfo;
}
+
+ public String getSplitName() {
+ return mSplitName;
+ }
}
/**
@@ -129,17 +170,25 @@
*
* @hide
*/
+ @SystemApi
public static final class EphemeralDigest implements Parcelable {
+ private static final int DIGEST_MASK = 0xfffff000;
+ private static final int DIGEST_PREFIX_COUNT = 5;
/** Full digest of the domain hashes */
private final byte[][] mDigestBytes;
/** The first 4 bytes of the domain hashes */
private final int[] mDigestPrefix;
- public EphemeralDigest(@NonNull Uri uri, int digestMask, int maxDigests) {
- if (uri == null) {
+ public EphemeralDigest(@NonNull String hostName) {
+ this(hostName, -1 /*maxDigests*/);
+ }
+
+ /** @hide */
+ public EphemeralDigest(@NonNull String hostName, int maxDigests) {
+ if (hostName == null) {
throw new IllegalArgumentException();
}
- mDigestBytes = generateDigest(uri, maxDigests);
+ mDigestBytes = generateDigest(hostName.toLowerCase(Locale.ENGLISH), maxDigests);
mDigestPrefix = new int[mDigestBytes.length];
for (int i = 0; i < mDigestBytes.length; i++) {
mDigestPrefix[i] =
@@ -147,31 +196,32 @@
| (mDigestBytes[i][1] & 0xFF) << 16
| (mDigestBytes[i][2] & 0xFF) << 8
| (mDigestBytes[i][3] & 0xFF) << 0)
- & digestMask;
+ & DIGEST_MASK;
}
}
- private static byte[][] generateDigest(Uri uri, int maxDigests) {
+ private static byte[][] generateDigest(String hostName, int maxDigests) {
ArrayList<byte[]> digests = new ArrayList<>();
try {
- final String host = uri.getHost().toLowerCase(Locale.ENGLISH);
final MessageDigest digest = MessageDigest.getInstance(SHA_ALGORITHM);
if (maxDigests <= 0) {
- final byte[] hostBytes = host.getBytes();
+ final byte[] hostBytes = hostName.getBytes();
digests.add(digest.digest(hostBytes));
} else {
- int prevDot = host.lastIndexOf('.');
- prevDot = host.lastIndexOf('.', prevDot - 1);
+ int prevDot = hostName.lastIndexOf('.');
+ prevDot = hostName.lastIndexOf('.', prevDot - 1);
// shortcut for short URLs
if (prevDot < 0) {
- digests.add(digest.digest(host.getBytes()));
+ digests.add(digest.digest(hostName.getBytes()));
} else {
- byte[] hostBytes = host.substring(prevDot + 1, host.length()).getBytes();
+ byte[] hostBytes =
+ hostName.substring(prevDot + 1, hostName.length()).getBytes();
digests.add(digest.digest(hostBytes));
int digestCount = 1;
while (prevDot >= 0 && digestCount < maxDigests) {
- prevDot = host.lastIndexOf('.', prevDot - 1);
- hostBytes = host.substring(prevDot + 1, host.length()).getBytes();
+ prevDot = hostName.lastIndexOf('.', prevDot - 1);
+ hostBytes =
+ hostName.substring(prevDot + 1, hostName.length()).getBytes();
digests.add(digest.digest(hostBytes));
digestCount++;
}
diff --git a/core/java/android/content/pm/ResolveInfo.java b/core/java/android/content/pm/ResolveInfo.java
index b5df4d7..86dbe8a 100644
--- a/core/java/android/content/pm/ResolveInfo.java
+++ b/core/java/android/content/pm/ResolveInfo.java
@@ -18,6 +18,7 @@
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;
@@ -65,7 +66,7 @@
* only be set in specific circumstances.
* @hide
*/
- public EphemeralResolveInfo ephemeralResolveInfo;
+ public EphemeralResolveIntentInfo ephemeralIntentInfo;
/**
* The IntentFilter that was matched for this ResolveInfo.
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 1b905a0..22336c6 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -8956,24 +8956,6 @@
public static final String ENABLE_EPHEMERAL_FEATURE = "enable_ephemeral_feature";
/**
- * A mask applied to the ephemeral hash to generate the hash prefix.
- * <p>
- * Type: int
- *
- * @hide
- */
- public static final String EPHEMERAL_HASH_PREFIX_MASK = "ephemeral_hash_prefix_mask";
-
- /**
- * Number of hash prefixes to send during ephemeral resolution.
- * <p>
- * Type: int
- *
- * @hide
- */
- public static final String EPHEMERAL_HASH_PREFIX_COUNT = "ephemeral_hash_prefix_count";
-
- /**
* The duration for caching uninstalled ephemeral apps.
* <p>
* Type: long
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index e4ec169..2a0c6d0 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -458,9 +458,12 @@
// 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.ephemeralResolveInfo != null) {
+ if (rInfo != null && rInfo.ephemeralIntentInfo != null) {
+ final String packageName =
+ rInfo.ephemeralIntentInfo.getEphemeralResolveInfo().getPackageName();
+ final String splitName = rInfo.ephemeralIntentInfo.getSplitName();
intent = buildEphemeralInstallerIntent(intent, ephemeralIntent,
- rInfo.ephemeralResolveInfo.getPackageName(), callingPackage, resolvedType,
+ packageName, splitName, callingPackage, resolvedType,
userId);
resolvedType = null;
callingUid = realCallingUid;
@@ -524,7 +527,8 @@
* Builds and returns an intent to launch the ephemeral installer.
*/
private Intent buildEphemeralInstallerIntent(Intent launchIntent, Intent origIntent,
- String ephemeralPackage, String callingPackage, String resolvedType, int userId) {
+ 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
@@ -540,7 +544,7 @@
if (USE_DEFAULT_EPHEMERAL_LAUNCHER) {
// Force the intent to be directed to the ephemeral package
ephemeralIntent = new Intent(origIntent);
- ephemeralIntent.setPackage(ephemeralPackage);
+ ephemeralIntent.setPackage(ephemeralPackageName);
} else {
// Success intent goes back to the installer
ephemeralIntent = new Intent(launchIntent);
@@ -564,7 +568,8 @@
| Intent.FLAG_ACTIVITY_CLEAR_TASK
| Intent.FLAG_ACTIVITY_NO_HISTORY
| Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
- intent.putExtra(Intent.EXTRA_PACKAGE_NAME, ephemeralPackage);
+ 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
diff --git a/services/core/java/com/android/server/pm/EphemeralResolverConnection.java b/services/core/java/com/android/server/pm/EphemeralResolverConnection.java
index a6a3774..9778919 100644
--- a/services/core/java/com/android/server/pm/EphemeralResolverConnection.java
+++ b/services/core/java/com/android/server/pm/EphemeralResolverConnection.java
@@ -51,6 +51,8 @@
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 */
@@ -64,12 +66,26 @@
mIntent = new Intent(Intent.ACTION_RESOLVE_EPHEMERAL_PACKAGE).setComponent(componentName);
}
- public final List<EphemeralResolveInfo> getEphemeralResolveInfoList(
- int hashPrefix[], int prefixMask) {
+ public final List<EphemeralResolveInfo> getEphemeralResolveInfoList(int hashPrefix[]) {
throwIfCalledOnMainThread();
try {
return mGetEphemeralResolveInfoCaller.getEphemeralResolveInfoList(
- getRemoteInstanceLazy(), hashPrefix, prefixMask);
+ getRemoteInstanceLazy(), hashPrefix);
+ } catch (RemoteException re) {
+ } catch (TimeoutException te) {
+ } finally {
+ synchronized (mLock) {
+ mLock.notifyAll();
+ }
+ }
+ return null;
+ }
+
+ public final List<EphemeralResolveInfo> getEphemeralIntentFilterList(int digestPrefix[]) {
+ throwIfCalledOnMainThread();
+ try {
+ return mGetEphemeralIntentFilterCaller.getEphemeralIntentFilterList(
+ getRemoteInstanceLazy(), digestPrefix);
} catch (RemoteException re) {
} catch (TimeoutException te) {
} finally {
@@ -181,10 +197,38 @@
}
public List<EphemeralResolveInfo> getEphemeralResolveInfoList(
- IEphemeralResolver target, int hashPrefix[], int prefixMask)
+ IEphemeralResolver target, int hashPrefix[])
throws RemoteException, TimeoutException {
final int sequence = onBeforeRemoteCall();
- target.getEphemeralResolveInfoList(mCallback, hashPrefix, prefixMask, sequence);
+ target.getEphemeralResolveInfoList(mCallback, hashPrefix, sequence);
+ 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 b1f2a24..21b6964 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -122,6 +122,7 @@
import android.content.pm.AppsQueryHelper;
import android.content.pm.ComponentInfo;
import android.content.pm.EphemeralApplicationInfo;
+import android.content.pm.EphemeralIntentFilter;
import android.content.pm.EphemeralResolveInfo;
import android.content.pm.EphemeralResolveInfo.EphemeralDigest;
import android.content.pm.EphemeralResolveInfo.EphemeralResolveIntentInfo;
@@ -478,9 +479,6 @@
private static final String VENDOR_OVERLAY_THEME_PERSIST_PROPERTY
= "persist.vendor.overlay.theme";
- private static int DEFAULT_EPHEMERAL_HASH_PREFIX_MASK = 0xFFFFF000;
- private static int DEFAULT_EPHEMERAL_HASH_PREFIX_COUNT = 5;
-
/** Permission grant: not grant the permission. */
private static final int GRANT_DENIED = 1;
@@ -4945,19 +4943,15 @@
return true;
}
- private static EphemeralResolveInfo getEphemeralResolveInfo(
+ private static EphemeralResolveIntentInfo getEphemeralIntentInfo(
Context context, EphemeralResolverConnection resolverConnection, Intent intent,
String resolvedType, int userId, String packageName) {
- final int ephemeralPrefixMask = Global.getInt(context.getContentResolver(),
- Global.EPHEMERAL_HASH_PREFIX_MASK, DEFAULT_EPHEMERAL_HASH_PREFIX_MASK);
- final int ephemeralPrefixCount = Global.getInt(context.getContentResolver(),
- Global.EPHEMERAL_HASH_PREFIX_COUNT, DEFAULT_EPHEMERAL_HASH_PREFIX_COUNT);
- final EphemeralDigest digest = new EphemeralDigest(intent.getData(), ephemeralPrefixMask,
- ephemeralPrefixCount);
+ 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, ephemeralPrefixMask);
+ resolverConnection.getEphemeralResolveInfoList(shaPrefix);
if (ephemeralResolveInfoList == null || ephemeralResolveInfoList.size() == 0) {
// No hash prefix match; there are no ephemeral apps for this domain.
return null;
@@ -4969,9 +4963,10 @@
if (!Arrays.equals(digestBytes[i], ephemeralApplication.getDigestBytes())) {
continue;
}
- final List<IntentFilter> filters = ephemeralApplication.getFilters();
+ final List<EphemeralIntentFilter> ephemeralFilters =
+ ephemeralApplication.getIntentFilters();
// No filters; this should never happen.
- if (filters.isEmpty()) {
+ if (ephemeralFilters.isEmpty()) {
continue;
}
if (packageName != null
@@ -4980,13 +4975,21 @@
}
// We have a domain match; resolve the filters to see if anything matches.
final EphemeralIntentResolver ephemeralResolver = new EphemeralIntentResolver();
- for (int j = filters.size() - 1; j >= 0; --j) {
- final EphemeralResolveIntentInfo intentInfo =
- new EphemeralResolveIntentInfo(filters.get(j), ephemeralApplication);
- ephemeralResolver.addFilter(intentInfo);
+ 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<EphemeralResolveInfo> matchedResolveInfoList = ephemeralResolver.queryIntent(
- intent, resolvedType, false /*defaultOnly*/, userId);
+ List<EphemeralResolveIntentInfo> matchedResolveInfoList = ephemeralResolver
+ .queryIntent(intent, resolvedType, false /*defaultOnly*/, userId);
if (!matchedResolveInfoList.isEmpty()) {
return matchedResolveInfoList.get(0);
}
@@ -5469,15 +5472,15 @@
}
if (addEphemeral) {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "resolveEphemeral");
- final EphemeralResolveInfo ai = getEphemeralResolveInfo(
+ final EphemeralResolveIntentInfo intentInfo = getEphemeralIntentInfo(
mContext, mEphemeralResolverConnection, intent, resolvedType, userId,
matchEphemeralPackage ? pkgName : null);
- if (ai != null) {
+ if (intentInfo != null) {
if (DEBUG_EPHEMERAL) {
Slog.v(TAG, "Adding ephemeral installer to the ResolveInfo list");
}
final ResolveInfo ephemeralInstaller = new ResolveInfo(mEphemeralInstallerInfo);
- ephemeralInstaller.ephemeralResolveInfo = ai;
+ ephemeralInstaller.ephemeralIntentInfo = intentInfo;
// make sure this resolver is the default
ephemeralInstaller.isDefault = true;
ephemeralInstaller.match = IntentFilter.MATCH_CATEGORY_SCHEME_SPECIFIC_PART
@@ -11451,7 +11454,7 @@
}
private static final class EphemeralIntentResolver
- extends IntentResolver<EphemeralResolveIntentInfo, EphemeralResolveInfo> {
+ extends IntentResolver<EphemeralResolveIntentInfo, EphemeralResolveIntentInfo> {
/**
* 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
@@ -11476,7 +11479,7 @@
}
@Override
- protected EphemeralResolveInfo newResult(EphemeralResolveIntentInfo info, int match,
+ protected EphemeralResolveIntentInfo newResult(EphemeralResolveIntentInfo info, int match,
int userId) {
if (!sUserManager.exists(userId)) {
return null;
@@ -11494,18 +11497,18 @@
// non-zero order, enable ordering
mOrderResult.put(packageName, new Pair<>(order, res));
}
- return res;
+ return info;
}
@Override
- protected void filterResults(List<EphemeralResolveInfo> results) {
+ protected void filterResults(List<EphemeralResolveIntentInfo> 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);
+ final EphemeralResolveInfo info = results.get(i).getEphemeralResolveInfo();
final String packageName = info.getPackageName();
final Pair<Integer, EphemeralResolveInfo> savedInfo = mOrderResult.get(packageName);
if (savedInfo == null) {