Loading core/java/android/content/pm/EphemeralResolveInfo.java +122 −26 Original line number Diff line number Diff line Loading @@ -37,10 +37,7 @@ public final class EphemeralResolveInfo implements Parcelable { /** Algorithm that will be used to generate the domain digest */ public static final String SHA_ALGORITHM = "SHA-256"; /** Full digest of the domain hash */ private final byte[] mDigestBytes; /** The first 4 bytes of the domain hash */ private final int mDigestPrefix; private final EphemeralDigest mDigest; private final String mPackageName; /** The filters used to match domain */ private final List<IntentFilter> mFilters = new ArrayList<IntentFilter>(); Loading @@ -55,29 +52,23 @@ public final class EphemeralResolveInfo implements Parcelable { throw new IllegalArgumentException(); } mDigestBytes = generateDigest(uri); mDigestPrefix = (mDigestBytes[0] & 0xFF) << 24 | (mDigestBytes[1] & 0xFF) << 16 | (mDigestBytes[2] & 0xFF) << 8 | (mDigestBytes[3] & 0xFF) << 0; mDigest = new EphemeralDigest(uri, -1); mFilters.addAll(filters); mPackageName = packageName; } EphemeralResolveInfo(Parcel in) { mDigestBytes = in.createByteArray(); mDigestPrefix = in.readInt(); mDigest = in.readParcelable(null /*loader*/); mPackageName = in.readString(); in.readList(mFilters, null /*loader*/); } public byte[] getDigestBytes() { return mDigestBytes; return mDigest.getDigestBytes()[0]; } public int getDigestPrefix() { return mDigestPrefix; return mDigest.getDigestPrefix()[0]; } public String getPackageName() { Loading @@ -88,16 +79,6 @@ public final class EphemeralResolveInfo implements Parcelable { return mFilters; } private static byte[] generateDigest(Uri uri) { try { final MessageDigest digest = MessageDigest.getInstance(SHA_ALGORITHM); final byte[] hostBytes = uri.getHost().getBytes(); return digest.digest(hostBytes); } catch (NoSuchAlgorithmException e) { throw new IllegalStateException("could not find digest algorithm"); } } @Override public int describeContents() { return 0; Loading @@ -105,8 +86,7 @@ public final class EphemeralResolveInfo implements Parcelable { @Override public void writeToParcel(Parcel out, int flags) { out.writeByteArray(mDigestBytes); out.writeInt(mDigestPrefix); out.writeParcelable(mDigest, flags); out.writeString(mPackageName); out.writeList(mFilters); } Loading Loading @@ -136,4 +116,120 @@ public final class EphemeralResolveInfo implements Parcelable { return mResolveInfo; } } /** * Helper class to generate and store each of the digests and prefixes * sent to the Ephemeral Resolver. * <p> * Since intent filters may want to handle multiple hosts within a * domain [eg “*.google.com”], the resolver is presented with multiple * hash prefixes. For example, "a.b.c.d.e" generates digests for * "d.e", "c.d.e", "b.c.d.e" and "a.b.c.d.e". * * @hide */ public static final class EphemeralDigest implements Parcelable { /** 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 maxDigests) { if (uri == null) { throw new IllegalArgumentException(); } mDigestBytes = generateDigest(uri, maxDigests); mDigestPrefix = new int[mDigestBytes.length]; for (int i = 0; i < mDigestBytes.length; i++) { mDigestPrefix[i] = (mDigestBytes[i][0] & 0xFF) << 24 | (mDigestBytes[i][1] & 0xFF) << 16 | (mDigestBytes[i][2] & 0xFF) << 8 | (mDigestBytes[i][3] & 0xFF) << 0; } } private static byte[][] generateDigest(Uri uri, int maxDigests) { ArrayList<byte[]> digests = new ArrayList<>(); try { final String host = uri.getHost(); final MessageDigest digest = MessageDigest.getInstance(SHA_ALGORITHM); if (maxDigests <= 0) { final byte[] hostBytes = host.getBytes(); digests.add(digest.digest(hostBytes)); } else { int prevDot = host.lastIndexOf('.'); prevDot = host.lastIndexOf('.', prevDot - 1); // shortcut for short URLs if (prevDot < 0) { digests.add(digest.digest(host.getBytes())); } else { byte[] hostBytes = host.substring(prevDot + 1, host.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(); digests.add(digest.digest(hostBytes)); digestCount++; } } } } catch (NoSuchAlgorithmException e) { throw new IllegalStateException("could not find digest algorithm"); } return digests.toArray(new byte[digests.size()][]); } EphemeralDigest(Parcel in) { final int digestCount = in.readInt(); if (digestCount == -1) { mDigestBytes = null; } else { mDigestBytes = new byte[digestCount][]; for (int i = 0; i < digestCount; i++) { mDigestBytes[i] = in.createByteArray(); } } mDigestPrefix = in.createIntArray(); } public byte[][] getDigestBytes() { return mDigestBytes; } public int[] getDigestPrefix() { return mDigestPrefix; } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel out, int flags) { if (mDigestBytes == null) { out.writeInt(-1); } else { out.writeInt(mDigestBytes.length); for (int i = 0; i < mDigestBytes.length; i++) { out.writeByteArray(mDigestBytes[i]); } } out.writeIntArray(mDigestPrefix); } @SuppressWarnings("hiding") public static final Parcelable.Creator<EphemeralDigest> CREATOR = new Parcelable.Creator<EphemeralDigest>() { public EphemeralDigest createFromParcel(Parcel in) { return new EphemeralDigest(in); } public EphemeralDigest[] newArray(int size) { return new EphemeralDigest[size]; } }; } } core/java/android/provider/Settings.java +18 −0 Original line number Diff line number Diff line Loading @@ -8582,6 +8582,24 @@ public final class Settings { public static final String EPHEMERAL_COOKIE_MAX_SIZE_BYTES = "ephemeral_cookie_max_size_bytes"; /** * 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> Loading core/java/com/android/internal/app/EphemeralResolverService.java +15 −6 Original line number Diff line number Diff line Loading @@ -39,14 +39,19 @@ import java.util.List; public abstract class EphemeralResolverService extends Service { public static final String EXTRA_RESOLVE_INFO = "com.android.internal.app.RESOLVE_INFO"; public static final String EXTRA_SEQUENCE = "com.android.internal.app.SEQUENCE"; private static final String EXTRA_PREFIX = "com.android.internal.app.PREFIX"; private Handler mHandler; /** * Called to retrieve resolve info for ephemeral applications. * * @param digestPrefix The hash prefix of the ephemeral's domain. * @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. */ protected abstract List<EphemeralResolveInfo> getEphemeralResolveInfoList(int digestPrefix); protected abstract List<EphemeralResolveInfo> getEphemeralResolveInfoList( int digestPrefix[], int prefixMask); @Override protected final void attachBaseContext(Context base) { Loading @@ -59,10 +64,13 @@ public abstract class EphemeralResolverService extends Service { return new IEphemeralResolver.Stub() { @Override public void getEphemeralResolveInfoList( IRemoteCallback callback, int digestPrefix, int sequence) { mHandler.obtainMessage(ServiceHandler.MSG_GET_EPHEMERAL_RESOLVE_INFO, digestPrefix, sequence, callback) .sendToTarget(); IRemoteCallback callback, int digestPrefix[], int prefixMask, int sequence) { final Message msg = mHandler.obtainMessage( ServiceHandler.MSG_GET_EPHEMERAL_RESOLVE_INFO, prefixMask, sequence, callback); final Bundle data = new Bundle(); data.putIntArray(EXTRA_PREFIX, digestPrefix); msg.setData(data); msg.sendToTarget(); } }; } Loading @@ -81,8 +89,9 @@ public abstract class EphemeralResolverService extends Service { switch (action) { case MSG_GET_EPHEMERAL_RESOLVE_INFO: { final IRemoteCallback callback = (IRemoteCallback) message.obj; final int[] digestPrefix = message.getData().getIntArray(EXTRA_PREFIX); final List<EphemeralResolveInfo> resolveInfo = getEphemeralResolveInfoList(message.arg1); getEphemeralResolveInfoList(digestPrefix, message.arg1); final Bundle data = new Bundle(); data.putInt(EXTRA_SEQUENCE, message.arg2); data.putParcelableList(EXTRA_RESOLVE_INFO, resolveInfo); Loading core/java/com/android/internal/app/IEphemeralResolver.aidl +2 −1 Original line number Diff line number Diff line Loading @@ -20,5 +20,6 @@ import android.content.Intent; import android.os.IRemoteCallback; oneway interface IEphemeralResolver { void getEphemeralResolveInfoList(IRemoteCallback callback, int digestPrefix, int sequence); void getEphemeralResolveInfoList(IRemoteCallback callback, in int[] digestPrefix, int prefixMask, int sequence); } services/core/java/com/android/server/pm/EphemeralResolverConnection.java +5 −4 Original line number Diff line number Diff line Loading @@ -64,11 +64,12 @@ final class EphemeralResolverConnection { mIntent = new Intent().setComponent(componentName); } public final List<EphemeralResolveInfo> getEphemeralResolveInfoList(int hashPrefix) { public final List<EphemeralResolveInfo> getEphemeralResolveInfoList( int hashPrefix[], int prefixMask) { throwIfCalledOnMainThread(); try { return mGetEphemeralResolveInfoCaller.getEphemeralResolveInfoList( getRemoteInstanceLazy(), hashPrefix); getRemoteInstanceLazy(), hashPrefix, prefixMask); } catch (RemoteException re) { } catch (TimeoutException te) { } finally { Loading Loading @@ -177,10 +178,10 @@ final class EphemeralResolverConnection { } public List<EphemeralResolveInfo> getEphemeralResolveInfoList( IEphemeralResolver target, int hashPrefix) IEphemeralResolver target, int hashPrefix[], int prefixMask) throws RemoteException, TimeoutException { final int sequence = onBeforeRemoteCall(); target.getEphemeralResolveInfoList(mCallback, hashPrefix, sequence); target.getEphemeralResolveInfoList(mCallback, hashPrefix, prefixMask, sequence); return getResultTimed(sequence); } } Loading Loading
core/java/android/content/pm/EphemeralResolveInfo.java +122 −26 Original line number Diff line number Diff line Loading @@ -37,10 +37,7 @@ public final class EphemeralResolveInfo implements Parcelable { /** Algorithm that will be used to generate the domain digest */ public static final String SHA_ALGORITHM = "SHA-256"; /** Full digest of the domain hash */ private final byte[] mDigestBytes; /** The first 4 bytes of the domain hash */ private final int mDigestPrefix; private final EphemeralDigest mDigest; private final String mPackageName; /** The filters used to match domain */ private final List<IntentFilter> mFilters = new ArrayList<IntentFilter>(); Loading @@ -55,29 +52,23 @@ public final class EphemeralResolveInfo implements Parcelable { throw new IllegalArgumentException(); } mDigestBytes = generateDigest(uri); mDigestPrefix = (mDigestBytes[0] & 0xFF) << 24 | (mDigestBytes[1] & 0xFF) << 16 | (mDigestBytes[2] & 0xFF) << 8 | (mDigestBytes[3] & 0xFF) << 0; mDigest = new EphemeralDigest(uri, -1); mFilters.addAll(filters); mPackageName = packageName; } EphemeralResolveInfo(Parcel in) { mDigestBytes = in.createByteArray(); mDigestPrefix = in.readInt(); mDigest = in.readParcelable(null /*loader*/); mPackageName = in.readString(); in.readList(mFilters, null /*loader*/); } public byte[] getDigestBytes() { return mDigestBytes; return mDigest.getDigestBytes()[0]; } public int getDigestPrefix() { return mDigestPrefix; return mDigest.getDigestPrefix()[0]; } public String getPackageName() { Loading @@ -88,16 +79,6 @@ public final class EphemeralResolveInfo implements Parcelable { return mFilters; } private static byte[] generateDigest(Uri uri) { try { final MessageDigest digest = MessageDigest.getInstance(SHA_ALGORITHM); final byte[] hostBytes = uri.getHost().getBytes(); return digest.digest(hostBytes); } catch (NoSuchAlgorithmException e) { throw new IllegalStateException("could not find digest algorithm"); } } @Override public int describeContents() { return 0; Loading @@ -105,8 +86,7 @@ public final class EphemeralResolveInfo implements Parcelable { @Override public void writeToParcel(Parcel out, int flags) { out.writeByteArray(mDigestBytes); out.writeInt(mDigestPrefix); out.writeParcelable(mDigest, flags); out.writeString(mPackageName); out.writeList(mFilters); } Loading Loading @@ -136,4 +116,120 @@ public final class EphemeralResolveInfo implements Parcelable { return mResolveInfo; } } /** * Helper class to generate and store each of the digests and prefixes * sent to the Ephemeral Resolver. * <p> * Since intent filters may want to handle multiple hosts within a * domain [eg “*.google.com”], the resolver is presented with multiple * hash prefixes. For example, "a.b.c.d.e" generates digests for * "d.e", "c.d.e", "b.c.d.e" and "a.b.c.d.e". * * @hide */ public static final class EphemeralDigest implements Parcelable { /** 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 maxDigests) { if (uri == null) { throw new IllegalArgumentException(); } mDigestBytes = generateDigest(uri, maxDigests); mDigestPrefix = new int[mDigestBytes.length]; for (int i = 0; i < mDigestBytes.length; i++) { mDigestPrefix[i] = (mDigestBytes[i][0] & 0xFF) << 24 | (mDigestBytes[i][1] & 0xFF) << 16 | (mDigestBytes[i][2] & 0xFF) << 8 | (mDigestBytes[i][3] & 0xFF) << 0; } } private static byte[][] generateDigest(Uri uri, int maxDigests) { ArrayList<byte[]> digests = new ArrayList<>(); try { final String host = uri.getHost(); final MessageDigest digest = MessageDigest.getInstance(SHA_ALGORITHM); if (maxDigests <= 0) { final byte[] hostBytes = host.getBytes(); digests.add(digest.digest(hostBytes)); } else { int prevDot = host.lastIndexOf('.'); prevDot = host.lastIndexOf('.', prevDot - 1); // shortcut for short URLs if (prevDot < 0) { digests.add(digest.digest(host.getBytes())); } else { byte[] hostBytes = host.substring(prevDot + 1, host.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(); digests.add(digest.digest(hostBytes)); digestCount++; } } } } catch (NoSuchAlgorithmException e) { throw new IllegalStateException("could not find digest algorithm"); } return digests.toArray(new byte[digests.size()][]); } EphemeralDigest(Parcel in) { final int digestCount = in.readInt(); if (digestCount == -1) { mDigestBytes = null; } else { mDigestBytes = new byte[digestCount][]; for (int i = 0; i < digestCount; i++) { mDigestBytes[i] = in.createByteArray(); } } mDigestPrefix = in.createIntArray(); } public byte[][] getDigestBytes() { return mDigestBytes; } public int[] getDigestPrefix() { return mDigestPrefix; } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel out, int flags) { if (mDigestBytes == null) { out.writeInt(-1); } else { out.writeInt(mDigestBytes.length); for (int i = 0; i < mDigestBytes.length; i++) { out.writeByteArray(mDigestBytes[i]); } } out.writeIntArray(mDigestPrefix); } @SuppressWarnings("hiding") public static final Parcelable.Creator<EphemeralDigest> CREATOR = new Parcelable.Creator<EphemeralDigest>() { public EphemeralDigest createFromParcel(Parcel in) { return new EphemeralDigest(in); } public EphemeralDigest[] newArray(int size) { return new EphemeralDigest[size]; } }; } }
core/java/android/provider/Settings.java +18 −0 Original line number Diff line number Diff line Loading @@ -8582,6 +8582,24 @@ public final class Settings { public static final String EPHEMERAL_COOKIE_MAX_SIZE_BYTES = "ephemeral_cookie_max_size_bytes"; /** * 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> Loading
core/java/com/android/internal/app/EphemeralResolverService.java +15 −6 Original line number Diff line number Diff line Loading @@ -39,14 +39,19 @@ import java.util.List; public abstract class EphemeralResolverService extends Service { public static final String EXTRA_RESOLVE_INFO = "com.android.internal.app.RESOLVE_INFO"; public static final String EXTRA_SEQUENCE = "com.android.internal.app.SEQUENCE"; private static final String EXTRA_PREFIX = "com.android.internal.app.PREFIX"; private Handler mHandler; /** * Called to retrieve resolve info for ephemeral applications. * * @param digestPrefix The hash prefix of the ephemeral's domain. * @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. */ protected abstract List<EphemeralResolveInfo> getEphemeralResolveInfoList(int digestPrefix); protected abstract List<EphemeralResolveInfo> getEphemeralResolveInfoList( int digestPrefix[], int prefixMask); @Override protected final void attachBaseContext(Context base) { Loading @@ -59,10 +64,13 @@ public abstract class EphemeralResolverService extends Service { return new IEphemeralResolver.Stub() { @Override public void getEphemeralResolveInfoList( IRemoteCallback callback, int digestPrefix, int sequence) { mHandler.obtainMessage(ServiceHandler.MSG_GET_EPHEMERAL_RESOLVE_INFO, digestPrefix, sequence, callback) .sendToTarget(); IRemoteCallback callback, int digestPrefix[], int prefixMask, int sequence) { final Message msg = mHandler.obtainMessage( ServiceHandler.MSG_GET_EPHEMERAL_RESOLVE_INFO, prefixMask, sequence, callback); final Bundle data = new Bundle(); data.putIntArray(EXTRA_PREFIX, digestPrefix); msg.setData(data); msg.sendToTarget(); } }; } Loading @@ -81,8 +89,9 @@ public abstract class EphemeralResolverService extends Service { switch (action) { case MSG_GET_EPHEMERAL_RESOLVE_INFO: { final IRemoteCallback callback = (IRemoteCallback) message.obj; final int[] digestPrefix = message.getData().getIntArray(EXTRA_PREFIX); final List<EphemeralResolveInfo> resolveInfo = getEphemeralResolveInfoList(message.arg1); getEphemeralResolveInfoList(digestPrefix, message.arg1); final Bundle data = new Bundle(); data.putInt(EXTRA_SEQUENCE, message.arg2); data.putParcelableList(EXTRA_RESOLVE_INFO, resolveInfo); Loading
core/java/com/android/internal/app/IEphemeralResolver.aidl +2 −1 Original line number Diff line number Diff line Loading @@ -20,5 +20,6 @@ import android.content.Intent; import android.os.IRemoteCallback; oneway interface IEphemeralResolver { void getEphemeralResolveInfoList(IRemoteCallback callback, int digestPrefix, int sequence); void getEphemeralResolveInfoList(IRemoteCallback callback, in int[] digestPrefix, int prefixMask, int sequence); }
services/core/java/com/android/server/pm/EphemeralResolverConnection.java +5 −4 Original line number Diff line number Diff line Loading @@ -64,11 +64,12 @@ final class EphemeralResolverConnection { mIntent = new Intent().setComponent(componentName); } public final List<EphemeralResolveInfo> getEphemeralResolveInfoList(int hashPrefix) { public final List<EphemeralResolveInfo> getEphemeralResolveInfoList( int hashPrefix[], int prefixMask) { throwIfCalledOnMainThread(); try { return mGetEphemeralResolveInfoCaller.getEphemeralResolveInfoList( getRemoteInstanceLazy(), hashPrefix); getRemoteInstanceLazy(), hashPrefix, prefixMask); } catch (RemoteException re) { } catch (TimeoutException te) { } finally { Loading Loading @@ -177,10 +178,10 @@ final class EphemeralResolverConnection { } public List<EphemeralResolveInfo> getEphemeralResolveInfoList( IEphemeralResolver target, int hashPrefix) IEphemeralResolver target, int hashPrefix[], int prefixMask) throws RemoteException, TimeoutException { final int sequence = onBeforeRemoteCall(); target.getEphemeralResolveInfoList(mCallback, hashPrefix, sequence); target.getEphemeralResolveInfoList(mCallback, hashPrefix, prefixMask, sequence); return getResultTimed(sequence); } } Loading