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