1 /* 2 * Copyright (C) 2024 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.hardware.contexthub; 18 19 import android.annotation.FlaggedApi; 20 import android.annotation.NonNull; 21 import android.annotation.Nullable; 22 import android.annotation.RequiresPermission; 23 import android.annotation.SystemApi; 24 import android.chre.flags.Flags; 25 import android.hardware.location.ContextHubTransaction; 26 import android.hardware.location.ContextHubTransactionHelper; 27 import android.hardware.location.IContextHubTransactionCallback; 28 import android.util.CloseGuard; 29 30 import java.util.Objects; 31 import java.util.concurrent.atomic.AtomicBoolean; 32 33 /** 34 * An object representing a communication session between two different hub endpoints. 35 * 36 * @hide 37 */ 38 @SystemApi 39 @FlaggedApi(Flags.FLAG_OFFLOAD_API) 40 public class HubEndpointSession implements AutoCloseable { 41 private final CloseGuard mCloseGuard = new CloseGuard(); 42 43 private final int mId; 44 45 @NonNull private final HubEndpoint mHubEndpoint; 46 @NonNull private final HubEndpointInfo mInitiator; 47 @NonNull private final HubEndpointInfo mDestination; 48 @Nullable private final String mServiceDescriptor; 49 50 private final AtomicBoolean mIsClosed = new AtomicBoolean(true); 51 52 /** @hide */ HubEndpointSession( int id, @NonNull HubEndpoint hubEndpoint, @NonNull HubEndpointInfo destination, @NonNull HubEndpointInfo initiator, @Nullable String serviceDescriptor)53 HubEndpointSession( 54 int id, 55 @NonNull HubEndpoint hubEndpoint, 56 @NonNull HubEndpointInfo destination, 57 @NonNull HubEndpointInfo initiator, 58 @Nullable String serviceDescriptor) { 59 mId = id; 60 mHubEndpoint = hubEndpoint; 61 mDestination = destination; 62 mInitiator = initiator; 63 mServiceDescriptor = serviceDescriptor; 64 } 65 66 /** 67 * Send a message to the peer endpoint in this session. 68 * 69 * @param message The message object constructed with {@link HubMessage#createMessage}. 70 * @return For messages that does not require a response, the transaction will immediately 71 * complete. For messages that requires a response, the transaction will complete after 72 * receiving the response for the message. 73 * @throws SecurityException if the application doesn't have the right permissions to send this 74 * message. 75 */ 76 @NonNull 77 @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) sendMessage(@onNull HubMessage message)78 public ContextHubTransaction<Void> sendMessage(@NonNull HubMessage message) { 79 if (mIsClosed.get()) { 80 throw new IllegalStateException("Session is already closed."); 81 } 82 83 boolean isResponseRequired = message.isResponseRequired(); 84 ContextHubTransaction<Void> ret = 85 new ContextHubTransaction<>( 86 isResponseRequired 87 ? ContextHubTransaction.TYPE_HUB_MESSAGE_REQUIRES_RESPONSE 88 : ContextHubTransaction.TYPE_HUB_MESSAGE_DEFAULT); 89 if (!isResponseRequired) { 90 // If the message doesn't require acknowledgement, respond with success immediately 91 mHubEndpoint.sendMessage(this, message, null); 92 ret.setResponse( 93 new ContextHubTransaction.Response<>( 94 ContextHubTransaction.RESULT_SUCCESS, null)); 95 } else { 96 IContextHubTransactionCallback callback = 97 ContextHubTransactionHelper.createTransactionCallback(ret); 98 // Sequence number will be assigned at the service 99 mHubEndpoint.sendMessage(this, message, callback); 100 } 101 return ret; 102 } 103 104 /** @hide */ getId()105 public int getId() { 106 return mId; 107 } 108 109 /** @hide */ setOpened()110 public void setOpened() { 111 mIsClosed.set(false); 112 mCloseGuard.open("close"); 113 } 114 115 /** @hide */ setClosed()116 public void setClosed() { 117 mIsClosed.set(true); 118 mCloseGuard.close(); 119 } 120 121 /** 122 * Closes the connection for this session between an endpoint and the Context Hub Service. 123 * 124 * <p>When this function is invoked, the messaging associated with this session is invalidated. 125 * All futures messages targeted for this client are dropped. 126 */ 127 @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) close()128 public void close() { 129 if (!mIsClosed.getAndSet(true)) { 130 mCloseGuard.close(); 131 mHubEndpoint.closeSession(this); 132 } 133 } 134 135 /** 136 * Get the service descriptor associated with this session. Null value indicates that there is 137 * no service associated to this session. 138 * 139 * <p>For hub initiated sessions, the object was previously used in as an argument for open 140 * request in {@link HubEndpointLifecycleCallback#onSessionOpenRequest}. 141 * 142 * <p>For app initiated sessions, the object was previously used in an open request in {@link 143 * android.hardware.location.ContextHubManager#openSession} 144 */ 145 @Nullable getServiceDescriptor()146 public String getServiceDescriptor() { 147 return mServiceDescriptor; 148 } 149 150 @Override toString()151 public String toString() { 152 StringBuilder stringBuilder = new StringBuilder(); 153 stringBuilder.append("Session ["); 154 stringBuilder.append(mId); 155 stringBuilder.append("]: ["); 156 stringBuilder.append(mInitiator); 157 stringBuilder.append("]->["); 158 stringBuilder.append(mDestination); 159 stringBuilder.append("]"); 160 return stringBuilder.toString(); 161 } 162 163 @Override equals(@ullable Object object)164 public boolean equals(@Nullable Object object) { 165 if (object == this) { 166 return true; 167 } 168 169 boolean isEqual = false; 170 if (object instanceof HubEndpointSession other) { 171 isEqual = (other.getId() == mId); 172 if (mServiceDescriptor != null) { 173 isEqual &= mServiceDescriptor.equals(other.getServiceDescriptor()); 174 } else { 175 isEqual &= (other.getServiceDescriptor() == null); 176 } 177 isEqual &= 178 mInitiator.equals(other.mInitiator) && mDestination.equals(other.mDestination); 179 } 180 181 return isEqual; 182 } 183 184 @Override hashCode()185 public int hashCode() { 186 return Objects.hash(mId, mServiceDescriptor, mInitiator, mDestination); 187 } 188 189 /** @hide */ finalize()190 protected void finalize() throws Throwable { 191 try { 192 // Note that guard could be null if the constructor threw. 193 if (mCloseGuard != null) { 194 mCloseGuard.warnIfOpen(); 195 } 196 close(); 197 } finally { 198 super.finalize(); 199 } 200 } 201 } 202