Loading core/api/system-current.txt +26 −0 Original line number Diff line number Diff line Loading @@ -5246,6 +5246,7 @@ package android.hardware.contexthub { @FlaggedApi("android.chre.flags.offload_api") public class HubEndpoint { method @Nullable public android.hardware.contexthub.IHubEndpointLifecycleCallback getLifecycleCallback(); method @Nullable public android.hardware.contexthub.IHubEndpointMessageCallback getMessageCallback(); method @Nullable public String getTag(); } Loading @@ -5254,6 +5255,8 @@ package android.hardware.contexthub { method @NonNull public android.hardware.contexthub.HubEndpoint build(); method @NonNull public android.hardware.contexthub.HubEndpoint.Builder setLifecycleCallback(@NonNull android.hardware.contexthub.IHubEndpointLifecycleCallback); method @NonNull public android.hardware.contexthub.HubEndpoint.Builder setLifecycleCallback(@NonNull java.util.concurrent.Executor, @NonNull android.hardware.contexthub.IHubEndpointLifecycleCallback); method @NonNull public android.hardware.contexthub.HubEndpoint.Builder setMessageCallback(@NonNull android.hardware.contexthub.IHubEndpointMessageCallback); method @NonNull public android.hardware.contexthub.HubEndpoint.Builder setMessageCallback(@NonNull java.util.concurrent.Executor, @NonNull android.hardware.contexthub.IHubEndpointMessageCallback); method @NonNull public android.hardware.contexthub.HubEndpoint.Builder setTag(@NonNull String); } Loading @@ -5273,6 +5276,7 @@ package android.hardware.contexthub { @FlaggedApi("android.chre.flags.offload_api") public class HubEndpointSession implements java.lang.AutoCloseable { method public void close(); method @NonNull public android.hardware.location.ContextHubTransaction<java.lang.Void> sendMessage(@NonNull android.hardware.contexthub.HubMessage); } @FlaggedApi("android.chre.flags.offload_api") public class HubEndpointSessionResult { Loading @@ -5282,6 +5286,22 @@ package android.hardware.contexthub { method @NonNull public static android.hardware.contexthub.HubEndpointSessionResult reject(@NonNull String); } @FlaggedApi("android.chre.flags.offload_api") public final class HubMessage implements android.os.Parcelable { method @NonNull public static android.hardware.contexthub.HubMessage createMessage(int, @NonNull byte[]); method @NonNull public static android.hardware.contexthub.HubMessage createMessage(int, @NonNull byte[], @NonNull android.hardware.contexthub.HubMessage.DeliveryParams); method public int describeContents(); method @NonNull public byte[] getMessageBody(); method public int getMessageType(); method public void writeToParcel(@NonNull android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.hardware.contexthub.HubMessage> CREATOR; } public static class HubMessage.DeliveryParams { method public boolean isResponseRequired(); method @NonNull public static android.hardware.contexthub.HubMessage.DeliveryParams makeBasic(); method @NonNull public android.hardware.contexthub.HubMessage.DeliveryParams setResponseRequired(boolean); } @FlaggedApi("android.chre.flags.offload_api") public interface IHubEndpointLifecycleCallback { method public void onSessionClosed(@NonNull android.hardware.contexthub.HubEndpointSession, int); method @NonNull public android.hardware.contexthub.HubEndpointSessionResult onSessionOpenRequest(@NonNull android.hardware.contexthub.HubEndpointInfo); Loading @@ -5291,6 +5311,10 @@ package android.hardware.contexthub { field public static final int REASON_UNSPECIFIED = 0; // 0x0 } @FlaggedApi("android.chre.flags.offload_api") public interface IHubEndpointMessageCallback { method public void onMessageReceived(@NonNull android.hardware.contexthub.HubEndpointSession, @NonNull android.hardware.contexthub.HubMessage); } } package android.hardware.devicestate { Loading Loading @@ -6358,6 +6382,8 @@ package android.hardware.location { field public static final int RESULT_SUCCESS = 0; // 0x0 field public static final int TYPE_DISABLE_NANOAPP = 3; // 0x3 field public static final int TYPE_ENABLE_NANOAPP = 2; // 0x2 field @FlaggedApi("android.chre.flags.offload_api") public static final int TYPE_HUB_MESSAGE_DEFAULT = 6; // 0x6 field @FlaggedApi("android.chre.flags.offload_api") public static final int TYPE_HUB_MESSAGE_REQUIRES_RESPONSE = 7; // 0x7 field public static final int TYPE_LOAD_NANOAPP = 0; // 0x0 field public static final int TYPE_QUERY_NANOAPPS = 4; // 0x4 field public static final int TYPE_RELIABLE_MESSAGE = 5; // 0x5 core/java/android/hardware/contexthub/HubEndpoint.java +103 −2 Original line number Diff line number Diff line Loading @@ -24,6 +24,7 @@ import android.annotation.SystemApi; import android.chre.flags.Flags; import android.content.Context; import android.hardware.location.IContextHubService; import android.hardware.location.IContextHubTransactionCallback; import android.os.RemoteException; import android.util.Log; import android.util.SparseArray; Loading @@ -46,7 +47,9 @@ public class HubEndpoint { private final Object mLock = new Object(); private final HubEndpointInfo mPendingHubEndpointInfo; @Nullable private final IHubEndpointLifecycleCallback mLifecycleCallback; @Nullable private final IHubEndpointMessageCallback mMessageCallback; @NonNull private final Executor mLifecycleCallbackExecutor; @NonNull private final Executor mMessageCallbackExecutor; @GuardedBy("mLock") private final SparseArray<HubEndpointSession> mActiveSessions = new SparseArray<>(); Loading Loading @@ -216,6 +219,50 @@ public class HubEndpoint { }); } } @Override public void onMessageReceived(int sessionId, HubMessage message) throws RemoteException { final HubEndpointSession activeSession; // Retrieve the active session synchronized (mLock) { activeSession = mActiveSessions.get(sessionId); } if (activeSession == null) { Log.i(TAG, "onMessageReceived: session not active, id=" + sessionId); } if (activeSession == null || mMessageCallback == null) { if (message.getDeliveryParams().isResponseRequired()) { try { mServiceToken.sendMessageDeliveryStatus( sessionId, message.getMessageSequenceNumber(), ErrorCode.DESTINATION_NOT_FOUND); } catch (RemoteException e) { e.rethrowFromSystemServer(); } } return; } // Execute the callback mMessageCallbackExecutor.execute( () -> { mMessageCallback.onMessageReceived(activeSession, message); if (message.getDeliveryParams().isResponseRequired()) { try { mServiceToken.sendMessageDeliveryStatus( sessionId, message.getMessageSequenceNumber(), ErrorCode.OK); } catch (RemoteException e) { e.rethrowFromSystemServer(); } } }); } }; /** Binder returned from system service, non-null while registered. */ Loading @@ -227,10 +274,15 @@ public class HubEndpoint { private HubEndpoint( @NonNull HubEndpointInfo pendingEndpointInfo, @Nullable IHubEndpointLifecycleCallback endpointLifecycleCallback, @NonNull Executor lifecycleCallbackExecutor) { @NonNull Executor lifecycleCallbackExecutor, @Nullable IHubEndpointMessageCallback endpointMessageCallback, @NonNull Executor messageCallbackExecutor) { mPendingHubEndpointInfo = pendingEndpointInfo; mLifecycleCallback = endpointLifecycleCallback; mLifecycleCallbackExecutor = lifecycleCallbackExecutor; mMessageCallback = endpointMessageCallback; mMessageCallbackExecutor = messageCallbackExecutor; } /** @hide */ Loading Loading @@ -337,6 +389,24 @@ public class HubEndpoint { } } void sendMessage( HubEndpointSession session, HubMessage message, @Nullable IContextHubTransactionCallback transactionCallback) { IContextHubEndpoint serviceToken = mServiceToken; if (serviceToken == null) { // Not registered return; } try { serviceToken.sendMessage(session.getId(), message, transactionCallback); } catch (RemoteException e) { Log.e(TAG, "sendMessage: failed to send message session=" + session, e); e.rethrowFromSystemServer(); } } @Nullable public String getTag() { return mPendingHubEndpointInfo.getTag(); Loading @@ -347,6 +417,11 @@ public class HubEndpoint { return mLifecycleCallback; } @Nullable public IHubEndpointMessageCallback getMessageCallback() { return mMessageCallback; } /** Builder for a {@link HubEndpoint} object. */ public static final class Builder { private final String mPackageName; Loading @@ -355,12 +430,16 @@ public class HubEndpoint { @NonNull private Executor mLifecycleCallbackExecutor; @Nullable private IHubEndpointMessageCallback mMessageCallback; @NonNull private Executor mMessageCallbackExecutor; @Nullable private String mTag; /** Create a builder for {@link HubEndpoint} */ public Builder(@NonNull Context context) { mPackageName = context.getPackageName(); mLifecycleCallbackExecutor = context.getMainExecutor(); mMessageCallbackExecutor = context.getMainExecutor(); } /** Loading Loading @@ -394,13 +473,35 @@ public class HubEndpoint { return this; } /** Attach a callback interface for message events for this Endpoint */ @NonNull public Builder setMessageCallback(@NonNull IHubEndpointMessageCallback messageCallback) { mMessageCallback = messageCallback; return this; } /** * Attach a callback interface for message events for this Endpoint with a specified * executor */ @NonNull public Builder setMessageCallback( @NonNull @CallbackExecutor Executor executor, @NonNull IHubEndpointMessageCallback messageCallback) { mMessageCallbackExecutor = executor; mMessageCallback = messageCallback; return this; } /** Build the {@link HubEndpoint} object. */ @NonNull public HubEndpoint build() { return new HubEndpoint( new HubEndpointInfo(mPackageName, mTag), mLifecycleCallback, mLifecycleCallbackExecutor); mLifecycleCallbackExecutor, mMessageCallback, mMessageCallbackExecutor); } } } core/java/android/hardware/contexthub/HubEndpointSession.java +39 −3 Original line number Diff line number Diff line Loading @@ -20,6 +20,9 @@ import android.annotation.FlaggedApi; import android.annotation.NonNull; import android.annotation.SystemApi; import android.chre.flags.Flags; import android.hardware.location.ContextHubTransaction; import android.hardware.location.ContextHubTransactionHelper; import android.hardware.location.IContextHubTransactionCallback; import android.util.CloseGuard; import java.util.concurrent.atomic.AtomicBoolean; Loading @@ -27,8 +30,6 @@ import java.util.concurrent.atomic.AtomicBoolean; /** * An object representing a communication session between two different hub endpoints. * * <p>A published enpoint can receive * * @hide */ @SystemApi Loading @@ -38,7 +39,6 @@ public class HubEndpointSession implements AutoCloseable { private final int mId; // TODO(b/377717509): Implement Message sending API & interface @NonNull private final HubEndpoint mHubEndpoint; @NonNull private final HubEndpointInfo mInitiator; @NonNull private final HubEndpointInfo mDestination; Loading @@ -57,6 +57,42 @@ public class HubEndpointSession implements AutoCloseable { mInitiator = initiator; } /** * Send a message to the peer endpoint in this session. * * @param message The message object constructed with {@link HubMessage#createMessage}. * @return For messages that does not require a response, the transaction will immediately * complete. For messages that requires a response, the transaction will complete after * receiving the response for the message. */ @NonNull public ContextHubTransaction<Void> sendMessage(@NonNull HubMessage message) { if (mIsClosed.get()) { throw new IllegalStateException("Session is already closed."); } boolean isResponseRequired = message.getDeliveryParams().isResponseRequired(); ContextHubTransaction<Void> ret = new ContextHubTransaction<>( isResponseRequired ? ContextHubTransaction.TYPE_HUB_MESSAGE_REQUIRES_RESPONSE : ContextHubTransaction.TYPE_HUB_MESSAGE_DEFAULT); if (!isResponseRequired) { // If the message doesn't require acknowledgement, respond with success immediately // TODO(b/379162322): Improve handling of synchronous failures. mHubEndpoint.sendMessage(this, message, null); ret.setResponse( new ContextHubTransaction.Response<>( ContextHubTransaction.RESULT_SUCCESS, null)); } else { IContextHubTransactionCallback callback = ContextHubTransactionHelper.createTransactionCallback(ret); // Sequence number will be assigned at the service mHubEndpoint.sendMessage(this, message, callback); } return ret; } /** @hide */ public int getId() { return mId; Loading core/java/android/hardware/contexthub/HubMessage.aidl 0 → 100644 +22 −0 Original line number Diff line number Diff line /* * Copyright 2024 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.hardware.contexthub; /** * @hide */ parcelable HubMessage; core/java/android/hardware/contexthub/HubMessage.java 0 → 100644 +289 −0 Original line number Diff line number Diff line /* * Copyright 2024 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.hardware.contexthub; import android.annotation.FlaggedApi; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; import android.chre.flags.Flags; import android.os.Parcel; import android.os.Parcelable; import libcore.util.HexEncoding; import java.util.Arrays; import java.util.Objects; /** * A class describing general messages send through the Context Hub Service. * * @hide */ @SystemApi @FlaggedApi(Flags.FLAG_OFFLOAD_API) public final class HubMessage implements Parcelable { private static final int DEBUG_LOG_NUM_BYTES = 16; private final int mMessageType; private final byte[] mMessageBody; private final DeliveryParams mDeliveryParams; private int mMessageSequenceNumber; /** * Configurable options for message delivery. This option can be passed into {@link * HubEndpointSession#sendMessage} to specify the behavior of message delivery. */ public static class DeliveryParams { private boolean mResponseRequired; private DeliveryParams(boolean responseRequired) { mResponseRequired = responseRequired; } /** Get the acknowledgement requirement. */ public boolean isResponseRequired() { return mResponseRequired; } /** * Set the response requirement for a message. Message sent with this option will have a * {@link android.hardware.location.ContextHubTransaction.Response} when the peer received * the message. Default is false. */ @NonNull public DeliveryParams setResponseRequired(boolean required) { mResponseRequired = required; return this; } /** Construct a default delivery option. */ @NonNull public static DeliveryParams makeBasic() { return new DeliveryParams(false); } @Override public String toString() { StringBuilder out = new StringBuilder(); out.append("DeliveryParams["); out.append("responseRequired = ").append(mResponseRequired); out.append("]"); return out.toString(); } @Override public int hashCode() { return Objects.hash(mResponseRequired); } @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (obj instanceof DeliveryParams other) { return other.mResponseRequired == mResponseRequired; } return false; } } private HubMessage(int messageType, byte[] messageBody, DeliveryParams deliveryParams) { mMessageType = messageType; mMessageBody = messageBody; mDeliveryParams = deliveryParams; } /** * Creates a HubMessage object to send to through an endpoint. * * @param messageType the endpoint & service dependent message type * @param messageBody the byte array message contents * @return the HubMessage object */ @NonNull public static HubMessage createMessage(int messageType, @NonNull byte[] messageBody) { return new HubMessage(messageType, messageBody, DeliveryParams.makeBasic()); } /** * Creates a HubMessage object to send to through an endpoint. * * @param messageType the endpoint & service dependent message type * @param messageBody the byte array message contents * @param deliveryParams The message delivery parameters. See {@link HubMessage.DeliveryParams} * for more details. * @return the HubMessage object */ @NonNull public static HubMessage createMessage( int messageType, @NonNull byte[] messageBody, @NonNull DeliveryParams deliveryParams) { return new HubMessage(messageType, messageBody, deliveryParams); } /** * Retrieve the message type. * * @return the type of the message */ public int getMessageType() { return mMessageType; } /** * Retrieve the body of the message. The body can be an empty byte array. * * @return the byte array contents of the message */ @NonNull public byte[] getMessageBody() { return mMessageBody; } /** * Retrieve the {@link DeliveryParams} object specifying the behavior of message delivery. * * @hide */ public DeliveryParams getDeliveryParams() { return mDeliveryParams; } /** * Assign a message sequence number. This should only be called by the system service. * * @hide */ public void setMessageSequenceNumber(int messageSequenceNumber) { mMessageSequenceNumber = messageSequenceNumber; } /** * Returns the message sequence number. The default value is 0. * * @return the message sequence number of the message * @hide */ public int getMessageSequenceNumber() { return mMessageSequenceNumber; } private HubMessage(@NonNull Parcel in) { mMessageType = in.readInt(); int msgSize = in.readInt(); mMessageBody = new byte[msgSize]; in.readByteArray(mMessageBody); mDeliveryParams = DeliveryParams.makeBasic(); mDeliveryParams.setResponseRequired(in.readInt() == 1); mMessageSequenceNumber = in.readInt(); } @Override public int describeContents() { return 0; } @Override public void writeToParcel(@NonNull Parcel out, int flags) { out.writeInt(mMessageType); out.writeInt(mMessageBody.length); out.writeByteArray(mMessageBody); out.writeInt(mDeliveryParams.isResponseRequired() ? 1 : 0); out.writeInt(mMessageSequenceNumber); } public static final @NonNull Creator<HubMessage> CREATOR = new Creator<>() { @Override public HubMessage createFromParcel(Parcel in) { return new HubMessage(in); } @Override public HubMessage[] newArray(int size) { return new HubMessage[size]; } }; @NonNull @Override public String toString() { int length = mMessageBody.length; StringBuilder out = new StringBuilder(); out.append("HubMessage[type = ").append(mMessageType); out.append(", length = ").append(mMessageBody.length); out.append(", messageSequenceNumber = ").append(mMessageSequenceNumber); out.append(", deliveryParams = ").append(mDeliveryParams); out.append("]("); if (length > 0) { out.append("data = 0x"); } for (int i = 0; i < Math.min(length, DEBUG_LOG_NUM_BYTES); i++) { out.append(HexEncoding.encodeToString(mMessageBody[i], true /* upperCase */)); if ((i + 1) % 4 == 0) { out.append(" "); } } if (length > DEBUG_LOG_NUM_BYTES) { out.append("..."); } out.append(")"); return out.toString(); } @Override public boolean equals(@Nullable Object object) { if (object == this) { return true; } boolean isEqual = false; if (object instanceof HubMessage other) { isEqual = (other.getMessageType() == mMessageType) && Arrays.equals(other.getMessageBody(), mMessageBody) && (other.getDeliveryParams().equals(mDeliveryParams)) && (other.getMessageSequenceNumber() == mMessageSequenceNumber); } return isEqual; } @Override public int hashCode() { if (!Flags.fixApiCheck()) { return super.hashCode(); } return Objects.hash( mMessageType, Arrays.hashCode(mMessageBody), mDeliveryParams, mMessageSequenceNumber); } } Loading
core/api/system-current.txt +26 −0 Original line number Diff line number Diff line Loading @@ -5246,6 +5246,7 @@ package android.hardware.contexthub { @FlaggedApi("android.chre.flags.offload_api") public class HubEndpoint { method @Nullable public android.hardware.contexthub.IHubEndpointLifecycleCallback getLifecycleCallback(); method @Nullable public android.hardware.contexthub.IHubEndpointMessageCallback getMessageCallback(); method @Nullable public String getTag(); } Loading @@ -5254,6 +5255,8 @@ package android.hardware.contexthub { method @NonNull public android.hardware.contexthub.HubEndpoint build(); method @NonNull public android.hardware.contexthub.HubEndpoint.Builder setLifecycleCallback(@NonNull android.hardware.contexthub.IHubEndpointLifecycleCallback); method @NonNull public android.hardware.contexthub.HubEndpoint.Builder setLifecycleCallback(@NonNull java.util.concurrent.Executor, @NonNull android.hardware.contexthub.IHubEndpointLifecycleCallback); method @NonNull public android.hardware.contexthub.HubEndpoint.Builder setMessageCallback(@NonNull android.hardware.contexthub.IHubEndpointMessageCallback); method @NonNull public android.hardware.contexthub.HubEndpoint.Builder setMessageCallback(@NonNull java.util.concurrent.Executor, @NonNull android.hardware.contexthub.IHubEndpointMessageCallback); method @NonNull public android.hardware.contexthub.HubEndpoint.Builder setTag(@NonNull String); } Loading @@ -5273,6 +5276,7 @@ package android.hardware.contexthub { @FlaggedApi("android.chre.flags.offload_api") public class HubEndpointSession implements java.lang.AutoCloseable { method public void close(); method @NonNull public android.hardware.location.ContextHubTransaction<java.lang.Void> sendMessage(@NonNull android.hardware.contexthub.HubMessage); } @FlaggedApi("android.chre.flags.offload_api") public class HubEndpointSessionResult { Loading @@ -5282,6 +5286,22 @@ package android.hardware.contexthub { method @NonNull public static android.hardware.contexthub.HubEndpointSessionResult reject(@NonNull String); } @FlaggedApi("android.chre.flags.offload_api") public final class HubMessage implements android.os.Parcelable { method @NonNull public static android.hardware.contexthub.HubMessage createMessage(int, @NonNull byte[]); method @NonNull public static android.hardware.contexthub.HubMessage createMessage(int, @NonNull byte[], @NonNull android.hardware.contexthub.HubMessage.DeliveryParams); method public int describeContents(); method @NonNull public byte[] getMessageBody(); method public int getMessageType(); method public void writeToParcel(@NonNull android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.hardware.contexthub.HubMessage> CREATOR; } public static class HubMessage.DeliveryParams { method public boolean isResponseRequired(); method @NonNull public static android.hardware.contexthub.HubMessage.DeliveryParams makeBasic(); method @NonNull public android.hardware.contexthub.HubMessage.DeliveryParams setResponseRequired(boolean); } @FlaggedApi("android.chre.flags.offload_api") public interface IHubEndpointLifecycleCallback { method public void onSessionClosed(@NonNull android.hardware.contexthub.HubEndpointSession, int); method @NonNull public android.hardware.contexthub.HubEndpointSessionResult onSessionOpenRequest(@NonNull android.hardware.contexthub.HubEndpointInfo); Loading @@ -5291,6 +5311,10 @@ package android.hardware.contexthub { field public static final int REASON_UNSPECIFIED = 0; // 0x0 } @FlaggedApi("android.chre.flags.offload_api") public interface IHubEndpointMessageCallback { method public void onMessageReceived(@NonNull android.hardware.contexthub.HubEndpointSession, @NonNull android.hardware.contexthub.HubMessage); } } package android.hardware.devicestate { Loading Loading @@ -6358,6 +6382,8 @@ package android.hardware.location { field public static final int RESULT_SUCCESS = 0; // 0x0 field public static final int TYPE_DISABLE_NANOAPP = 3; // 0x3 field public static final int TYPE_ENABLE_NANOAPP = 2; // 0x2 field @FlaggedApi("android.chre.flags.offload_api") public static final int TYPE_HUB_MESSAGE_DEFAULT = 6; // 0x6 field @FlaggedApi("android.chre.flags.offload_api") public static final int TYPE_HUB_MESSAGE_REQUIRES_RESPONSE = 7; // 0x7 field public static final int TYPE_LOAD_NANOAPP = 0; // 0x0 field public static final int TYPE_QUERY_NANOAPPS = 4; // 0x4 field public static final int TYPE_RELIABLE_MESSAGE = 5; // 0x5
core/java/android/hardware/contexthub/HubEndpoint.java +103 −2 Original line number Diff line number Diff line Loading @@ -24,6 +24,7 @@ import android.annotation.SystemApi; import android.chre.flags.Flags; import android.content.Context; import android.hardware.location.IContextHubService; import android.hardware.location.IContextHubTransactionCallback; import android.os.RemoteException; import android.util.Log; import android.util.SparseArray; Loading @@ -46,7 +47,9 @@ public class HubEndpoint { private final Object mLock = new Object(); private final HubEndpointInfo mPendingHubEndpointInfo; @Nullable private final IHubEndpointLifecycleCallback mLifecycleCallback; @Nullable private final IHubEndpointMessageCallback mMessageCallback; @NonNull private final Executor mLifecycleCallbackExecutor; @NonNull private final Executor mMessageCallbackExecutor; @GuardedBy("mLock") private final SparseArray<HubEndpointSession> mActiveSessions = new SparseArray<>(); Loading Loading @@ -216,6 +219,50 @@ public class HubEndpoint { }); } } @Override public void onMessageReceived(int sessionId, HubMessage message) throws RemoteException { final HubEndpointSession activeSession; // Retrieve the active session synchronized (mLock) { activeSession = mActiveSessions.get(sessionId); } if (activeSession == null) { Log.i(TAG, "onMessageReceived: session not active, id=" + sessionId); } if (activeSession == null || mMessageCallback == null) { if (message.getDeliveryParams().isResponseRequired()) { try { mServiceToken.sendMessageDeliveryStatus( sessionId, message.getMessageSequenceNumber(), ErrorCode.DESTINATION_NOT_FOUND); } catch (RemoteException e) { e.rethrowFromSystemServer(); } } return; } // Execute the callback mMessageCallbackExecutor.execute( () -> { mMessageCallback.onMessageReceived(activeSession, message); if (message.getDeliveryParams().isResponseRequired()) { try { mServiceToken.sendMessageDeliveryStatus( sessionId, message.getMessageSequenceNumber(), ErrorCode.OK); } catch (RemoteException e) { e.rethrowFromSystemServer(); } } }); } }; /** Binder returned from system service, non-null while registered. */ Loading @@ -227,10 +274,15 @@ public class HubEndpoint { private HubEndpoint( @NonNull HubEndpointInfo pendingEndpointInfo, @Nullable IHubEndpointLifecycleCallback endpointLifecycleCallback, @NonNull Executor lifecycleCallbackExecutor) { @NonNull Executor lifecycleCallbackExecutor, @Nullable IHubEndpointMessageCallback endpointMessageCallback, @NonNull Executor messageCallbackExecutor) { mPendingHubEndpointInfo = pendingEndpointInfo; mLifecycleCallback = endpointLifecycleCallback; mLifecycleCallbackExecutor = lifecycleCallbackExecutor; mMessageCallback = endpointMessageCallback; mMessageCallbackExecutor = messageCallbackExecutor; } /** @hide */ Loading Loading @@ -337,6 +389,24 @@ public class HubEndpoint { } } void sendMessage( HubEndpointSession session, HubMessage message, @Nullable IContextHubTransactionCallback transactionCallback) { IContextHubEndpoint serviceToken = mServiceToken; if (serviceToken == null) { // Not registered return; } try { serviceToken.sendMessage(session.getId(), message, transactionCallback); } catch (RemoteException e) { Log.e(TAG, "sendMessage: failed to send message session=" + session, e); e.rethrowFromSystemServer(); } } @Nullable public String getTag() { return mPendingHubEndpointInfo.getTag(); Loading @@ -347,6 +417,11 @@ public class HubEndpoint { return mLifecycleCallback; } @Nullable public IHubEndpointMessageCallback getMessageCallback() { return mMessageCallback; } /** Builder for a {@link HubEndpoint} object. */ public static final class Builder { private final String mPackageName; Loading @@ -355,12 +430,16 @@ public class HubEndpoint { @NonNull private Executor mLifecycleCallbackExecutor; @Nullable private IHubEndpointMessageCallback mMessageCallback; @NonNull private Executor mMessageCallbackExecutor; @Nullable private String mTag; /** Create a builder for {@link HubEndpoint} */ public Builder(@NonNull Context context) { mPackageName = context.getPackageName(); mLifecycleCallbackExecutor = context.getMainExecutor(); mMessageCallbackExecutor = context.getMainExecutor(); } /** Loading Loading @@ -394,13 +473,35 @@ public class HubEndpoint { return this; } /** Attach a callback interface for message events for this Endpoint */ @NonNull public Builder setMessageCallback(@NonNull IHubEndpointMessageCallback messageCallback) { mMessageCallback = messageCallback; return this; } /** * Attach a callback interface for message events for this Endpoint with a specified * executor */ @NonNull public Builder setMessageCallback( @NonNull @CallbackExecutor Executor executor, @NonNull IHubEndpointMessageCallback messageCallback) { mMessageCallbackExecutor = executor; mMessageCallback = messageCallback; return this; } /** Build the {@link HubEndpoint} object. */ @NonNull public HubEndpoint build() { return new HubEndpoint( new HubEndpointInfo(mPackageName, mTag), mLifecycleCallback, mLifecycleCallbackExecutor); mLifecycleCallbackExecutor, mMessageCallback, mMessageCallbackExecutor); } } }
core/java/android/hardware/contexthub/HubEndpointSession.java +39 −3 Original line number Diff line number Diff line Loading @@ -20,6 +20,9 @@ import android.annotation.FlaggedApi; import android.annotation.NonNull; import android.annotation.SystemApi; import android.chre.flags.Flags; import android.hardware.location.ContextHubTransaction; import android.hardware.location.ContextHubTransactionHelper; import android.hardware.location.IContextHubTransactionCallback; import android.util.CloseGuard; import java.util.concurrent.atomic.AtomicBoolean; Loading @@ -27,8 +30,6 @@ import java.util.concurrent.atomic.AtomicBoolean; /** * An object representing a communication session between two different hub endpoints. * * <p>A published enpoint can receive * * @hide */ @SystemApi Loading @@ -38,7 +39,6 @@ public class HubEndpointSession implements AutoCloseable { private final int mId; // TODO(b/377717509): Implement Message sending API & interface @NonNull private final HubEndpoint mHubEndpoint; @NonNull private final HubEndpointInfo mInitiator; @NonNull private final HubEndpointInfo mDestination; Loading @@ -57,6 +57,42 @@ public class HubEndpointSession implements AutoCloseable { mInitiator = initiator; } /** * Send a message to the peer endpoint in this session. * * @param message The message object constructed with {@link HubMessage#createMessage}. * @return For messages that does not require a response, the transaction will immediately * complete. For messages that requires a response, the transaction will complete after * receiving the response for the message. */ @NonNull public ContextHubTransaction<Void> sendMessage(@NonNull HubMessage message) { if (mIsClosed.get()) { throw new IllegalStateException("Session is already closed."); } boolean isResponseRequired = message.getDeliveryParams().isResponseRequired(); ContextHubTransaction<Void> ret = new ContextHubTransaction<>( isResponseRequired ? ContextHubTransaction.TYPE_HUB_MESSAGE_REQUIRES_RESPONSE : ContextHubTransaction.TYPE_HUB_MESSAGE_DEFAULT); if (!isResponseRequired) { // If the message doesn't require acknowledgement, respond with success immediately // TODO(b/379162322): Improve handling of synchronous failures. mHubEndpoint.sendMessage(this, message, null); ret.setResponse( new ContextHubTransaction.Response<>( ContextHubTransaction.RESULT_SUCCESS, null)); } else { IContextHubTransactionCallback callback = ContextHubTransactionHelper.createTransactionCallback(ret); // Sequence number will be assigned at the service mHubEndpoint.sendMessage(this, message, callback); } return ret; } /** @hide */ public int getId() { return mId; Loading
core/java/android/hardware/contexthub/HubMessage.aidl 0 → 100644 +22 −0 Original line number Diff line number Diff line /* * Copyright 2024 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.hardware.contexthub; /** * @hide */ parcelable HubMessage;
core/java/android/hardware/contexthub/HubMessage.java 0 → 100644 +289 −0 Original line number Diff line number Diff line /* * Copyright 2024 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.hardware.contexthub; import android.annotation.FlaggedApi; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; import android.chre.flags.Flags; import android.os.Parcel; import android.os.Parcelable; import libcore.util.HexEncoding; import java.util.Arrays; import java.util.Objects; /** * A class describing general messages send through the Context Hub Service. * * @hide */ @SystemApi @FlaggedApi(Flags.FLAG_OFFLOAD_API) public final class HubMessage implements Parcelable { private static final int DEBUG_LOG_NUM_BYTES = 16; private final int mMessageType; private final byte[] mMessageBody; private final DeliveryParams mDeliveryParams; private int mMessageSequenceNumber; /** * Configurable options for message delivery. This option can be passed into {@link * HubEndpointSession#sendMessage} to specify the behavior of message delivery. */ public static class DeliveryParams { private boolean mResponseRequired; private DeliveryParams(boolean responseRequired) { mResponseRequired = responseRequired; } /** Get the acknowledgement requirement. */ public boolean isResponseRequired() { return mResponseRequired; } /** * Set the response requirement for a message. Message sent with this option will have a * {@link android.hardware.location.ContextHubTransaction.Response} when the peer received * the message. Default is false. */ @NonNull public DeliveryParams setResponseRequired(boolean required) { mResponseRequired = required; return this; } /** Construct a default delivery option. */ @NonNull public static DeliveryParams makeBasic() { return new DeliveryParams(false); } @Override public String toString() { StringBuilder out = new StringBuilder(); out.append("DeliveryParams["); out.append("responseRequired = ").append(mResponseRequired); out.append("]"); return out.toString(); } @Override public int hashCode() { return Objects.hash(mResponseRequired); } @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (obj instanceof DeliveryParams other) { return other.mResponseRequired == mResponseRequired; } return false; } } private HubMessage(int messageType, byte[] messageBody, DeliveryParams deliveryParams) { mMessageType = messageType; mMessageBody = messageBody; mDeliveryParams = deliveryParams; } /** * Creates a HubMessage object to send to through an endpoint. * * @param messageType the endpoint & service dependent message type * @param messageBody the byte array message contents * @return the HubMessage object */ @NonNull public static HubMessage createMessage(int messageType, @NonNull byte[] messageBody) { return new HubMessage(messageType, messageBody, DeliveryParams.makeBasic()); } /** * Creates a HubMessage object to send to through an endpoint. * * @param messageType the endpoint & service dependent message type * @param messageBody the byte array message contents * @param deliveryParams The message delivery parameters. See {@link HubMessage.DeliveryParams} * for more details. * @return the HubMessage object */ @NonNull public static HubMessage createMessage( int messageType, @NonNull byte[] messageBody, @NonNull DeliveryParams deliveryParams) { return new HubMessage(messageType, messageBody, deliveryParams); } /** * Retrieve the message type. * * @return the type of the message */ public int getMessageType() { return mMessageType; } /** * Retrieve the body of the message. The body can be an empty byte array. * * @return the byte array contents of the message */ @NonNull public byte[] getMessageBody() { return mMessageBody; } /** * Retrieve the {@link DeliveryParams} object specifying the behavior of message delivery. * * @hide */ public DeliveryParams getDeliveryParams() { return mDeliveryParams; } /** * Assign a message sequence number. This should only be called by the system service. * * @hide */ public void setMessageSequenceNumber(int messageSequenceNumber) { mMessageSequenceNumber = messageSequenceNumber; } /** * Returns the message sequence number. The default value is 0. * * @return the message sequence number of the message * @hide */ public int getMessageSequenceNumber() { return mMessageSequenceNumber; } private HubMessage(@NonNull Parcel in) { mMessageType = in.readInt(); int msgSize = in.readInt(); mMessageBody = new byte[msgSize]; in.readByteArray(mMessageBody); mDeliveryParams = DeliveryParams.makeBasic(); mDeliveryParams.setResponseRequired(in.readInt() == 1); mMessageSequenceNumber = in.readInt(); } @Override public int describeContents() { return 0; } @Override public void writeToParcel(@NonNull Parcel out, int flags) { out.writeInt(mMessageType); out.writeInt(mMessageBody.length); out.writeByteArray(mMessageBody); out.writeInt(mDeliveryParams.isResponseRequired() ? 1 : 0); out.writeInt(mMessageSequenceNumber); } public static final @NonNull Creator<HubMessage> CREATOR = new Creator<>() { @Override public HubMessage createFromParcel(Parcel in) { return new HubMessage(in); } @Override public HubMessage[] newArray(int size) { return new HubMessage[size]; } }; @NonNull @Override public String toString() { int length = mMessageBody.length; StringBuilder out = new StringBuilder(); out.append("HubMessage[type = ").append(mMessageType); out.append(", length = ").append(mMessageBody.length); out.append(", messageSequenceNumber = ").append(mMessageSequenceNumber); out.append(", deliveryParams = ").append(mDeliveryParams); out.append("]("); if (length > 0) { out.append("data = 0x"); } for (int i = 0; i < Math.min(length, DEBUG_LOG_NUM_BYTES); i++) { out.append(HexEncoding.encodeToString(mMessageBody[i], true /* upperCase */)); if ((i + 1) % 4 == 0) { out.append(" "); } } if (length > DEBUG_LOG_NUM_BYTES) { out.append("..."); } out.append(")"); return out.toString(); } @Override public boolean equals(@Nullable Object object) { if (object == this) { return true; } boolean isEqual = false; if (object instanceof HubMessage other) { isEqual = (other.getMessageType() == mMessageType) && Arrays.equals(other.getMessageBody(), mMessageBody) && (other.getDeliveryParams().equals(mDeliveryParams)) && (other.getMessageSequenceNumber() == mMessageSequenceNumber); } return isEqual; } @Override public int hashCode() { if (!Flags.fixApiCheck()) { return super.hashCode(); } return Objects.hash( mMessageType, Arrays.hashCode(mMessageBody), mDeliveryParams, mMessageSequenceNumber); } }