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