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