From 1fb3404b2ba69a823e961bec2d9ed61622107052 Mon Sep 17 00:00:00 2001 From: Todd Kennedy Date: Wed, 1 Mar 2017 13:56:58 -0800 Subject: [PATCH] Refactor EphemeralResolverService * Change name to InstantAppResolverService * Left old service in place to handle existing client [to be removed prior to O launch] * When resolving phase II, return a list instead of a single item Bug: 34763730 Test: Build and verify resolution occurs w/ legacy & new resolver service Change-Id: Ieccaf91538bd91c04f4be4e35d8264619d7cd6d7 --- Android.mk | 1 + api/current.txt | 2 +- api/system-current.txt | 58 +++- api/test-current.txt | 2 +- .../android/app/EphemeralResolveInfo.aidl | 1 + .../android/app/EphemeralResolverService.java | 107 ++------ core/java/android/app/IEphemeralResolver.aidl | 2 +- .../java/android/app/IInstantAppResolver.aidl | 28 ++ .../android/app/InstantAppResolveInfo.aidl | 19 ++ .../app/InstantAppResolverService.java | 176 ++++++++++++ .../content/pm/AuxiliaryResolveInfo.java | 10 +- .../content/pm/EphemeralIntentFilter.java | 48 ++-- .../content/pm/EphemeralResolveInfo.java | 173 ++++-------- .../content/pm/InstantAppIntentFilter.java | 82 ++++++ ...ralRequest.java => InstantAppRequest.java} | 14 +- .../content/pm/InstantAppResolveInfo.java | 250 ++++++++++++++++++ .../android/server/am/ActivityStarter.java | 10 +- .../pm/EphemeralResolverConnection.java | 44 +-- ...lResolver.java => InstantAppResolver.java} | 116 ++++---- .../server/pm/PackageManagerService.java | 24 +- 20 files changed, 816 insertions(+), 351 deletions(-) create mode 100644 core/java/android/app/IInstantAppResolver.aidl create mode 100644 core/java/android/app/InstantAppResolveInfo.aidl create mode 100644 core/java/android/app/InstantAppResolverService.java create mode 100644 core/java/android/content/pm/InstantAppIntentFilter.java rename core/java/android/content/pm/{EphemeralRequest.java => InstantAppRequest.java} (72%) create mode 100644 core/java/android/content/pm/InstantAppResolveInfo.java rename services/core/java/com/android/server/pm/{EphemeralResolver.java => InstantAppResolver.java} (66%) diff --git a/Android.mk b/Android.mk index f544620b993f2..415ca983473ca 100644 --- a/Android.mk +++ b/Android.mk @@ -81,6 +81,7 @@ LOCAL_SRC_FILES += \ core/java/android/app/ITaskStackListener.aidl \ core/java/android/app/IBackupAgent.aidl \ core/java/android/app/IEphemeralResolver.aidl \ + core/java/android/app/IInstantAppResolver.aidl \ core/java/android/app/IInstrumentationWatcher.aidl \ core/java/android/app/INotificationManager.aidl \ core/java/android/app/IProcessObserver.aidl \ diff --git a/api/current.txt b/api/current.txt index 0872b2d7d7072..9a01f442b08f3 100644 --- a/api/current.txt +++ b/api/current.txt @@ -30125,12 +30125,12 @@ package android.os { field public static final int BATTERY_PLUGGED_AC = 1; // 0x1 field public static final int BATTERY_PLUGGED_USB = 2; // 0x2 field public static final int BATTERY_PLUGGED_WIRELESS = 4; // 0x4 - field public static final int BATTERY_PROPERTY_STATUS = 6; // 0x6 field public static final int BATTERY_PROPERTY_CAPACITY = 4; // 0x4 field public static final int BATTERY_PROPERTY_CHARGE_COUNTER = 1; // 0x1 field public static final int BATTERY_PROPERTY_CURRENT_AVERAGE = 3; // 0x3 field public static final int BATTERY_PROPERTY_CURRENT_NOW = 2; // 0x2 field public static final int BATTERY_PROPERTY_ENERGY_COUNTER = 5; // 0x5 + field public static final int BATTERY_PROPERTY_STATUS = 6; // 0x6 field public static final int BATTERY_STATUS_CHARGING = 2; // 0x2 field public static final int BATTERY_STATUS_DISCHARGING = 3; // 0x3 field public static final int BATTERY_STATUS_FULL = 5; // 0x5 diff --git a/api/system-current.txt b/api/system-current.txt index c321e08619c80..ceff622242daa 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -4658,16 +4658,12 @@ package android.app { field public static final int VISIBILITY_VISIBLE_NOTIFY_ONLY_COMPLETION = 3; // 0x3 } - public abstract class EphemeralResolverService extends android.app.Service { + public abstract deprecated class EphemeralResolverService extends android.app.InstantAppResolverService { ctor public EphemeralResolverService(); - method public final void attachBaseContext(android.content.Context); method public android.os.Looper getLooper(); - method public final android.os.IBinder onBind(android.content.Intent); method public abstract deprecated java.util.List onEphemeralResolveInfoList(int[], int); method public android.content.pm.EphemeralResolveInfo onGetEphemeralIntentFilter(java.lang.String); method public java.util.List 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"; } public class ExpandableListActivity extends android.app.Activity implements android.widget.ExpandableListView.OnChildClickListener android.widget.ExpandableListView.OnGroupCollapseListener android.widget.ExpandableListView.OnGroupExpandListener android.view.View.OnCreateContextMenuListener { @@ -4988,6 +4984,20 @@ package android.app { field public static final int TRANSIT_UNSET = -1; // 0xffffffff } + public abstract class InstantAppResolverService extends android.app.Service { + ctor public InstantAppResolverService(); + method public final void attachBaseContext(android.content.Context); + method public final android.os.IBinder onBind(android.content.Intent); + method public void onGetInstantAppIntentFilter(int[], android.app.InstantAppResolverService.InstantAppResolutionCallback); + method public void onGetInstantAppResolveInfo(int[], android.app.InstantAppResolverService.InstantAppResolutionCallback); + 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"; + } + + public static final class InstantAppResolverService.InstantAppResolutionCallback { + method public void onInstantAppResolveInfo(java.util.List); + } + public class Instrumentation { ctor public Instrumentation(); method public void addMonitor(android.app.Instrumentation.ActivityMonitor); @@ -10477,7 +10487,7 @@ package android.content.pm { field public int reqTouchScreen; } - public final class EphemeralIntentFilter implements android.os.Parcelable { + public final deprecated class EphemeralIntentFilter implements android.os.Parcelable { ctor public EphemeralIntentFilter(java.lang.String, java.util.List); method public int describeContents(); method public java.util.List getFilters(); @@ -10486,7 +10496,7 @@ package android.content.pm { field public static final android.os.Parcelable.Creator CREATOR; } - public final class EphemeralResolveInfo implements android.os.Parcelable { + public final deprecated class EphemeralResolveInfo implements android.os.Parcelable { ctor public deprecated EphemeralResolveInfo(android.net.Uri, java.lang.String, java.util.List); ctor public deprecated EphemeralResolveInfo(android.content.pm.EphemeralResolveInfo.EphemeralDigest, java.lang.String, java.util.List); ctor public EphemeralResolveInfo(android.content.pm.EphemeralResolveInfo.EphemeralDigest, java.lang.String, java.util.List, int); @@ -10536,6 +10546,38 @@ package android.content.pm { field public int version; } + public final class InstantAppIntentFilter implements android.os.Parcelable { + ctor public InstantAppIntentFilter(java.lang.String, java.util.List); + method public int describeContents(); + method public java.util.List getFilters(); + method public java.lang.String getSplitName(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator CREATOR; + } + + public final class InstantAppResolveInfo implements android.os.Parcelable { + ctor public InstantAppResolveInfo(android.content.pm.InstantAppResolveInfo.InstantAppDigest, java.lang.String, java.util.List, int); + ctor public InstantAppResolveInfo(java.lang.String, java.lang.String, java.util.List); + method public int describeContents(); + method public byte[] getDigestBytes(); + method public int getDigestPrefix(); + method public java.util.List getIntentFilters(); + method public java.lang.String getPackageName(); + method public int getVersionCode(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator CREATOR; + field public static final java.lang.String SHA_ALGORITHM = "SHA-256"; + } + + public static final class InstantAppResolveInfo.InstantAppDigest implements android.os.Parcelable { + ctor public InstantAppResolveInfo.InstantAppDigest(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 CREATOR; + } + public class InstrumentationInfo extends android.content.pm.PackageItemInfo implements android.os.Parcelable { ctor public InstrumentationInfo(); ctor public InstrumentationInfo(android.content.pm.InstrumentationInfo); @@ -32726,12 +32768,12 @@ package android.os { field public static final int BATTERY_PLUGGED_AC = 1; // 0x1 field public static final int BATTERY_PLUGGED_USB = 2; // 0x2 field public static final int BATTERY_PLUGGED_WIRELESS = 4; // 0x4 - field public static final int BATTERY_PROPERTY_STATUS = 6; // 0x6 field public static final int BATTERY_PROPERTY_CAPACITY = 4; // 0x4 field public static final int BATTERY_PROPERTY_CHARGE_COUNTER = 1; // 0x1 field public static final int BATTERY_PROPERTY_CURRENT_AVERAGE = 3; // 0x3 field public static final int BATTERY_PROPERTY_CURRENT_NOW = 2; // 0x2 field public static final int BATTERY_PROPERTY_ENERGY_COUNTER = 5; // 0x5 + field public static final int BATTERY_PROPERTY_STATUS = 6; // 0x6 field public static final int BATTERY_STATUS_CHARGING = 2; // 0x2 field public static final int BATTERY_STATUS_DISCHARGING = 3; // 0x3 field public static final int BATTERY_STATUS_FULL = 5; // 0x5 diff --git a/api/test-current.txt b/api/test-current.txt index 584903293669f..c0a0413dacba2 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -30221,12 +30221,12 @@ package android.os { field public static final int BATTERY_PLUGGED_AC = 1; // 0x1 field public static final int BATTERY_PLUGGED_USB = 2; // 0x2 field public static final int BATTERY_PLUGGED_WIRELESS = 4; // 0x4 - field public static final int BATTERY_PROPERTY_STATUS = 6; // 0x6 field public static final int BATTERY_PROPERTY_CAPACITY = 4; // 0x4 field public static final int BATTERY_PROPERTY_CHARGE_COUNTER = 1; // 0x1 field public static final int BATTERY_PROPERTY_CURRENT_AVERAGE = 3; // 0x3 field public static final int BATTERY_PROPERTY_CURRENT_NOW = 2; // 0x2 field public static final int BATTERY_PROPERTY_ENERGY_COUNTER = 5; // 0x5 + field public static final int BATTERY_PROPERTY_STATUS = 6; // 0x6 field public static final int BATTERY_STATUS_CHARGING = 2; // 0x2 field public static final int BATTERY_STATUS_DISCHARGING = 3; // 0x3 field public static final int BATTERY_STATUS_FULL = 5; // 0x5 diff --git a/core/java/android/app/EphemeralResolveInfo.aidl b/core/java/android/app/EphemeralResolveInfo.aidl index db71d250ade23..fe28081ee08b4 100644 --- a/core/java/android/app/EphemeralResolveInfo.aidl +++ b/core/java/android/app/EphemeralResolveInfo.aidl @@ -16,4 +16,5 @@ package android.app; +/** @deprecated */ parcelable EphemeralResolveInfo; diff --git a/core/java/android/app/EphemeralResolverService.java b/core/java/android/app/EphemeralResolverService.java index 45652ef7c37dc..445d3bd237b81 100644 --- a/core/java/android/app/EphemeralResolverService.java +++ b/core/java/android/app/EphemeralResolverService.java @@ -18,9 +18,11 @@ package android.app; import android.annotation.SystemApi; import android.app.Service; +import android.app.InstantAppResolverService.InstantAppResolutionCallback; import android.content.Context; import android.content.Intent; import android.content.pm.EphemeralResolveInfo; +import android.content.pm.InstantAppResolveInfo; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; @@ -29,20 +31,17 @@ import android.os.Looper; import android.os.Message; import android.os.RemoteException; +import java.util.ArrayList; import java.util.List; /** * Base class for implementing the resolver service. * @hide + * @deprecated use InstantAppResolverService instead */ +@Deprecated @SystemApi -public abstract class EphemeralResolverService extends Service { - 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; - +public abstract class EphemeralResolverService extends InstantAppResolverService { /** * Called to retrieve resolve info for ephemeral applications. * @@ -74,90 +73,28 @@ public abstract class EphemeralResolverService extends Service { throw new IllegalStateException("Must define"); } - /** - * Returns a {@link Looper} to perform service operations on. - */ + @Override public Looper getLooper() { - return getBaseContext().getMainLooper(); + return super.getLooper(); } @Override - public final void attachBaseContext(Context base) { - super.attachBaseContext(base); - mHandler = new ServiceHandler(getLooper()); + void _onGetInstantAppResolveInfo(int[] digestPrefix, InstantAppResolutionCallback callback) { + final List response = onGetEphemeralResolveInfo(digestPrefix); + final int responseSize = response == null ? 0 : response.size(); + final List resultList = new ArrayList<>(responseSize); + for (int i = 0; i < responseSize; i++) { + resultList.add(response.get(i).getInstantAppResolveInfo()); + } + callback.onInstantAppResolveInfo(resultList); } @Override - public final IBinder onBind(Intent intent) { - return new IEphemeralResolver.Stub() { - @Override - public void getEphemeralResolveInfoList( - IRemoteCallback callback, int digestPrefix[], int sequence) { - final Message msg = mHandler.obtainMessage( - 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, String hostName, int sequence) { - final Message msg = mHandler.obtainMessage( - ServiceHandler.MSG_GET_EPHEMERAL_INTENT_FILTER, sequence, 0, callback); - final Bundle data = new Bundle(); - data.putString(EXTRA_HOSTNAME, hostName); - msg.setData(data); - msg.sendToTarget(); - } - }; - } - - 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*/); - } - - @Override - @SuppressWarnings("unchecked") - public void handleMessage(Message message) { - final int action = message.what; - switch (action) { - case MSG_GET_EPHEMERAL_RESOLVE_INFO: { - final IRemoteCallback callback = (IRemoteCallback) message.obj; - final int[] digestPrefix = message.getData().getIntArray(EXTRA_PREFIX); - final List resolveInfo = - onGetEphemeralResolveInfo(digestPrefix); - final Bundle data = new Bundle(); - 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 String hostName = message.getData().getString(EXTRA_HOSTNAME); - final EphemeralResolveInfo resolveInfo = onGetEphemeralIntentFilter(hostName); - final Bundle data = new Bundle(); - data.putInt(EXTRA_SEQUENCE, message.arg1); - data.putParcelable(EXTRA_RESOLVE_INFO, resolveInfo); - try { - callback.sendResult(data); - } catch (RemoteException e) { - } - } break; - - default: { - throw new IllegalArgumentException("Unknown message: " + action); - } - } - } + void _onGetInstantAppIntentFilter(int[] digestPrefix, String hostName, + InstantAppResolutionCallback callback) { + final EphemeralResolveInfo response = onGetEphemeralIntentFilter(hostName); + final List resultList = new ArrayList<>(1); + resultList.add(response.getInstantAppResolveInfo()); + callback.onInstantAppResolveInfo(resultList); } } diff --git a/core/java/android/app/IEphemeralResolver.aidl b/core/java/android/app/IEphemeralResolver.aidl index 260b80c7d6340..734ad9ed83bae 100644 --- a/core/java/android/app/IEphemeralResolver.aidl +++ b/core/java/android/app/IEphemeralResolver.aidl @@ -18,7 +18,7 @@ package android.app; import android.os.IRemoteCallback; -/** @hide */ +/** @hide @deprecated */ oneway interface IEphemeralResolver { void getEphemeralResolveInfoList(IRemoteCallback callback, in int[] digestPrefix, int sequence); diff --git a/core/java/android/app/IInstantAppResolver.aidl b/core/java/android/app/IInstantAppResolver.aidl new file mode 100644 index 0000000000000..04e321f89c88f --- /dev/null +++ b/core/java/android/app/IInstantAppResolver.aidl @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2017 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.app; + +import android.os.IRemoteCallback; + +/** @hide */ +oneway interface IInstantAppResolver { + void getInstantAppResolveInfoList(in int[] digestPrefix, + int sequence, IRemoteCallback callback); + + void getInstantAppIntentFilterList(in int[] digestPrefix, + int sequence, String hostName, IRemoteCallback callback); +} diff --git a/core/java/android/app/InstantAppResolveInfo.aidl b/core/java/android/app/InstantAppResolveInfo.aidl new file mode 100644 index 0000000000000..440e5422a45cd --- /dev/null +++ b/core/java/android/app/InstantAppResolveInfo.aidl @@ -0,0 +1,19 @@ +/* +** Copyright 2017, 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.app; + +parcelable InstantAppResolveInfo; diff --git a/core/java/android/app/InstantAppResolverService.java b/core/java/android/app/InstantAppResolverService.java new file mode 100644 index 0000000000000..1ce30b258a540 --- /dev/null +++ b/core/java/android/app/InstantAppResolverService.java @@ -0,0 +1,176 @@ +/* + * Copyright (C) 2017 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.app; + +import android.annotation.SystemApi; +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.content.pm.InstantAppResolveInfo; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.IRemoteCallback; +import android.os.Looper; +import android.os.Message; +import android.os.RemoteException; + +import java.util.List; + +/** + * Base class for implementing the resolver service. + * @hide + */ +@SystemApi +public abstract class InstantAppResolverService extends Service { + public static final String EXTRA_RESOLVE_INFO = "android.app.extra.RESOLVE_INFO"; + public static final String EXTRA_SEQUENCE = "android.app.extra.SEQUENCE"; + static final String EXTRA_PREFIX = "android.app.PREFIX"; + static final String EXTRA_HOSTNAME = "android.app.HOSTNAME"; + Handler mHandler; + + /** + * Called to retrieve resolve info for instant applications. + * + * @param digestPrefix The hash prefix of the instant app's domain. + */ + public void onGetInstantAppResolveInfo( + int digestPrefix[], InstantAppResolutionCallback callback) { + throw new IllegalStateException("Must define"); + } + + /** + * Called to retrieve intent filters for instant applications. + * + * @param digestPrefix The hash prefix of the instant app's domain. + */ + public void onGetInstantAppIntentFilter( + int digestPrefix[], InstantAppResolutionCallback callback) { + throw new IllegalStateException("Must define"); + } + + /** + * Returns a {@link Looper} to perform service operations on. + */ + Looper getLooper() { + return getBaseContext().getMainLooper(); + } + + @Override + public final void attachBaseContext(Context base) { + super.attachBaseContext(base); + mHandler = new ServiceHandler(getLooper()); + } + + @Override + public final IBinder onBind(Intent intent) { + return new IInstantAppResolver.Stub() { + @Override + public void getInstantAppResolveInfoList( + int digestPrefix[], int sequence, IRemoteCallback callback) { + final Message msg = mHandler.obtainMessage( + ServiceHandler.MSG_GET_INSTANT_APP_RESOLVE_INFO, sequence, 0, callback); + final Bundle data = new Bundle(); + data.putIntArray(EXTRA_PREFIX, digestPrefix); + msg.setData(data); + msg.sendToTarget(); + } + + @Override + public void getInstantAppIntentFilterList( + int digestPrefix[], int sequence, String hostName, IRemoteCallback callback) { + final Message msg = mHandler.obtainMessage( + ServiceHandler.MSG_GET_INSTANT_APP_INTENT_FILTER, sequence, 0, callback); + final Bundle data = new Bundle(); + data.putString(EXTRA_HOSTNAME, hostName); + data.putIntArray(EXTRA_PREFIX, digestPrefix); + msg.setData(data); + msg.sendToTarget(); + } + }; + } + + /** + * Callback to post results from instant app resolution. + */ + public static final class InstantAppResolutionCallback { + private final IRemoteCallback mCallback; + private final int mSequence; + InstantAppResolutionCallback(int sequence, IRemoteCallback callback) { + mCallback = callback; + mSequence = sequence; + } + + public void onInstantAppResolveInfo(List resolveInfo) { + final Bundle data = new Bundle(); + data.putInt(EXTRA_SEQUENCE, mSequence); + data.putParcelableList(EXTRA_RESOLVE_INFO, resolveInfo); + try { + mCallback.sendResult(data); + } catch (RemoteException e) { + } + } + } + + @Deprecated + void _onGetInstantAppResolveInfo(int[] digestPrefix, InstantAppResolutionCallback callback) { + onGetInstantAppResolveInfo(digestPrefix, callback); + } + @Deprecated + void _onGetInstantAppIntentFilter(int digestPrefix[], String hostName, + InstantAppResolutionCallback callback) { + onGetInstantAppIntentFilter(digestPrefix, callback); + } + + private final class ServiceHandler extends Handler { + public static final int MSG_GET_INSTANT_APP_RESOLVE_INFO = 1; + public static final int MSG_GET_INSTANT_APP_INTENT_FILTER = 2; + + public ServiceHandler(Looper looper) { + super(looper, null /*callback*/, true /*async*/); + } + + @Override + @SuppressWarnings("unchecked") + public void handleMessage(Message message) { + final int action = message.what; + switch (action) { + case MSG_GET_INSTANT_APP_RESOLVE_INFO: { + final IRemoteCallback callback = (IRemoteCallback) message.obj; + final int sequence = message.arg1; + final int[] digestPrefix = message.getData().getIntArray(EXTRA_PREFIX); + _onGetInstantAppResolveInfo( + digestPrefix, new InstantAppResolutionCallback(sequence, callback)); + } break; + + case MSG_GET_INSTANT_APP_INTENT_FILTER: { + final IRemoteCallback callback = (IRemoteCallback) message.obj; + final int sequence = message.arg1; + final int[] digestPrefix = message.getData().getIntArray(EXTRA_PREFIX); + final String hostName = message.getData().getString(EXTRA_HOSTNAME); + _onGetInstantAppIntentFilter( + digestPrefix, hostName, + new InstantAppResolutionCallback(sequence, callback)); + } break; + + default: { + throw new IllegalArgumentException("Unknown message: " + action); + } + } + } + } +} diff --git a/core/java/android/content/pm/AuxiliaryResolveInfo.java b/core/java/android/content/pm/AuxiliaryResolveInfo.java index 7d7784a185bf0..69bfeca403e12 100644 --- a/core/java/android/content/pm/AuxiliaryResolveInfo.java +++ b/core/java/android/content/pm/AuxiliaryResolveInfo.java @@ -31,21 +31,21 @@ import android.content.IntentFilter; * @hide */ public final class AuxiliaryResolveInfo extends IntentFilter { - /** Resolved information returned from the external ephemeral resolver */ - public final EphemeralResolveInfo resolveInfo; + /** Resolved information returned from the external instant resolver */ + public final InstantAppResolveInfo 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 */ + /** Whether or not instant resolution needs the second phase */ public final boolean needsPhaseTwo; - /** Opaque token to track the ephemeral application resolution */ + /** Opaque token to track the instant application resolution */ public final String token; /** The version code of the package */ public final int versionCode; /** Create a response for installing an instant application. */ - public AuxiliaryResolveInfo(@NonNull EphemeralResolveInfo resolveInfo, + public AuxiliaryResolveInfo(@NonNull InstantAppResolveInfo resolveInfo, @NonNull IntentFilter orig, @Nullable String splitName, @NonNull String token, diff --git a/core/java/android/content/pm/EphemeralIntentFilter.java b/core/java/android/content/pm/EphemeralIntentFilter.java index 0674e7c216315..f7d83a9e8d6e9 100644 --- a/core/java/android/content/pm/EphemeralIntentFilter.java +++ b/core/java/android/content/pm/EphemeralIntentFilter.java @@ -30,31 +30,34 @@ import java.util.List; * Information about an ephemeral application intent filter. * @hide */ +@Deprecated @SystemApi public final class EphemeralIntentFilter implements Parcelable { - private final String mSplitName; - /** The filters used to match domain */ - private final List mFilters = new ArrayList(); + private final InstantAppIntentFilter mInstantAppIntentFilter; public EphemeralIntentFilter(@Nullable String splitName, @NonNull List filters) { - if (filters == null || filters.size() == 0) { - throw new IllegalArgumentException(); - } - mSplitName = splitName; - mFilters.addAll(filters); + mInstantAppIntentFilter = new InstantAppIntentFilter(splitName, filters); + } + + EphemeralIntentFilter(@NonNull InstantAppIntentFilter intentFilter) { + mInstantAppIntentFilter = intentFilter; } EphemeralIntentFilter(Parcel in) { - mSplitName = in.readString(); - in.readList(mFilters, null /*loader*/); + mInstantAppIntentFilter = in.readParcelable(null /*loader*/); } public String getSplitName() { - return mSplitName; + return mInstantAppIntentFilter.getSplitName(); } public List getFilters() { - return mFilters; + return mInstantAppIntentFilter.getFilters(); + } + + /** @hide */ + InstantAppIntentFilter getInstantAppIntentFilter() { + return mInstantAppIntentFilter; } @Override @@ -64,33 +67,18 @@ public final class EphemeralIntentFilter implements Parcelable { @Override public void writeToParcel(Parcel out, int flags) { - out.writeString(mSplitName); - out.writeList(mFilters); + out.writeParcelable(mInstantAppIntentFilter, flags); } public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { + @Override public EphemeralIntentFilter createFromParcel(Parcel in) { return new EphemeralIntentFilter(in); } - + @Override 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 1d7b8f28453c3..a6e826826511e 100644 --- a/core/java/android/content/pm/EphemeralResolveInfo.java +++ b/core/java/android/content/pm/EphemeralResolveInfo.java @@ -20,6 +20,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; import android.content.IntentFilter; +import android.content.pm.InstantAppResolveInfo.InstantAppDigest; import android.net.Uri; import android.os.Parcel; import android.os.Parcelable; @@ -34,18 +35,13 @@ import java.util.Locale; * Information about an ephemeral application. * @hide */ +@Deprecated @SystemApi public final class EphemeralResolveInfo implements Parcelable { /** Algorithm that will be used to generate the domain digest */ public static final String SHA_ALGORITHM = "SHA-256"; - private final EphemeralDigest mDigest; - private final String mPackageName; - /** The filters used to match domain */ - private final List mFilters; - /** The version code of the app that this class resolves to */ - private final int mVersionCode; - /** Filters only for legacy clients */ + private final InstantAppResolveInfo mInstantAppResolveInfo; @Deprecated private final List mLegacyFilters; @@ -55,13 +51,12 @@ public final class EphemeralResolveInfo implements Parcelable { if (uri == null || packageName == null || filters == null || filters.isEmpty()) { throw new IllegalArgumentException(); } - mDigest = new EphemeralDigest(uri.getHost()); - mPackageName = packageName; - mFilters = new ArrayList(); - mFilters.add(new EphemeralIntentFilter(packageName, filters)); + final List ephemeralFilters = new ArrayList<>(1); + ephemeralFilters.add(new EphemeralIntentFilter(packageName, filters)); + mInstantAppResolveInfo = new InstantAppResolveInfo(uri.getHost(), packageName, + createInstantAppIntentFilterList(ephemeralFilters)); mLegacyFilters = new ArrayList(filters.size()); mLegacyFilters.addAll(filters); - mVersionCode = -1; } @Deprecated @@ -71,22 +66,11 @@ public final class EphemeralResolveInfo implements Parcelable { } public EphemeralResolveInfo(@NonNull EphemeralDigest digest, @Nullable String packageName, - @Nullable List filters, int versionConde) { - // 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(filters.size()); - mFilters.addAll(filters); - } else { - mFilters = null; - } + @Nullable List filters, int versionCode) { + mInstantAppResolveInfo = new InstantAppResolveInfo( + digest.getInstantAppDigest(), packageName, + createInstantAppIntentFilterList(filters), versionCode); mLegacyFilters = null; - mPackageName = packageName; - mVersionCode = versionConde; } public EphemeralResolveInfo(@NonNull String hostName, @Nullable String packageName, @@ -95,33 +79,53 @@ public final class EphemeralResolveInfo implements Parcelable { } EphemeralResolveInfo(Parcel in) { - mDigest = in.readParcelable(null /*loader*/); - mPackageName = in.readString(); - mFilters = new ArrayList(); - in.readList(mFilters, null /*loader*/); - mVersionCode = in.readInt(); + mInstantAppResolveInfo = in.readParcelable(null /*loader*/); mLegacyFilters = new ArrayList(); in.readList(mLegacyFilters, null /*loader*/); } + /** @hide */ + public InstantAppResolveInfo getInstantAppResolveInfo() { + return mInstantAppResolveInfo; + } + + private static List createInstantAppIntentFilterList( + List filters) { + if (filters == null) { + return null; + } + final int filterCount = filters.size(); + final List returnList = new ArrayList<>(filterCount); + for (int i = 0; i < filterCount; i++) { + returnList.add(filters.get(i).getInstantAppIntentFilter()); + } + return returnList; + } + public byte[] getDigestBytes() { - return mDigest.getDigestBytes()[0]; + return mInstantAppResolveInfo.getDigestBytes(); } public int getDigestPrefix() { - return mDigest.getDigestPrefix()[0]; + return mInstantAppResolveInfo.getDigestPrefix(); } public String getPackageName() { - return mPackageName; + return mInstantAppResolveInfo.getPackageName(); } public List getIntentFilters() { - return mFilters; + final List filters = mInstantAppResolveInfo.getIntentFilters(); + final int filterCount = filters.size(); + final List returnList = new ArrayList<>(filterCount); + for (int i = 0; i < filterCount; i++) { + returnList.add(new EphemeralIntentFilter(filters.get(i))); + } + return returnList; } public int getVersionCode() { - return mVersionCode; + return mInstantAppResolveInfo.getVersionCode(); } @Deprecated @@ -136,19 +140,17 @@ public final class EphemeralResolveInfo implements Parcelable { @Override public void writeToParcel(Parcel out, int flags) { - out.writeParcelable(mDigest, flags); - out.writeString(mPackageName); - out.writeList(mFilters); - out.writeInt(mVersionCode); + out.writeParcelable(mInstantAppResolveInfo, flags); out.writeList(mLegacyFilters); } public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { + @Override public EphemeralResolveInfo createFromParcel(Parcel in) { return new EphemeralResolveInfo(in); } - + @Override public EphemeralResolveInfo[] newArray(int size) { return new EphemeralResolveInfo[size]; } @@ -167,12 +169,7 @@ public final class EphemeralResolveInfo implements Parcelable { */ @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; + private final InstantAppDigest mInstantAppDigest; public EphemeralDigest(@NonNull String hostName) { this(hostName, -1 /*maxDigests*/); @@ -180,73 +177,24 @@ public final class EphemeralResolveInfo implements Parcelable { /** @hide */ public EphemeralDigest(@NonNull String hostName, int maxDigests) { - if (hostName == null) { - throw new IllegalArgumentException(); - } - mDigestBytes = generateDigest(hostName.toLowerCase(Locale.ENGLISH), 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) - & DIGEST_MASK; - } - } - - private static byte[][] generateDigest(String hostName, int maxDigests) { - ArrayList digests = new ArrayList<>(); - try { - final MessageDigest digest = MessageDigest.getInstance(SHA_ALGORITHM); - if (maxDigests <= 0) { - final byte[] hostBytes = hostName.getBytes(); - digests.add(digest.digest(hostBytes)); - } else { - int prevDot = hostName.lastIndexOf('.'); - prevDot = hostName.lastIndexOf('.', prevDot - 1); - // shortcut for short URLs - if (prevDot < 0) { - digests.add(digest.digest(hostName.getBytes())); - } else { - byte[] hostBytes = - hostName.substring(prevDot + 1, hostName.length()).getBytes(); - digests.add(digest.digest(hostBytes)); - int digestCount = 1; - while (prevDot >= 0 && digestCount < maxDigests) { - prevDot = hostName.lastIndexOf('.', prevDot - 1); - hostBytes = - hostName.substring(prevDot + 1, hostName.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()][]); + mInstantAppDigest = new InstantAppDigest(hostName, maxDigests); } 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(); + mInstantAppDigest = in.readParcelable(null /*loader*/); + } + + /** @hide */ + InstantAppDigest getInstantAppDigest() { + return mInstantAppDigest; } public byte[][] getDigestBytes() { - return mDigestBytes; + return mInstantAppDigest.getDigestBytes(); } public int[] getDigestPrefix() { - return mDigestPrefix; + return mInstantAppDigest.getDigestPrefix(); } @Override @@ -256,24 +204,17 @@ public final class EphemeralResolveInfo implements Parcelable { @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); + out.writeParcelable(mInstantAppDigest, flags); } @SuppressWarnings("hiding") public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { + @Override public EphemeralDigest createFromParcel(Parcel in) { return new EphemeralDigest(in); } - + @Override public EphemeralDigest[] newArray(int size) { return new EphemeralDigest[size]; } diff --git a/core/java/android/content/pm/InstantAppIntentFilter.java b/core/java/android/content/pm/InstantAppIntentFilter.java new file mode 100644 index 0000000000000..257ab9675b819 --- /dev/null +++ b/core/java/android/content/pm/InstantAppIntentFilter.java @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2017 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 instant application intent filter. + * @hide + */ +@SystemApi +public final class InstantAppIntentFilter implements Parcelable { + private final String mSplitName; + /** The filters used to match domain */ + private final List mFilters = new ArrayList(); + + public InstantAppIntentFilter(@Nullable String splitName, @NonNull List filters) { + if (filters == null || filters.size() == 0) { + throw new IllegalArgumentException(); + } + mSplitName = splitName; + mFilters.addAll(filters); + } + + InstantAppIntentFilter(Parcel in) { + mSplitName = in.readString(); + in.readList(mFilters, null /*loader*/); + } + + public String getSplitName() { + return mSplitName; + } + + public List 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 CREATOR + = new Parcelable.Creator() { + @Override + public InstantAppIntentFilter createFromParcel(Parcel in) { + return new InstantAppIntentFilter(in); + } + @Override + public InstantAppIntentFilter[] newArray(int size) { + return new InstantAppIntentFilter[size]; + } + }; +} diff --git a/core/java/android/content/pm/EphemeralRequest.java b/core/java/android/content/pm/InstantAppRequest.java similarity index 72% rename from core/java/android/content/pm/EphemeralRequest.java rename to core/java/android/content/pm/InstantAppRequest.java index 58099c2b9b6a5..b45169d324be6 100644 --- a/core/java/android/content/pm/EphemeralRequest.java +++ b/core/java/android/content/pm/InstantAppRequest.java @@ -19,22 +19,22 @@ package android.content.pm; import android.content.Intent; /** - * Information needed to make an ephemeral application resolution request. + * Information needed to make an instant application resolution request. * @hide */ -public final class EphemeralRequest { - /** Response from the first phase of ephemeral application resolution */ +public final class InstantAppRequest { + /** Response from the first phase of instant application resolution */ public final AuxiliaryResolveInfo responseObj; - /** The original intent that triggered ephemeral application resolution */ + /** The original intent that triggered instant application resolution */ public final Intent origIntent; /** Resolved type of the intent */ public final String resolvedType; - /** The name of the package requesting the ephemeral application */ + /** The name of the package requesting the instant application */ public final String callingPackage; - /** ID of the user requesting the ephemeral application */ + /** ID of the user requesting the instant application */ public final int userId; - public EphemeralRequest(AuxiliaryResolveInfo responseObj, Intent origIntent, + public InstantAppRequest(AuxiliaryResolveInfo responseObj, Intent origIntent, String resolvedType, String callingPackage, int userId) { this.responseObj = responseObj; this.origIntent = origIntent; diff --git a/core/java/android/content/pm/InstantAppResolveInfo.java b/core/java/android/content/pm/InstantAppResolveInfo.java new file mode 100644 index 0000000000000..603192a51af76 --- /dev/null +++ b/core/java/android/content/pm/InstantAppResolveInfo.java @@ -0,0 +1,250 @@ +/* + * Copyright (C) 2017 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.net.Uri; +import android.os.Parcel; +import android.os.Parcelable; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; + +/** + * Information about an instant application. + * @hide + */ +@SystemApi +public final class InstantAppResolveInfo implements Parcelable { + /** Algorithm that will be used to generate the domain digest */ + public static final String SHA_ALGORITHM = "SHA-256"; + + private final InstantAppDigest mDigest; + private final String mPackageName; + /** The filters used to match domain */ + private final List mFilters; + /** The version code of the app that this class resolves to */ + private final int mVersionCode; + + public InstantAppResolveInfo(@NonNull InstantAppDigest digest, @Nullable String packageName, + @Nullable List filters, int versionConde) { + // 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(filters.size()); + mFilters.addAll(filters); + } else { + mFilters = null; + } + mPackageName = packageName; + mVersionCode = versionConde; + } + + public InstantAppResolveInfo(@NonNull String hostName, @Nullable String packageName, + @Nullable List filters) { + this(new InstantAppDigest(hostName), packageName, filters, -1 /*versionCode*/); + } + + InstantAppResolveInfo(Parcel in) { + mDigest = in.readParcelable(null /*loader*/); + mPackageName = in.readString(); + mFilters = new ArrayList(); + in.readList(mFilters, null /*loader*/); + mVersionCode = in.readInt(); + } + + public byte[] getDigestBytes() { + return mDigest.getDigestBytes()[0]; + } + + public int getDigestPrefix() { + return mDigest.getDigestPrefix()[0]; + } + + public String getPackageName() { + return mPackageName; + } + + public List getIntentFilters() { + return mFilters; + } + + public int getVersionCode() { + return mVersionCode; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel out, int flags) { + out.writeParcelable(mDigest, flags); + out.writeString(mPackageName); + out.writeList(mFilters); + out.writeInt(mVersionCode); + } + + public static final Parcelable.Creator CREATOR + = new Parcelable.Creator() { + public InstantAppResolveInfo createFromParcel(Parcel in) { + return new InstantAppResolveInfo(in); + } + + public InstantAppResolveInfo[] newArray(int size) { + return new InstantAppResolveInfo[size]; + } + }; + + /** + * Helper class to generate and store each of the digests and prefixes + * sent to the Instant App Resolver. + *

+ * 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 + */ + @SystemApi + public static final class InstantAppDigest 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 InstantAppDigest(@NonNull String hostName) { + this(hostName, -1 /*maxDigests*/); + } + + /** @hide */ + public InstantAppDigest(@NonNull String hostName, int maxDigests) { + if (hostName == null) { + throw new IllegalArgumentException(); + } + mDigestBytes = generateDigest(hostName.toLowerCase(Locale.ENGLISH), 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) + & DIGEST_MASK; + } + } + + private static byte[][] generateDigest(String hostName, int maxDigests) { + ArrayList digests = new ArrayList<>(); + try { + final MessageDigest digest = MessageDigest.getInstance(SHA_ALGORITHM); + if (maxDigests <= 0) { + final byte[] hostBytes = hostName.getBytes(); + digests.add(digest.digest(hostBytes)); + } else { + int prevDot = hostName.lastIndexOf('.'); + prevDot = hostName.lastIndexOf('.', prevDot - 1); + // shortcut for short URLs + if (prevDot < 0) { + digests.add(digest.digest(hostName.getBytes())); + } else { + byte[] hostBytes = + hostName.substring(prevDot + 1, hostName.length()).getBytes(); + digests.add(digest.digest(hostBytes)); + int digestCount = 1; + while (prevDot >= 0 && digestCount < maxDigests) { + prevDot = hostName.lastIndexOf('.', prevDot - 1); + hostBytes = + hostName.substring(prevDot + 1, hostName.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()][]); + } + + InstantAppDigest(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 CREATOR = + new Parcelable.Creator() { + @Override + public InstantAppDigest createFromParcel(Parcel in) { + return new InstantAppDigest(in); + } + @Override + public InstantAppDigest[] newArray(int size) { + return new InstantAppDigest[size]; + } + }; + } +} diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java index 83d43db20671b..47a7f5d8d8f23 100644 --- a/services/core/java/com/android/server/am/ActivityStarter.java +++ b/services/core/java/com/android/server/am/ActivityStarter.java @@ -122,7 +122,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.pm.InstantAppResolver; import com.android.server.wm.WindowManagerService; import java.util.ArrayList; @@ -529,10 +529,10 @@ class ActivityStarter { mService.getPackageManagerInternalLocked().requestInstantAppResolutionPhaseTwo( auxiliaryResponse, originalIntent, resolvedType, callingPackage, userId); } - return EphemeralResolver.buildEphemeralInstallerIntent(originalIntent, - callingPackage, resolvedType, userId, auxiliaryResponse.packageName, - auxiliaryResponse.splitName, auxiliaryResponse.versionCode, - auxiliaryResponse.token, auxiliaryResponse.needsPhaseTwo); + return InstantAppResolver.buildEphemeralInstallerIntent(originalIntent, + callingPackage, resolvedType, userId, auxiliaryResponse.packageName, + auxiliaryResponse.splitName, auxiliaryResponse.versionCode, + auxiliaryResponse.token, auxiliaryResponse.needsPhaseTwo); } void postStartActivityUncheckedProcessing( diff --git a/services/core/java/com/android/server/pm/EphemeralResolverConnection.java b/services/core/java/com/android/server/pm/EphemeralResolverConnection.java index 7f5973e642835..9c1992cf7083f 100644 --- a/services/core/java/com/android/server/pm/EphemeralResolverConnection.java +++ b/services/core/java/com/android/server/pm/EphemeralResolverConnection.java @@ -16,13 +16,13 @@ package com.android.server.pm; -import android.app.EphemeralResolverService; -import android.app.IEphemeralResolver; +import android.app.IInstantAppResolver; +import android.app.InstantAppResolverService; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; -import android.content.pm.EphemeralResolveInfo; +import android.content.pm.InstantAppResolveInfo; import android.os.Build; import android.os.Bundle; import android.os.Handler; @@ -61,14 +61,14 @@ final class EphemeralResolverConnection { private final Intent mIntent; private volatile boolean mBindRequested; - private IEphemeralResolver mRemoteInstance; + private IInstantAppResolver mRemoteInstance; public EphemeralResolverConnection(Context context, ComponentName componentName) { mContext = context; mIntent = new Intent(Intent.ACTION_RESOLVE_EPHEMERAL_PACKAGE).setComponent(componentName); } - public final List getEphemeralResolveInfoList(int hashPrefix[]) { + public final List getInstantAppResolveInfoList(int hashPrefix[]) { throwIfCalledOnMainThread(); try { return mGetEphemeralResolveInfoCaller.getEphemeralResolveInfoList( @@ -83,24 +83,25 @@ final class EphemeralResolverConnection { return null; } - public final void getEphemeralIntentFilterList(String hostName, PhaseTwoCallback callback, - Handler callbackHandler, final int sequence) { + public final void getInstantAppIntentFilterList(int hashPrefix[], String hostName, + PhaseTwoCallback callback, Handler callbackHandler, final int sequence) { final IRemoteCallback remoteCallback = new IRemoteCallback.Stub() { @Override public void sendResult(Bundle data) throws RemoteException { - final EphemeralResolveInfo ephemeralResolveInfo = - data.getParcelable(EphemeralResolverService.EXTRA_RESOLVE_INFO); + final ArrayList resolveList = + data.getParcelableArrayList( + InstantAppResolverService.EXTRA_RESOLVE_INFO); callbackHandler.post(new Runnable() { @Override public void run() { - callback.onPhaseTwoResolved(ephemeralResolveInfo, sequence); + callback.onPhaseTwoResolved(resolveList, sequence); } }); } }; try { getRemoteInstanceLazy() - .getEphemeralIntentFilterList(remoteCallback, hostName, sequence); + .getInstantAppIntentFilterList(hashPrefix, sequence, hostName, remoteCallback); } catch (RemoteException re) { } catch (TimeoutException te) { } @@ -121,7 +122,7 @@ final class EphemeralResolverConnection { } } - private IEphemeralResolver getRemoteInstanceLazy() throws TimeoutException { + private IInstantAppResolver getRemoteInstanceLazy() throws TimeoutException { synchronized (mLock) { if (mRemoteInstance != null) { return mRemoteInstance; @@ -172,14 +173,15 @@ final class EphemeralResolverConnection { * Asynchronous callback when results come back from ephemeral resolution phase two. */ public abstract static class PhaseTwoCallback { - abstract void onPhaseTwoResolved(EphemeralResolveInfo ephemeralResolveInfo, int sequence); + abstract void onPhaseTwoResolved( + List instantAppResolveInfoList, int sequence); } private final class MyServiceConnection implements ServiceConnection { @Override public void onServiceConnected(ComponentName name, IBinder service) { synchronized (mLock) { - mRemoteInstance = IEphemeralResolver.Stub.asInterface(service); + mRemoteInstance = IInstantAppResolver.Stub.asInterface(service); mLock.notifyAll(); } } @@ -193,7 +195,7 @@ final class EphemeralResolverConnection { } private static final class GetEphemeralResolveInfoCaller - extends TimedRemoteCaller> { + extends TimedRemoteCaller> { private final IRemoteCallback mCallback; public GetEphemeralResolveInfoCaller() { @@ -201,21 +203,21 @@ final class EphemeralResolverConnection { mCallback = new IRemoteCallback.Stub() { @Override public void sendResult(Bundle data) throws RemoteException { - final ArrayList resolveList = + final ArrayList resolveList = data.getParcelableArrayList( - EphemeralResolverService.EXTRA_RESOLVE_INFO); + InstantAppResolverService.EXTRA_RESOLVE_INFO); int sequence = - data.getInt(EphemeralResolverService.EXTRA_SEQUENCE, -1); + data.getInt(InstantAppResolverService.EXTRA_SEQUENCE, -1); onRemoteMethodResult(resolveList, sequence); } }; } - public List getEphemeralResolveInfoList( - IEphemeralResolver target, int hashPrefix[]) + public List getEphemeralResolveInfoList( + IInstantAppResolver target, int hashPrefix[]) throws RemoteException, TimeoutException { final int sequence = onBeforeRemoteCall(); - target.getEphemeralResolveInfoList(mCallback, hashPrefix, sequence); + target.getInstantAppResolveInfoList(hashPrefix, sequence, mCallback); return getResultTimed(sequence); } } diff --git a/services/core/java/com/android/server/pm/EphemeralResolver.java b/services/core/java/com/android/server/pm/InstantAppResolver.java similarity index 66% rename from services/core/java/com/android/server/pm/EphemeralResolver.java rename to services/core/java/com/android/server/pm/InstantAppResolver.java index 7bc65f91a7ab1..38214188a1a59 100644 --- a/services/core/java/com/android/server/pm/EphemeralResolver.java +++ b/services/core/java/com/android/server/pm/InstantAppResolver.java @@ -27,11 +27,11 @@ 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.InstantAppRequest; import android.content.pm.AuxiliaryResolveInfo; -import android.content.pm.EphemeralResolveInfo.EphemeralDigest; +import android.content.pm.InstantAppIntentFilter; +import android.content.pm.InstantAppResolveInfo; +import android.content.pm.InstantAppResolveInfo.InstantAppDigest; import android.os.Binder; import android.os.Handler; import android.os.RemoteException; @@ -46,54 +46,52 @@ import java.util.List; import java.util.UUID; /** @hide */ -public abstract class EphemeralResolver { - public static AuxiliaryResolveInfo doEphemeralResolutionPhaseOne(Context context, - EphemeralResolverConnection connection, EphemeralRequest requestObj) { +public abstract class InstantAppResolver { + public static AuxiliaryResolveInfo doInstantAppResolutionPhaseOne(Context context, + EphemeralResolverConnection connection, InstantAppRequest requestObj) { final Intent intent = requestObj.origIntent; - final EphemeralDigest digest = - new EphemeralDigest(intent.getData().getHost(), 5 /*maxDigests*/); + final InstantAppDigest digest = + new InstantAppDigest(intent.getData().getHost(), 5 /*maxDigests*/); final int[] shaPrefix = digest.getDigestPrefix(); - final List ephemeralResolveInfoList = - connection.getEphemeralResolveInfoList(shaPrefix); - if (ephemeralResolveInfoList == null || ephemeralResolveInfoList.size() == 0) { - // No hash prefix match; there are no ephemeral apps for this domain. + final List instantAppResolveInfoList = + connection.getInstantAppResolveInfoList(shaPrefix); + if (instantAppResolveInfoList == null || instantAppResolveInfoList.size() == 0) { + // No hash prefix match; there are no instant apps for this domain. return null; } final String token = UUID.randomUUID().toString(); - return EphemeralResolver.filterEphemeralIntent(ephemeralResolveInfoList, + return InstantAppResolver.filterInstantAppIntent(instantAppResolveInfoList, intent, requestObj.resolvedType, requestObj.userId, intent.getPackage(), digest, token); } - public static void doEphemeralResolutionPhaseTwo(Context context, - EphemeralResolverConnection connection, EphemeralRequest requestObj, - ActivityInfo ephemeralInstaller, Handler callbackHandler) { + public static void doInstantAppResolutionPhaseTwo(Context context, + EphemeralResolverConnection connection, InstantAppRequest requestObj, + ActivityInfo instantAppInstaller, Handler callbackHandler) { final Intent intent = requestObj.origIntent; final String hostName = intent.getData().getHost(); - final EphemeralDigest digest = new EphemeralDigest(hostName, 5 /*maxDigests*/); + final InstantAppDigest digest = new InstantAppDigest(hostName, 5 /*maxDigests*/); + final int[] shaPrefix = digest.getDigestPrefix(); final PhaseTwoCallback callback = new PhaseTwoCallback() { @Override - void onPhaseTwoResolved(EphemeralResolveInfo ephemeralResolveInfo, + void onPhaseTwoResolved(List instantAppResolveInfoList, int sequence) { final String packageName; final String splitName; final int versionCode; - if (ephemeralResolveInfo != null) { - final ArrayList ephemeralResolveInfoList = - new ArrayList(1); - ephemeralResolveInfoList.add(ephemeralResolveInfo); - final AuxiliaryResolveInfo ephemeralIntentInfo = - EphemeralResolver.filterEphemeralIntent( - ephemeralResolveInfoList, intent, null /*resolvedType*/, + if (instantAppResolveInfoList != null && instantAppResolveInfoList.size() > 0) { + final AuxiliaryResolveInfo instantAppIntentInfo = + InstantAppResolver.filterInstantAppIntent( + instantAppResolveInfoList, intent, null /*resolvedType*/, 0 /*userId*/, intent.getPackage(), digest, requestObj.responseObj.token); - if (ephemeralIntentInfo != null - && ephemeralIntentInfo.resolveInfo != null) { - packageName = ephemeralIntentInfo.resolveInfo.getPackageName(); - splitName = ephemeralIntentInfo.splitName; - versionCode = ephemeralIntentInfo.resolveInfo.getVersionCode(); + if (instantAppIntentInfo != null + && instantAppIntentInfo.resolveInfo != null) { + packageName = instantAppIntentInfo.resolveInfo.getPackageName(); + splitName = instantAppIntentInfo.splitName; + versionCode = instantAppIntentInfo.resolveInfo.getVersionCode(); } else { packageName = null; splitName = null; @@ -115,27 +113,27 @@ public abstract class EphemeralResolver { requestObj.responseObj.token, false /*needsPhaseTwo*/); installerIntent.setComponent(new ComponentName( - ephemeralInstaller.packageName, ephemeralInstaller.name)); + instantAppInstaller.packageName, instantAppInstaller.name)); context.startActivity(installerIntent); } }; - connection.getEphemeralIntentFilterList( - hostName, callback, callbackHandler, 0 /*sequence*/); + connection.getInstantAppIntentFilterList( + shaPrefix, hostName, callback, callbackHandler, 0 /*sequence*/); } /** - * Builds and returns an intent to launch the ephemeral installer. + * Builds and returns an intent to launch the instant installer. */ public static Intent buildEphemeralInstallerIntent(@NonNull Intent origIntent, @NonNull String callingPackage, @NonNull String resolvedType, int userId, - @NonNull String ephemeralPackageName, - @Nullable String ephemeralSplitName, + @NonNull String instantAppPackageName, + @Nullable String instantAppSplitName, int versionCode, @Nullable String token, boolean needsPhaseTwo) { - // Construct the intent that launches the ephemeral installer + // Construct the intent that launches the instant installer int flags = origIntent.getFlags(); final Intent intent = new Intent(); intent.setFlags(flags @@ -185,63 +183,63 @@ public abstract class EphemeralResolver { new IntentSender(successIntentTarget)); } catch (RemoteException ignore) { /* ignore; same process */ } - intent.putExtra(Intent.EXTRA_PACKAGE_NAME, ephemeralPackageName); - intent.putExtra(Intent.EXTRA_SPLIT_NAME, ephemeralSplitName); + intent.putExtra(Intent.EXTRA_PACKAGE_NAME, instantAppPackageName); + intent.putExtra(Intent.EXTRA_SPLIT_NAME, instantAppSplitName); intent.putExtra(Intent.EXTRA_VERSION_CODE, versionCode); } return intent; } - private static AuxiliaryResolveInfo filterEphemeralIntent( - List ephemeralResolveInfoList, + private static AuxiliaryResolveInfo filterInstantAppIntent( + List instantAppResolveInfoList, Intent intent, String resolvedType, int userId, String packageName, - EphemeralDigest digest, String token) { + InstantAppDigest 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())) { + for (InstantAppResolveInfo instantAppInfo : instantAppResolveInfoList) { + if (!Arrays.equals(digestBytes[i], instantAppInfo.getDigestBytes())) { continue; } if (packageName != null - && !packageName.equals(ephemeralInfo.getPackageName())) { + && !packageName.equals(instantAppInfo.getPackageName())) { continue; } - final List ephemeralFilters = - ephemeralInfo.getIntentFilters(); + final List instantAppFilters = + instantAppInfo.getIntentFilters(); // No filters; we need to start phase two - if (ephemeralFilters == null || ephemeralFilters.isEmpty()) { - return new AuxiliaryResolveInfo(ephemeralInfo, + if (instantAppFilters == null || instantAppFilters.isEmpty()) { + return new AuxiliaryResolveInfo(instantAppInfo, 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 = + final PackageManagerService.EphemeralIntentResolver instantAppResolver = new PackageManagerService.EphemeralIntentResolver(); - for (int j = ephemeralFilters.size() - 1; j >= 0; --j) { - final EphemeralIntentFilter ephemeralFilter = ephemeralFilters.get(j); - final List splitFilters = ephemeralFilter.getFilters(); + for (int j = instantAppFilters.size() - 1; j >= 0; --j) { + final InstantAppIntentFilter instantAppFilter = instantAppFilters.get(j); + final List splitFilters = instantAppFilter.getFilters(); if (splitFilters == null || splitFilters.isEmpty()) { continue; } for (int k = splitFilters.size() - 1; k >= 0; --k) { final AuxiliaryResolveInfo intentInfo = - new AuxiliaryResolveInfo(ephemeralInfo, - splitFilters.get(k), ephemeralFilter.getSplitName(), + new AuxiliaryResolveInfo(instantAppInfo, + splitFilters.get(k), instantAppFilter.getSplitName(), token, false /*needsPhase2*/); - ephemeralResolver.addFilter(intentInfo); + instantAppResolver.addFilter(intentInfo); } } - List matchedResolveInfoList = ephemeralResolver.queryIntent( + List matchedResolveInfoList = instantAppResolver.queryIntent( intent, resolvedType, false /*defaultOnly*/, userId); if (!matchedResolveInfoList.isEmpty()) { return matchedResolveInfoList.get(0); } } } - // Hash or filter mis-match; no ephemeral apps for this domain. + // Hash or filter mis-match; no instant apps for this domain. return null; } } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 838098310caf7..7b047299a0032 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -128,8 +128,7 @@ import android.content.pm.ApplicationInfo; import android.content.pm.AppsQueryHelper; import android.content.pm.ChangedPackages; import android.content.pm.ComponentInfo; -import android.content.pm.EphemeralRequest; -import android.content.pm.EphemeralResolveInfo; +import android.content.pm.InstantAppRequest; import android.content.pm.AuxiliaryResolveInfo; import android.content.pm.FallbackCategoryProvider; import android.content.pm.FeatureInfo; @@ -143,6 +142,7 @@ import android.content.pm.IPackageManager; import android.content.pm.IPackageMoveObserver; import android.content.pm.IPackageStatsObserver; import android.content.pm.InstantAppInfo; +import android.content.pm.InstantAppResolveInfo; import android.content.pm.InstrumentationInfo; import android.content.pm.IntentFilterVerificationInfo; import android.content.pm.KeySet; @@ -1742,9 +1742,9 @@ public class PackageManagerService extends IPackageManager.Stub { break; } case INSTANT_APP_RESOLUTION_PHASE_TWO: { - EphemeralResolver.doEphemeralResolutionPhaseTwo(mContext, + InstantAppResolver.doInstantAppResolutionPhaseTwo(mContext, mInstantAppResolverConnection, - (EphemeralRequest) msg.obj, + (InstantAppRequest) msg.obj, mInstantAppInstallerActivity, mHandler); } @@ -5767,7 +5767,7 @@ public class PackageManagerService extends IPackageManager.Stub { Intent origIntent, String resolvedType, String callingPackage, int userId) { final Message msg = mHandler.obtainMessage(INSTANT_APP_RESOLUTION_PHASE_TWO, - new EphemeralRequest(responseObj, origIntent, resolvedType, + new InstantAppRequest(responseObj, origIntent, resolvedType, callingPackage, userId)); mHandler.sendMessage(msg); } @@ -6298,11 +6298,11 @@ public class PackageManagerService extends IPackageManager.Stub { } if (addEphemeral) { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "resolveEphemeral"); - final EphemeralRequest requestObject = new EphemeralRequest( + final InstantAppRequest requestObject = new InstantAppRequest( null /*responseObj*/, intent /*origIntent*/, resolvedType, null /*callingPackage*/, userId); final AuxiliaryResolveInfo auxiliaryResponse = - EphemeralResolver.doEphemeralResolutionPhaseOne( + InstantAppResolver.doInstantAppResolutionPhaseOne( mContext, mInstantAppResolverConnection, requestObject); if (auxiliaryResponse != null) { if (DEBUG_EPHEMERAL) { @@ -12816,7 +12816,7 @@ public class PackageManagerService extends IPackageManager.Stub { * would be needed to apply ordering. If the intent resolver becomes re-entrant, * this needs to be contained entirely within {@link #filterResults()}. */ - final ArrayMap> mOrderResult = new ArrayMap<>(); + final ArrayMap> mOrderResult = new ArrayMap<>(); @Override protected AuxiliaryResolveInfo[] newArray(int size) { @@ -12836,13 +12836,13 @@ public class PackageManagerService extends IPackageManager.Stub { } final String packageName = responseObj.resolveInfo.getPackageName(); final Integer order = responseObj.getOrder(); - final Pair lastOrderResult = + final Pair 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 = responseObj.resolveInfo; + final InstantAppResolveInfo res = responseObj.resolveInfo; if (order > 0) { // non-zero order, enable ordering mOrderResult.put(packageName, new Pair<>(order, res)); @@ -12858,9 +12858,9 @@ public class PackageManagerService extends IPackageManager.Stub { } int resultSize = results.size(); for (int i = 0; i < resultSize; i++) { - final EphemeralResolveInfo info = results.get(i).resolveInfo; + final InstantAppResolveInfo info = results.get(i).resolveInfo; final String packageName = info.getPackageName(); - final Pair savedInfo = mOrderResult.get(packageName); + final Pair savedInfo = mOrderResult.get(packageName); if (savedInfo == null) { // package doesn't having ordering continue;