From 818d2dac5483271ead598ff316eb8abffcebba62 Mon Sep 17 00:00:00 2001 From: Anthony Stange Date: Thu, 21 Jan 2021 15:31:38 -0500 Subject: [PATCH 1/2] Add new callback / intent type to ContextHub APIs Updates the ContextHub APIs to support notifying clients (either through traditional callbacks or via PendingIntent) that their ability to communicate with nanoapps has changed. Also, adds the ability for clients to discover the permissions required to communicate with nanoapps so they know whether their initial or subsequent nanoapp messages will successfully be delivered. Bug: 166846988 Test: compile Change-Id: I37e475a1457a031890693d2858b5d5f7de34de26 --- core/api/system-current.txt | 9 +++ .../location/ContextHubClientCallback.java | 31 ++++++++++ .../location/ContextHubIntentEvent.java | 45 ++++++++++++-- .../hardware/location/ContextHubManager.java | 59 +++++++++++++++++++ .../location/IContextHubClientCallback.aidl | 3 + .../hardware/location/NanoAppState.java | 24 ++++++++ .../contexthub/ContextHubService.java | 4 ++ 7 files changed, 169 insertions(+), 6 deletions(-) diff --git a/core/api/system-current.txt b/core/api/system-current.txt index b3e78ab7377af..50b025176aa4c 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -3168,6 +3168,7 @@ package android.hardware.location { public class ContextHubClientCallback { ctor public ContextHubClientCallback(); + method public void onClientAuthorizationChanged(@NonNull android.hardware.location.ContextHubClient, long, int); method public void onHubReset(android.hardware.location.ContextHubClient); method public void onMessageFromNanoApp(android.hardware.location.ContextHubClient, android.hardware.location.NanoAppMessage); method public void onNanoAppAborted(android.hardware.location.ContextHubClient, long, int); @@ -3204,6 +3205,7 @@ package android.hardware.location { public class ContextHubIntentEvent { method @NonNull public static android.hardware.location.ContextHubIntentEvent fromIntent(@NonNull android.content.Intent); + method public int getClientAuthorizationState(); method @NonNull public android.hardware.location.ContextHubInfo getContextHubInfo(); method public int getEventType(); method public int getNanoAppAbortCode(); @@ -3231,6 +3233,10 @@ package android.hardware.location { method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.LOCATION_HARDWARE, android.Manifest.permission.ACCESS_CONTEXT_HUB}) public int unloadNanoApp(int); method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.LOCATION_HARDWARE, android.Manifest.permission.ACCESS_CONTEXT_HUB}) public android.hardware.location.ContextHubTransaction unloadNanoApp(@NonNull android.hardware.location.ContextHubInfo, long); method @Deprecated public int unregisterCallback(@NonNull android.hardware.location.ContextHubManager.Callback); + field public static final int AUTHORIZATION_DENIED = 0; // 0x0 + field public static final int AUTHORIZATION_DENIED_GRACE_PERIOD = 1; // 0x1 + field public static final int AUTHORIZATION_GRANTED = 2; // 0x2 + field public static final int EVENT_CLIENT_AUTHORIZATION = 7; // 0x7 field public static final int EVENT_HUB_RESET = 6; // 0x6 field public static final int EVENT_NANOAPP_ABORTED = 4; // 0x4 field public static final int EVENT_NANOAPP_DISABLED = 3; // 0x3 @@ -3238,6 +3244,7 @@ package android.hardware.location { field public static final int EVENT_NANOAPP_LOADED = 0; // 0x0 field public static final int EVENT_NANOAPP_MESSAGE = 5; // 0x5 field public static final int EVENT_NANOAPP_UNLOADED = 1; // 0x1 + field public static final String EXTRA_CLIENT_AUTHORIZATION_STATE = "android.hardware.location.extra.CLIENT_AUTHORIZATION_STATE"; field public static final String EXTRA_CONTEXT_HUB_INFO = "android.hardware.location.extra.CONTEXT_HUB_INFO"; field public static final String EXTRA_EVENT_TYPE = "android.hardware.location.extra.EVENT_TYPE"; field public static final String EXTRA_MESSAGE = "android.hardware.location.extra.MESSAGE"; @@ -3476,8 +3483,10 @@ package android.hardware.location { public final class NanoAppState implements android.os.Parcelable { ctor public NanoAppState(long, int, boolean); + ctor public NanoAppState(long, int, boolean, @NonNull java.util.List); method public int describeContents(); method public long getNanoAppId(); + method @NonNull public java.util.List getNanoAppPermissions(); method public long getNanoAppVersion(); method public boolean isEnabled(); method public void writeToParcel(android.os.Parcel, int); diff --git a/core/java/android/hardware/location/ContextHubClientCallback.java b/core/java/android/hardware/location/ContextHubClientCallback.java index 20fa7538e2d5f..b31b85fdabf67 100644 --- a/core/java/android/hardware/location/ContextHubClientCallback.java +++ b/core/java/android/hardware/location/ContextHubClientCallback.java @@ -15,6 +15,7 @@ */ package android.hardware.location; +import android.annotation.NonNull; import android.annotation.SystemApi; import java.util.concurrent.Executor; @@ -101,4 +102,34 @@ public class ContextHubClientCallback { * @param nanoAppId the ID of the nanoapp that had been disabled */ public void onNanoAppDisabled(ContextHubClient client, long nanoAppId) {} + + /** + * Callback invoked when a {@link ContextHubClient}'s authorization to communicate with a + * nanoapp changes. This typically happens as a result of the app that created the + * {@link ContextHubClient} gaining or losing the permissions required to communicate with a + * nanoapp. + * + * An example of the connection callbacks looks like: + * 1) {@link ContextHubClient} sends message to nanoapp and holds required permissions + * 2) {@link ContextHubClient} loses required permissions + * 3) Callback invoked with the nanoapp ID and + * {@link ContextHubManager#AUTHORIZATION_DENIED_GRACE_PERIOD} + * 4) {@link ContextHubClient} performs any cleanup required with the nanoapp + * 5) Callback invoked with the nanoapp ID and {@link ContextHubManager#AUTHORIZATION_DENIED}. + * At this point, any further attempts of communication between the nanoapp and the + * {@link ContextHubClient} will be dropped by the contexthub along with + * {@link ContextHubManager#AUTHORIZATION_DENIED} being sent. The {@link ContextHubClient} + * should assume no communciation can happen again until + * {@link ContextHubManager#AUTHORIZATION_GRANTED} is received. + * + * @param client the client that is associated with this callback + * @param nanoAppId the ID of the nanoapp associated with the new + * authorization state + * @param authorization the authorization state denoting the ability of the + * client to communicate with the nanoapp + */ + public void onClientAuthorizationChanged( + @NonNull ContextHubClient client, + long nanoAppId, + @ContextHubManager.AuthorizationState int authorization) {} } diff --git a/core/java/android/hardware/location/ContextHubIntentEvent.java b/core/java/android/hardware/location/ContextHubIntentEvent.java index 917f62bc6cbf5..3e8f4219a485c 100644 --- a/core/java/android/hardware/location/ContextHubIntentEvent.java +++ b/core/java/android/hardware/location/ContextHubIntentEvent.java @@ -43,39 +43,45 @@ public class ContextHubIntentEvent { private final int mNanoAppAbortCode; + private final int mClientAuthorizationState; + private ContextHubIntentEvent( @NonNull ContextHubInfo contextHubInfo, @ContextHubManager.Event int eventType, - long nanoAppId, NanoAppMessage nanoAppMessage, int nanoAppAbortCode) { + long nanoAppId, NanoAppMessage nanoAppMessage, int nanoAppAbortCode, + @ContextHubManager.AuthorizationState int clientAuthorizationState) { mContextHubInfo = contextHubInfo; mEventType = eventType; mNanoAppId = nanoAppId; mNanoAppMessage = nanoAppMessage; mNanoAppAbortCode = nanoAppAbortCode; + mClientAuthorizationState = clientAuthorizationState; } private ContextHubIntentEvent( @NonNull ContextHubInfo contextHubInfo, @ContextHubManager.Event int eventType) { this(contextHubInfo, eventType, -1 /* nanoAppId */, null /* nanoAppMessage */, - -1 /* nanoAppAbortCode */); + -1 /* nanoAppAbortCode */, 0 /* clientAuthorizationState */); } private ContextHubIntentEvent( @NonNull ContextHubInfo contextHubInfo, @ContextHubManager.Event int eventType, long nanoAppId) { this(contextHubInfo, eventType, nanoAppId, null /* nanoAppMessage */, - -1 /* nanoAppAbortCode */); + -1 /* nanoAppAbortCode */, 0 /* clientAuthorizationState */); } private ContextHubIntentEvent( @NonNull ContextHubInfo contextHubInfo, @ContextHubManager.Event int eventType, long nanoAppId, @NonNull NanoAppMessage nanoAppMessage) { - this(contextHubInfo, eventType, nanoAppId, nanoAppMessage, -1 /* nanoAppAbortCode */); + this(contextHubInfo, eventType, nanoAppId, nanoAppMessage, -1 /* nanoAppAbortCode */, + 0 /* clientAuthorizationState */); } private ContextHubIntentEvent( @NonNull ContextHubInfo contextHubInfo, @ContextHubManager.Event int eventType, long nanoAppId, int nanoAppAbortCode) { - this(contextHubInfo, eventType, nanoAppId, null /* nanoAppMessage */, nanoAppAbortCode); + this(contextHubInfo, eventType, nanoAppId, null /* nanoAppMessage */, nanoAppAbortCode, + 0 /* clientAuthorizationState */); } /** @@ -105,7 +111,8 @@ public class ContextHubIntentEvent { case ContextHubManager.EVENT_NANOAPP_ENABLED: case ContextHubManager.EVENT_NANOAPP_DISABLED: case ContextHubManager.EVENT_NANOAPP_ABORTED: - case ContextHubManager.EVENT_NANOAPP_MESSAGE: // fall through + case ContextHubManager.EVENT_NANOAPP_MESSAGE: + case ContextHubManager.EVENT_CLIENT_AUTHORIZATION: // fall through long nanoAppId = getLongExtraOrThrow(intent, ContextHubManager.EXTRA_NANOAPP_ID); if (eventType == ContextHubManager.EVENT_NANOAPP_MESSAGE) { hasExtraOrThrow(intent, ContextHubManager.EXTRA_MESSAGE); @@ -120,6 +127,11 @@ public class ContextHubIntentEvent { int nanoAppAbortCode = getIntExtraOrThrow( intent, ContextHubManager.EXTRA_NANOAPP_ABORT_CODE); event = new ContextHubIntentEvent(info, eventType, nanoAppId, nanoAppAbortCode); + } else if (eventType == ContextHubManager.EVENT_CLIENT_AUTHORIZATION) { + int authState = getIntExtraOrThrow( + intent, ContextHubManager.EXTRA_CLIENT_AUTHORIZATION_STATE); + event = new ContextHubIntentEvent(info, eventType, nanoAppId, + null /* nanoAppMessage */, -1 /* nanoAppAbortCode */, authState); } else { event = new ContextHubIntentEvent(info, eventType, nanoAppId); } @@ -192,6 +204,21 @@ public class ContextHubIntentEvent { return mNanoAppMessage; } + /** + * @return the client authorization state + * + * @throws UnsupportedOperationException if this was not a client authorization state event + */ + @ContextHubManager.AuthorizationState + public int getClientAuthorizationState() { + if (mEventType != ContextHubManager.EVENT_CLIENT_AUTHORIZATION) { + throw new UnsupportedOperationException( + "Cannot invoke getClientAuthorizationState() on non-authorization event: " + + mEventType); + } + return mClientAuthorizationState; + } + @NonNull @Override public String toString() { @@ -207,6 +234,9 @@ public class ContextHubIntentEvent { if (mEventType == ContextHubManager.EVENT_NANOAPP_MESSAGE) { out += ", nanoAppMessage = " + mNanoAppMessage; } + if (mEventType == ContextHubManager.EVENT_CLIENT_AUTHORIZATION) { + out += ", clientAuthState = " + mClientAuthorizationState; + } return out + "]"; } @@ -233,6 +263,9 @@ public class ContextHubIntentEvent { if (mEventType == ContextHubManager.EVENT_NANOAPP_MESSAGE) { isEqual &= other.getNanoAppMessage().equals(mNanoAppMessage); } + if (mEventType == ContextHubManager.EVENT_CLIENT_AUTHORIZATION) { + isEqual &= other.getClientAuthorizationState() == mClientAuthorizationState; + } } catch (UnsupportedOperationException e) { isEqual = false; } diff --git a/core/java/android/hardware/location/ContextHubManager.java b/core/java/android/hardware/location/ContextHubManager.java index d44480796d756..03de891dd7df0 100644 --- a/core/java/android/hardware/location/ContextHubManager.java +++ b/core/java/android/hardware/location/ContextHubManager.java @@ -58,6 +58,12 @@ import java.util.concurrent.Executor; public final class ContextHubManager { private static final String TAG = "ContextHubManager"; + /** + * An extra of type int describing the client's authorization state. + */ + public static final String EXTRA_CLIENT_AUTHORIZATION_STATE = + "android.hardware.location.extra.CLIENT_AUTHORIZATION_STATE"; + /** * An extra of type {@link ContextHubInfo} describing the source of the event. */ @@ -85,6 +91,42 @@ public final class ContextHubManager { */ public static final String EXTRA_MESSAGE = "android.hardware.location.extra.MESSAGE"; + /** + * Constants describing if a {@link ContextHubClient} and a {@link NanoApp} are authorized to + * communicate. + * + * @hide + */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = { "AUTHORIZATION_" }, value = { + AUTHORIZATION_DENIED, + AUTHORIZATION_DENIED_GRACE_PERIOD, + AUTHORIZATION_GRANTED, + }) + public @interface AuthorizationState { } + + /** + * Indicates that the {@link ContextHubClient} can no longer communicate with a nanoapp. If the + * {@link ContextHubClient} attempts to send messages to the nanoapp, it will continue to + * receive this authorization state if the connection is still closed. + */ + public static final int AUTHORIZATION_DENIED = 0; + + /** + * Indicates the {@link ContextHubClient} will soon lose its authorization to communicate with a + * nanoapp. The {@link ContextHubClient} must perform any cleanup with the nanoapp as soon as + * possible. + * + * Note that the time between this state event and {@link AUTHORIZATION_DENIED} must be enough + * for the {@link ContextHubClient} to send at least one message to the nanoapp. + */ + public static final int AUTHORIZATION_DENIED_GRACE_PERIOD = 1; + + /** + * The {@link ContextHubClient} is authorized to communicate with the nanoapp. + */ + public static final int AUTHORIZATION_GRANTED = 2; + /** * Constants describing the type of events from a Context Hub. * {@hide} @@ -98,6 +140,7 @@ public final class ContextHubManager { EVENT_NANOAPP_ABORTED, EVENT_NANOAPP_MESSAGE, EVENT_HUB_RESET, + EVENT_CLIENT_AUTHORIZATION, }) public @interface Event { } @@ -138,6 +181,14 @@ public final class ContextHubManager { */ public static final int EVENT_HUB_RESET = 6; + /** + * An event describing a client authorization state change. See + * {@link ContextHubClientCallback#onClientAuthorizationChanged} for more details on when this + * event will be sent. Contains the EXTRA_NANOAPP_ID and EXTRA_CLIENT_AUTHORIZATION_STATE + * extras. + */ + public static final int EVENT_CLIENT_AUTHORIZATION = 7; + private final Looper mMainLooper; private final IContextHubService mService; private Callback mCallback; @@ -747,6 +798,14 @@ public final class ContextHubManager { public void onNanoAppDisabled(long nanoAppId) { executor.execute(() -> callback.onNanoAppDisabled(client, nanoAppId)); } + + @Override + public void onClientAuthorizationChanged( + long nanoAppId, @ContextHubManager.AuthorizationState int authorization) { + executor.execute( + () -> callback.onClientAuthorizationChanged( + client, nanoAppId, authorization)); + } }; } diff --git a/core/java/android/hardware/location/IContextHubClientCallback.aidl b/core/java/android/hardware/location/IContextHubClientCallback.aidl index 1c76bcbe18ce4..bcd6b087a276a 100644 --- a/core/java/android/hardware/location/IContextHubClientCallback.aidl +++ b/core/java/android/hardware/location/IContextHubClientCallback.aidl @@ -46,4 +46,7 @@ oneway interface IContextHubClientCallback { // Callback invoked when a nanoapp is disabled at the attached Context Hub. void onNanoAppDisabled(long nanoAppId); + + // Callback invoked when the authorization state of a client changes. + void onClientAuthorizationChanged(long nanoAppId, int authorization); } diff --git a/core/java/android/hardware/location/NanoAppState.java b/core/java/android/hardware/location/NanoAppState.java index 8de7ecd6b2b31..96b1f19a9cd57 100644 --- a/core/java/android/hardware/location/NanoAppState.java +++ b/core/java/android/hardware/location/NanoAppState.java @@ -15,10 +15,14 @@ */ package android.hardware.location; +import android.annotation.NonNull; import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; +import java.util.ArrayList; +import java.util.List; + /** * A class describing the nanoapp state information resulting from a query to a Context Hub. * @@ -29,11 +33,21 @@ public final class NanoAppState implements Parcelable { private long mNanoAppId; private int mNanoAppVersion; private boolean mIsEnabled; + private List mNanoAppPermissions; public NanoAppState(long nanoAppId, int appVersion, boolean enabled) { mNanoAppId = nanoAppId; mNanoAppVersion = appVersion; mIsEnabled = enabled; + mNanoAppPermissions = new ArrayList(); + } + + public NanoAppState(long nanoAppId, int appVersion, boolean enabled, + @NonNull List nanoAppPermissions) { + mNanoAppId = nanoAppId; + mNanoAppVersion = appVersion; + mIsEnabled = enabled; + mNanoAppPermissions = nanoAppPermissions; } /** @@ -57,10 +71,19 @@ public final class NanoAppState implements Parcelable { return mIsEnabled; } + /** + * @return List of Android permissions that are required to communicate with this app. + */ + public @NonNull List getNanoAppPermissions() { + return mNanoAppPermissions; + } + private NanoAppState(Parcel in) { mNanoAppId = in.readLong(); mNanoAppVersion = in.readInt(); mIsEnabled = (in.readInt() == 1); + mNanoAppPermissions = new ArrayList(); + in.readStringList(mNanoAppPermissions); } @Override @@ -73,6 +96,7 @@ public final class NanoAppState implements Parcelable { out.writeLong(mNanoAppId); out.writeInt(mNanoAppVersion); out.writeInt(mIsEnabled ? 1 : 0); + out.writeStringList(mNanoAppPermissions); } public static final @android.annotation.NonNull Creator CREATOR = diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubService.java b/services/core/java/com/android/server/location/contexthub/ContextHubService.java index dc1a26ae6fea5..d3536345ccf1a 100644 --- a/services/core/java/com/android/server/location/contexthub/ContextHubService.java +++ b/services/core/java/com/android/server/location/contexthub/ContextHubService.java @@ -320,6 +320,10 @@ public class ContextHubService extends IContextHubService.Stub { @Override public void onNanoAppDisabled(long nanoAppId) { } + + @Override + public void onClientAuthorizationChanged(long nanoAppId, int authorization) { + } }; } From b6b7b42358a8eb37d2a4cd7a2881d1e73e8e2deb Mon Sep 17 00:00:00 2001 From: Anthony Stange Date: Fri, 22 Jan 2021 11:53:03 -0500 Subject: [PATCH 2/2] Add attribution tag parameter to createClient Attribution tags allow clients to denote if they are providers of specific data in the system which can be useful when noting permissions operations later. Bug: 166846988 Test: compile Change-Id: I7b0ba9a9ffc6a49f79f80e8078f50625404ddafe --- core/api/system-current.txt | 2 + .../hardware/location/ContextHubManager.java | 69 ++++++++++++++----- .../hardware/location/IContextHubService.aidl | 6 +- .../contexthub/ContextHubService.java | 9 ++- 4 files changed, 65 insertions(+), 21 deletions(-) diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 50b025176aa4c..499b7c2ebc33d 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -3214,8 +3214,10 @@ package android.hardware.location { } public final class ContextHubManager { + method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.LOCATION_HARDWARE, android.Manifest.permission.ACCESS_CONTEXT_HUB}) public android.hardware.location.ContextHubClient createClient(@Nullable android.content.Context, @NonNull android.hardware.location.ContextHubInfo, @NonNull java.util.concurrent.Executor, @NonNull android.hardware.location.ContextHubClientCallback); method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.LOCATION_HARDWARE, android.Manifest.permission.ACCESS_CONTEXT_HUB}) public android.hardware.location.ContextHubClient createClient(@NonNull android.hardware.location.ContextHubInfo, @NonNull android.hardware.location.ContextHubClientCallback, @NonNull java.util.concurrent.Executor); method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.LOCATION_HARDWARE, android.Manifest.permission.ACCESS_CONTEXT_HUB}) public android.hardware.location.ContextHubClient createClient(@NonNull android.hardware.location.ContextHubInfo, @NonNull android.hardware.location.ContextHubClientCallback); + method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.LOCATION_HARDWARE, android.Manifest.permission.ACCESS_CONTEXT_HUB}) public android.hardware.location.ContextHubClient createClient(@Nullable android.content.Context, @NonNull android.hardware.location.ContextHubInfo, @NonNull android.app.PendingIntent, long); method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.LOCATION_HARDWARE, android.Manifest.permission.ACCESS_CONTEXT_HUB}) public android.hardware.location.ContextHubClient createClient(@NonNull android.hardware.location.ContextHubInfo, @NonNull android.app.PendingIntent, long); method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.LOCATION_HARDWARE, android.Manifest.permission.ACCESS_CONTEXT_HUB}) public android.hardware.location.ContextHubTransaction disableNanoApp(@NonNull android.hardware.location.ContextHubInfo, long); method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.LOCATION_HARDWARE, android.Manifest.permission.ACCESS_CONTEXT_HUB}) public android.hardware.location.ContextHubTransaction enableNanoApp(@NonNull android.hardware.location.ContextHubInfo, long); diff --git a/core/java/android/hardware/location/ContextHubManager.java b/core/java/android/hardware/location/ContextHubManager.java index 03de891dd7df0..ebb3021bf083d 100644 --- a/core/java/android/hardware/location/ContextHubManager.java +++ b/core/java/android/hardware/location/ContextHubManager.java @@ -816,9 +816,10 @@ public final class ContextHubManager { * registration succeeds, the client can send messages to nanoapps through the returned * {@link ContextHubClient} object, and receive notifications through the provided callback. * + * @param context the context of the application * @param hubInfo the hub to attach this client to - * @param callback the notification callback to register * @param executor the executor to invoke the callback + * @param callback the notification callback to register * @return the registered client object * * @throws IllegalArgumentException if hubInfo does not represent a valid hub @@ -832,8 +833,9 @@ public final class ContextHubManager { android.Manifest.permission.ACCESS_CONTEXT_HUB }) @NonNull public ContextHubClient createClient( - @NonNull ContextHubInfo hubInfo, @NonNull ContextHubClientCallback callback, - @NonNull @CallbackExecutor Executor executor) { + @Nullable Context context, @NonNull ContextHubInfo hubInfo, + @NonNull @CallbackExecutor Executor executor, + @NonNull ContextHubClientCallback callback) { Objects.requireNonNull(callback, "Callback cannot be null"); Objects.requireNonNull(hubInfo, "ContextHubInfo cannot be null"); Objects.requireNonNull(executor, "Executor cannot be null"); @@ -842,9 +844,14 @@ public final class ContextHubManager { IContextHubClientCallback clientInterface = createClientCallback( client, callback, executor); + String attributionTag = null; + if (context != null) { + attributionTag = context.getAttributionTag(); + } + IContextHubClient clientProxy; try { - clientProxy = mService.createClient(hubInfo.getId(), clientInterface); + clientProxy = mService.createClient(hubInfo.getId(), clientInterface, attributionTag); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -853,19 +860,25 @@ public final class ContextHubManager { return client; } + + /** + * Equivalent to + * {@link #createClient(ContextHubInfo, Executor, String, ContextHubClientCallback)} + * with the {@link Context} being set to null. + */ + @RequiresPermission(anyOf = { + android.Manifest.permission.LOCATION_HARDWARE, + android.Manifest.permission.ACCESS_CONTEXT_HUB + }) + @NonNull public ContextHubClient createClient( + @NonNull ContextHubInfo hubInfo, @NonNull ContextHubClientCallback callback, + @NonNull @CallbackExecutor Executor executor) { + return createClient(null /* context */, hubInfo, executor, callback); + } + /** * Equivalent to {@link #createClient(ContextHubInfo, ContextHubClientCallback, Executor)} * with the executor using the main thread's Looper. - * - * @param hubInfo the hub to attach this client to - * @param callback the notification callback to register - * @return the registered client object - * - * @throws IllegalArgumentException if hubInfo does not represent a valid hub - * @throws IllegalStateException if there were too many registered clients at the service - * @throws NullPointerException if callback or hubInfo is null - * - * @see ContextHubClientCallback */ @RequiresPermission(anyOf = { android.Manifest.permission.LOCATION_HARDWARE, @@ -873,7 +886,8 @@ public final class ContextHubManager { }) @NonNull public ContextHubClient createClient( @NonNull ContextHubInfo hubInfo, @NonNull ContextHubClientCallback callback) { - return createClient(hubInfo, callback, new HandlerExecutor(Handler.getMain())); + return createClient(null /* context */, hubInfo, new HandlerExecutor(Handler.getMain()), + callback); } /** @@ -907,6 +921,8 @@ public final class ContextHubManager { * on the provided PendingIntent, then the client will be automatically unregistered by the * service. * + * @param context the context of the application. If a PendingIntent client is recreated, + * the latest state in the context will be used and old state will be discarded * @param hubInfo the hub to attach this client to * @param pendingIntent the PendingIntent to register to the client * @param nanoAppId the ID of the nanoapp that Intent events will be generated for @@ -921,16 +937,22 @@ public final class ContextHubManager { android.Manifest.permission.ACCESS_CONTEXT_HUB }) @NonNull public ContextHubClient createClient( - @NonNull ContextHubInfo hubInfo, @NonNull PendingIntent pendingIntent, long nanoAppId) { + @Nullable Context context, @NonNull ContextHubInfo hubInfo, + @NonNull PendingIntent pendingIntent, long nanoAppId) { Objects.requireNonNull(pendingIntent); Objects.requireNonNull(hubInfo); ContextHubClient client = new ContextHubClient(hubInfo, true /* persistent */); + String attributionTag = null; + if (context != null) { + attributionTag = context.getAttributionTag(); + } + IContextHubClient clientProxy; try { clientProxy = mService.createPendingIntentClient( - hubInfo.getId(), pendingIntent, nanoAppId); + hubInfo.getId(), pendingIntent, nanoAppId, attributionTag); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -939,6 +961,19 @@ public final class ContextHubManager { return client; } + /** + * Equivalent to {@link #createClient(ContextHubInfo, PendingIntent, long, String)} + * with {@link Context} being set to null. + */ + @RequiresPermission(anyOf = { + android.Manifest.permission.LOCATION_HARDWARE, + android.Manifest.permission.ACCESS_CONTEXT_HUB + }) + @NonNull public ContextHubClient createClient( + @NonNull ContextHubInfo hubInfo, @NonNull PendingIntent pendingIntent, long nanoAppId) { + return createClient(null /* context */, hubInfo, pendingIntent, nanoAppId); + } + /** * Unregister a callback for receive messages from the context hub. * diff --git a/core/java/android/hardware/location/IContextHubService.aidl b/core/java/android/hardware/location/IContextHubService.aidl index 04cc5634bf2cf..4961195a3017a 100644 --- a/core/java/android/hardware/location/IContextHubService.aidl +++ b/core/java/android/hardware/location/IContextHubService.aidl @@ -59,11 +59,13 @@ interface IContextHubService { int sendMessage(int contextHubHandle, int nanoAppHandle, in ContextHubMessage msg); // Creates a client to send and receive messages - IContextHubClient createClient(int contextHubId, in IContextHubClientCallback client); + IContextHubClient createClient( + int contextHubId, in IContextHubClientCallback client, in String attributionTag); // Creates a PendingIntent-based client to send and receive messages IContextHubClient createPendingIntentClient( - int contextHubId, in PendingIntent pendingIntent, long nanoAppId); + int contextHubId, in PendingIntent pendingIntent, long nanoAppId, + in String attributionTag); // Returns a list of ContextHub objects of available hubs List getContextHubs(); diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubService.java b/services/core/java/com/android/server/location/contexthub/ContextHubService.java index d3536345ccf1a..785e6745087f7 100644 --- a/services/core/java/com/android/server/location/contexthub/ContextHubService.java +++ b/services/core/java/com/android/server/location/contexthub/ContextHubService.java @@ -16,6 +16,7 @@ package com.android.server.location.contexthub; +import android.annotation.Nullable; import android.app.PendingIntent; import android.content.BroadcastReceiver; import android.content.Context; @@ -701,6 +702,7 @@ public class ContextHubService extends IContextHubService.Stub { * * @param contextHubId the ID of the hub this client is attached to * @param clientCallback the client interface to register with the service + * @param attributionTag an optional attribution tag within the given package * @return the generated client interface, null if registration was unsuccessful * @throws IllegalArgumentException if contextHubId is not a valid ID * @throws IllegalStateException if max number of clients have already registered @@ -708,7 +710,8 @@ public class ContextHubService extends IContextHubService.Stub { */ @Override public IContextHubClient createClient( - int contextHubId, IContextHubClientCallback clientCallback) throws RemoteException { + int contextHubId, IContextHubClientCallback clientCallback, + @Nullable String attributionTag) throws RemoteException { checkPermissions(); if (!isValidContextHubId(contextHubId)) { throw new IllegalArgumentException("Invalid context hub ID " + contextHubId); @@ -727,13 +730,15 @@ public class ContextHubService extends IContextHubService.Stub { * @param contextHubId the ID of the hub this client is attached to * @param pendingIntent the PendingIntent associated with this client * @param nanoAppId the ID of the nanoapp PendingIntent events will be sent for + * @param attributionTag an optional attribution tag within the given package * @return the generated client interface * @throws IllegalArgumentException if hubInfo does not represent a valid hub * @throws IllegalStateException if there were too many registered clients at the service */ @Override public IContextHubClient createPendingIntentClient( - int contextHubId, PendingIntent pendingIntent, long nanoAppId) throws RemoteException { + int contextHubId, PendingIntent pendingIntent, long nanoAppId, + @Nullable String attributionTag) throws RemoteException { checkPermissions(); if (!isValidContextHubId(contextHubId)) { throw new IllegalArgumentException("Invalid context hub ID " + contextHubId);