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
This commit is contained in:
Anthony Stange
2021-01-21 15:31:38 -05:00
parent 3f08df9fe9
commit 818d2dac54
7 changed files with 169 additions and 6 deletions

View File

@@ -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<java.lang.Void> 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<java.lang.String>);
method public int describeContents();
method public long getNanoAppId();
method @NonNull public java.util.List<java.lang.String> getNanoAppPermissions();
method public long getNanoAppVersion();
method public boolean isEnabled();
method public void writeToParcel(android.os.Parcel, int);

View File

@@ -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) {}
}

View File

@@ -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;
}

View File

@@ -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));
}
};
}

View File

@@ -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);
}

View File

@@ -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<String> mNanoAppPermissions;
public NanoAppState(long nanoAppId, int appVersion, boolean enabled) {
mNanoAppId = nanoAppId;
mNanoAppVersion = appVersion;
mIsEnabled = enabled;
mNanoAppPermissions = new ArrayList<String>();
}
public NanoAppState(long nanoAppId, int appVersion, boolean enabled,
@NonNull List<String> 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<String> getNanoAppPermissions() {
return mNanoAppPermissions;
}
private NanoAppState(Parcel in) {
mNanoAppId = in.readLong();
mNanoAppVersion = in.readInt();
mIsEnabled = (in.readInt() == 1);
mNanoAppPermissions = new ArrayList<String>();
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<NanoAppState> CREATOR =

View File

@@ -320,6 +320,10 @@ public class ContextHubService extends IContextHubService.Stub {
@Override
public void onNanoAppDisabled(long nanoAppId) {
}
@Override
public void onClientAuthorizationChanged(long nanoAppId, int authorization) {
}
};
}