1 /* 2 * Copyright 2017 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 package android.hardware.location; 17 18 import android.annotation.CallbackExecutor; 19 import android.annotation.FlaggedApi; 20 import android.annotation.IntDef; 21 import android.annotation.NonNull; 22 import android.annotation.SystemApi; 23 import android.chre.flags.Flags; 24 import android.os.Handler; 25 import android.os.HandlerExecutor; 26 27 import java.lang.annotation.Retention; 28 import java.lang.annotation.RetentionPolicy; 29 import java.util.Objects; 30 import java.util.concurrent.CountDownLatch; 31 import java.util.concurrent.Executor; 32 import java.util.concurrent.TimeUnit; 33 import java.util.concurrent.TimeoutException; 34 35 /** 36 * A class describing a request sent to the Context Hub Service. 37 * 38 * This object is generated as a result of an asynchronous request sent to the Context Hub 39 * through the ContextHubManager APIs. The caller can either retrieve the result 40 * synchronously through a blocking call ({@link #waitForResponse(long, TimeUnit)}) or 41 * asynchronously through a user-defined listener 42 * ({@link #setOnCompleteListener(OnCompleteListener, Executor)} )}). 43 * 44 * @param <T> the type of the contents in the transaction response 45 * 46 * @hide 47 */ 48 @SystemApi 49 public class ContextHubTransaction<T> { 50 private static final String TAG = "ContextHubTransaction"; 51 52 /** 53 * Constants describing the type of a transaction through the Context Hub Service. 54 * 55 * @hide 56 */ 57 @Retention(RetentionPolicy.SOURCE) 58 @IntDef( 59 prefix = {"TYPE_"}, 60 value = { 61 TYPE_LOAD_NANOAPP, 62 TYPE_UNLOAD_NANOAPP, 63 TYPE_ENABLE_NANOAPP, 64 TYPE_DISABLE_NANOAPP, 65 TYPE_QUERY_NANOAPPS, 66 TYPE_RELIABLE_MESSAGE, 67 TYPE_HUB_MESSAGE_DEFAULT, 68 TYPE_HUB_MESSAGE_REQUIRES_RESPONSE, 69 }) 70 public @interface Type {} 71 72 public static final int TYPE_LOAD_NANOAPP = 0; 73 public static final int TYPE_UNLOAD_NANOAPP = 1; 74 public static final int TYPE_ENABLE_NANOAPP = 2; 75 public static final int TYPE_DISABLE_NANOAPP = 3; 76 public static final int TYPE_QUERY_NANOAPPS = 4; 77 public static final int TYPE_RELIABLE_MESSAGE = 5; 78 79 @FlaggedApi(Flags.FLAG_OFFLOAD_API) 80 public static final int TYPE_HUB_MESSAGE_DEFAULT = 6; 81 82 @FlaggedApi(Flags.FLAG_OFFLOAD_API) 83 public static final int TYPE_HUB_MESSAGE_REQUIRES_RESPONSE = 7; 84 85 /** 86 * Constants describing the result of a transaction or request through the Context Hub Service. 87 * 88 * @hide 89 */ 90 @Retention(RetentionPolicy.SOURCE) 91 @IntDef( 92 prefix = {"RESULT_"}, 93 value = { 94 RESULT_SUCCESS, 95 RESULT_FAILED_UNKNOWN, 96 RESULT_FAILED_BAD_PARAMS, 97 RESULT_FAILED_UNINITIALIZED, 98 RESULT_FAILED_BUSY, 99 RESULT_FAILED_AT_HUB, 100 RESULT_FAILED_TIMEOUT, 101 RESULT_FAILED_SERVICE_INTERNAL_FAILURE, 102 RESULT_FAILED_HAL_UNAVAILABLE, 103 RESULT_FAILED_NOT_SUPPORTED, 104 }) 105 public @interface Result {} 106 107 public static final int RESULT_SUCCESS = 0; 108 /** 109 * Generic failure mode. 110 */ 111 public static final int RESULT_FAILED_UNKNOWN = 1; 112 /** 113 * Failure mode when the request parameters were not valid. 114 */ 115 public static final int RESULT_FAILED_BAD_PARAMS = 2; 116 /** 117 * Failure mode when the Context Hub is not initialized. 118 */ 119 public static final int RESULT_FAILED_UNINITIALIZED = 3; 120 /** 121 * Failure mode when there are too many transactions pending. 122 */ 123 public static final int RESULT_FAILED_BUSY = 4; 124 /** 125 * Failure mode when the request went through, but failed asynchronously at the hub. 126 */ 127 public static final int RESULT_FAILED_AT_HUB = 5; 128 /** 129 * Failure mode when the transaction has timed out. 130 */ 131 public static final int RESULT_FAILED_TIMEOUT = 6; 132 /** 133 * Failure mode when the transaction has failed internally at the service. 134 */ 135 public static final int RESULT_FAILED_SERVICE_INTERNAL_FAILURE = 7; 136 /** 137 * Failure mode when the Context Hub HAL was not available. 138 */ 139 public static final int RESULT_FAILED_HAL_UNAVAILABLE = 8; 140 141 /** Failure mode when the operation is not supported. */ 142 public static final int RESULT_FAILED_NOT_SUPPORTED = 9; 143 144 /** 145 * A class describing the response for a ContextHubTransaction. 146 * 147 * @param <R> the type of the contents in the response 148 */ 149 public static class Response<R> { 150 /* 151 * The result of the transaction. 152 */ 153 @ContextHubTransaction.Result 154 private int mResult; 155 156 /* 157 * The contents of the response from the Context Hub. 158 */ 159 private R mContents; 160 161 /** @hide */ Response(@ontextHubTransaction.Result int result, R contents)162 public Response(@ContextHubTransaction.Result int result, R contents) { 163 mResult = result; 164 mContents = contents; 165 } 166 167 @ContextHubTransaction.Result getResult()168 public int getResult() { 169 return mResult; 170 } 171 getContents()172 public R getContents() { 173 return mContents; 174 } 175 } 176 177 /** 178 * An interface describing the listener for a transaction completion. 179 * 180 * @param <L> the type of the contents in the transaction response 181 */ 182 @FunctionalInterface 183 public interface OnCompleteListener<L> { 184 /** 185 * The listener function to invoke when the transaction completes. 186 * 187 * @param transaction the transaction that this callback was attached to. 188 * @param response the response of the transaction. 189 */ onComplete( ContextHubTransaction<L> transaction, ContextHubTransaction.Response<L> response)190 void onComplete( 191 ContextHubTransaction<L> transaction, ContextHubTransaction.Response<L> response); 192 } 193 194 /* 195 * The type of the transaction. 196 */ 197 @Type 198 private int mTransactionType; 199 200 /* 201 * The response of the transaction. 202 */ 203 private ContextHubTransaction.Response<T> mResponse; 204 205 /* 206 * The executor to invoke the onComplete async callback. 207 */ 208 private Executor mExecutor = null; 209 210 /* 211 * The listener to be invoked when the transaction completes. 212 */ 213 private ContextHubTransaction.OnCompleteListener<T> mListener = null; 214 215 /* 216 * Synchronization latch used to block on response. 217 */ 218 private final CountDownLatch mDoneSignal = new CountDownLatch(1); 219 220 /* 221 * true if the response has been set throught setResponse, false otherwise. 222 */ 223 private boolean mIsResponseSet = false; 224 225 /** @hide */ ContextHubTransaction(@ype int type)226 public ContextHubTransaction(@Type int type) { 227 mTransactionType = type; 228 } 229 230 /** 231 * Converts a transaction type to a human-readable string 232 * 233 * @param type the type of a transaction 234 * @param upperCase {@code true} if upper case the first letter, {@code false} otherwise 235 * @return a string describing the transaction 236 */ typeToString(@ype int type, boolean upperCase)237 public static String typeToString(@Type int type, boolean upperCase) { 238 switch (type) { 239 case ContextHubTransaction.TYPE_LOAD_NANOAPP: 240 return upperCase ? "Load" : "load"; 241 case ContextHubTransaction.TYPE_UNLOAD_NANOAPP: 242 return upperCase ? "Unload" : "unload"; 243 case ContextHubTransaction.TYPE_ENABLE_NANOAPP: 244 return upperCase ? "Enable" : "enable"; 245 case ContextHubTransaction.TYPE_DISABLE_NANOAPP: 246 return upperCase ? "Disable" : "disable"; 247 case ContextHubTransaction.TYPE_QUERY_NANOAPPS: 248 return upperCase ? "Query" : "query"; 249 case ContextHubTransaction.TYPE_RELIABLE_MESSAGE: 250 return upperCase ? "Reliable Message" : "reliable message"; 251 default: 252 return upperCase ? "Unknown" : "unknown"; 253 } 254 } 255 256 /** 257 * @return the type of the transaction 258 */ 259 @Type getType()260 public int getType() { 261 return mTransactionType; 262 } 263 264 /** 265 * Waits to receive the asynchronous transaction result. 266 * 267 * This function blocks until the Context Hub Service has received a response 268 * for the transaction represented by this object by the Context Hub, or a 269 * specified timeout period has elapsed. 270 * 271 * If the specified timeout has passed, a TimeoutException will be thrown and the caller may 272 * retry the invocation of this method at a later time. 273 * 274 * @param timeout the timeout duration 275 * @param unit the unit of the timeout 276 * 277 * @return the transaction response 278 * 279 * @throws InterruptedException if the current thread is interrupted while waiting for response 280 * @throws TimeoutException if the timeout period has passed 281 */ waitForResponse( long timeout, TimeUnit unit)282 public ContextHubTransaction.Response<T> waitForResponse( 283 long timeout, TimeUnit unit) throws InterruptedException, TimeoutException { 284 boolean success = mDoneSignal.await(timeout, unit); 285 286 if (!success) { 287 throw new TimeoutException("Timed out while waiting for transaction"); 288 } 289 290 return mResponse; 291 } 292 293 /** 294 * Sets the listener to be invoked invoked when the transaction completes. 295 * 296 * This function provides an asynchronous approach to retrieve the result of the 297 * transaction. When the transaction response has been provided by the Context Hub, 298 * the given listener will be invoked. 299 * 300 * If the transaction has already completed at the time of invocation, the listener 301 * will be immediately invoked. If the transaction has been invalidated, 302 * the listener will never be invoked. 303 * 304 * A transaction can be invalidated if the process owning the transaction is no longer active 305 * and the reference to this object is lost. 306 * 307 * This method or {@link #setOnCompleteListener(ContextHubTransaction.OnCompleteListener)} can 308 * only be invoked once, or an IllegalStateException will be thrown. 309 * 310 * @param listener the listener to be invoked upon completion 311 * @param executor the executor to invoke the callback 312 * 313 * @throws IllegalStateException if this method is called multiple times 314 * @throws NullPointerException if the callback or handler is null 315 */ setOnCompleteListener( @onNull ContextHubTransaction.OnCompleteListener<T> listener, @NonNull @CallbackExecutor Executor executor)316 public void setOnCompleteListener( 317 @NonNull ContextHubTransaction.OnCompleteListener<T> listener, 318 @NonNull @CallbackExecutor Executor executor) { 319 synchronized (this) { 320 Objects.requireNonNull(listener, "OnCompleteListener cannot be null"); 321 Objects.requireNonNull(executor, "Executor cannot be null"); 322 if (mListener != null) { 323 throw new IllegalStateException( 324 "Cannot set ContextHubTransaction listener multiple times"); 325 } 326 327 mListener = listener; 328 mExecutor = executor; 329 330 if (mDoneSignal.getCount() == 0) { 331 mExecutor.execute(() -> mListener.onComplete(this, mResponse)); 332 } 333 } 334 } 335 336 /** 337 * Sets the listener to be invoked invoked when the transaction completes. 338 * 339 * Equivalent to {@link #setOnCompleteListener(ContextHubTransaction.OnCompleteListener, 340 * Executor)} with the executor using the main thread's Looper. 341 * 342 * This method or {@link #setOnCompleteListener(ContextHubTransaction.OnCompleteListener, 343 * Executor)} can only be invoked once, or an IllegalStateException will be thrown. 344 * 345 * @param listener the listener to be invoked upon completion 346 * 347 * @throws IllegalStateException if this method is called multiple times 348 * @throws NullPointerException if the callback is null 349 */ setOnCompleteListener( @onNull ContextHubTransaction.OnCompleteListener<T> listener)350 public void setOnCompleteListener( 351 @NonNull ContextHubTransaction.OnCompleteListener<T> listener) { 352 setOnCompleteListener(listener, new HandlerExecutor(Handler.getMain())); 353 } 354 355 /** 356 * Sets the response of the transaction. 357 * 358 * <p>This method should only be invoked by ContextHubManager as a result of a callback from the 359 * Context Hub Service indicating the response from a transaction. This method should not be 360 * invoked more than once. 361 * 362 * @param response the response to set 363 * @throws IllegalStateException if this method is invoked multiple times 364 * @throws NullPointerException if the response is null 365 * @hide 366 */ setResponse(ContextHubTransaction.Response<T> response)367 public void setResponse(ContextHubTransaction.Response<T> response) { 368 synchronized (this) { 369 Objects.requireNonNull(response, "Response cannot be null"); 370 if (mIsResponseSet) { 371 throw new IllegalStateException( 372 "Cannot set response of ContextHubTransaction multiple times"); 373 } 374 375 mResponse = response; 376 mIsResponseSet = true; 377 378 mDoneSignal.countDown(); 379 if (mListener != null) { 380 mExecutor.execute(() -> mListener.onComplete(this, mResponse)); 381 } 382 } 383 } 384 } 385