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) { + } }; }