blob: 2a66cbc24b778ea0610fef14a7bf0d9e7de83682 [file] [log] [blame]
/*
* Copyright 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.hardware.location;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
/**
* A class describing a request sent to the Context Hub Service.
*
* This object is generated as a result of an asynchronous request sent to the Context Hub
* through the ContextHubManager APIs. The caller can either retrieve the result
* synchronously through a blocking call ({@link #waitForResponse(long, TimeUnit)}) or
* asynchronously through a user-defined callback
* ({@link #setCallbackOnComplete(ContextHubTransaction.Callback, Handler)}).
*
* @param <T> the type of the contents in the transaction response
*
* @hide
*/
public class ContextHubTransaction<T> {
private static final String TAG = "ContextHubTransaction";
/**
* Constants describing the type of a transaction through the Context Hub Service.
*/
@Retention(RetentionPolicy.SOURCE)
@IntDef(prefix = { "TYPE_" }, value = {
TYPE_LOAD_NANOAPP,
TYPE_UNLOAD_NANOAPP,
TYPE_ENABLE_NANOAPP,
TYPE_DISABLE_NANOAPP,
TYPE_QUERY_NANOAPPS
})
public @interface Type { }
public static final int TYPE_LOAD_NANOAPP = 0;
public static final int TYPE_UNLOAD_NANOAPP = 1;
public static final int TYPE_ENABLE_NANOAPP = 2;
public static final int TYPE_DISABLE_NANOAPP = 3;
public static final int TYPE_QUERY_NANOAPPS = 4;
/**
* Constants describing the result of a transaction or request through the Context Hub Service.
*/
@Retention(RetentionPolicy.SOURCE)
@IntDef(prefix = { "TRANSACTION_" }, value = {
TRANSACTION_SUCCESS,
TRANSACTION_FAILED_UNKNOWN,
TRANSACTION_FAILED_BAD_PARAMS,
TRANSACTION_FAILED_UNINITIALIZED,
TRANSACTION_FAILED_PENDING,
TRANSACTION_FAILED_AT_HUB,
TRANSACTION_FAILED_TIMEOUT,
TRANSACTION_FAILED_SERVICE_INTERNAL_FAILURE,
TRANSACTION_FAILED_HAL_UNAVAILABLE
})
public @interface Result {}
public static final int TRANSACTION_SUCCESS = 0;
/**
* Generic failure mode.
*/
public static final int TRANSACTION_FAILED_UNKNOWN = 1;
/**
* Failure mode when the request parameters were not valid.
*/
public static final int TRANSACTION_FAILED_BAD_PARAMS = 2;
/**
* Failure mode when the Context Hub is not initialized.
*/
public static final int TRANSACTION_FAILED_UNINITIALIZED = 3;
/**
* Failure mode when there are too many transactions pending.
*/
public static final int TRANSACTION_FAILED_PENDING = 4;
/**
* Failure mode when the request went through, but failed asynchronously at the hub.
*/
public static final int TRANSACTION_FAILED_AT_HUB = 5;
/**
* Failure mode when the transaction has timed out.
*/
public static final int TRANSACTION_FAILED_TIMEOUT = 6;
/**
* Failure mode when the transaction has failed internally at the service.
*/
public static final int TRANSACTION_FAILED_SERVICE_INTERNAL_FAILURE = 7;
/**
* Failure mode when the Context Hub HAL was not available.
*/
public static final int TRANSACTION_FAILED_HAL_UNAVAILABLE = 8;
/**
* A class describing the response for a ContextHubTransaction.
*
* @param <R> the type of the contents in the response
*/
public static class Response<R> {
/*
* The result of the transaction.
*/
@ContextHubTransaction.Result
private int mResult;
/*
* The contents of the response from the Context Hub.
*/
private R mContents;
Response(@ContextHubTransaction.Result int result, R contents) {
mResult = result;
mContents = contents;
}
@ContextHubTransaction.Result
public int getResult() {
return mResult;
}
public R getContents() {
return mContents;
}
}
/**
* An interface describing the callback to be invoked when a transaction completes.
*
* @param <C> the type of the contents in the transaction response
*/
@FunctionalInterface
public interface Callback<C> {
/**
* The callback to invoke when the transaction completes.
*
* @param transaction the transaction that this callback was attached to.
* @param response the response of the transaction.
*/
void onComplete(
ContextHubTransaction<C> transaction, ContextHubTransaction.Response<C> response);
}
/*
* The type of the transaction.
*/
@Type
private int mTransactionType;
/*
* The response of the transaction.
*/
private ContextHubTransaction.Response<T> mResponse;
/*
* The handler to invoke the aynsc response supplied by onComplete.
*/
private Handler mHandler = null;
/*
* The callback to invoke when the transaction completes.
*/
private ContextHubTransaction.Callback<T> mCallback = null;
/*
* Synchronization latch used to block on response.
*/
private final CountDownLatch mDoneSignal = new CountDownLatch(1);
/*
* true if the response has been set throught setResponse, false otherwise.
*/
private boolean mIsResponseSet = false;
ContextHubTransaction(@Type int type) {
mTransactionType = type;
}
/**
* Converts a transaction type to a human-readable string
*
* @param type the type of a transaction
* @param upperCase {@code true} if upper case the first letter, {@code false} otherwise
* @return a string describing the transaction
*/
public static String typeToString(@Type int type, boolean upperCase) {
switch (type) {
case ContextHubTransaction.TYPE_LOAD_NANOAPP:
return upperCase ? "Load" : "load";
case ContextHubTransaction.TYPE_UNLOAD_NANOAPP:
return upperCase ? "Unload" : "unload";
case ContextHubTransaction.TYPE_ENABLE_NANOAPP:
return upperCase ? "Enable" : "enable";
case ContextHubTransaction.TYPE_DISABLE_NANOAPP:
return upperCase ? "Disable" : "disable";
case ContextHubTransaction.TYPE_QUERY_NANOAPPS:
return upperCase ? "Query" : "query";
default:
return upperCase ? "Unknown" : "unknown";
}
}
/**
* @return the type of the transaction
*/
@Type
public int getType() {
return mTransactionType;
}
/**
* Waits to receive the asynchronous transaction result.
*
* This function blocks until the Context Hub Service has received a response
* for the transaction represented by this object by the Context Hub, or a
* specified timeout period has elapsed.
*
* If the specified timeout has passed, a TimeoutException will be thrown and the caller may
* retry the invocation of this method at a later time.
*
* @param timeout the timeout duration
* @param unit the unit of the timeout
*
* @return the transaction response
*
* @throws InterruptedException if the current thread is interrupted while waiting for response
* @throws TimeoutException if the timeout period has passed
*/
public ContextHubTransaction.Response<T> waitForResponse(
long timeout, TimeUnit unit) throws InterruptedException, TimeoutException {
boolean success = mDoneSignal.await(timeout, unit);
if (!success) {
throw new TimeoutException("Timed out while waiting for transaction");
}
return mResponse;
}
/**
* Sets a callback to be invoked when the transaction completes.
*
* This function provides an asynchronous approach to retrieve the result of the
* transaction. When the transaction response has been provided by the Context Hub,
* the given callback will be posted by the provided handler.
*
* If the transaction has already completed at the time of invocation, the callback
* will be immediately posted by the handler. If the transaction has been invalidated,
* the callback will never be invoked.
*
* A transaction can be invalidated if the process owning the transaction is no longer active
* and the reference to this object is lost.
*
* This method or {@link #setCallbackOnComplete(ContextHubTransaction.Callback)} can only be
* invoked once, or an IllegalStateException will be thrown.
*
* @param callback the callback to be invoked upon completion
* @param handler the handler to post the callback
*
* @throws IllegalStateException if this method is called multiple times
* @throws NullPointerException if the callback or handler is null
*/
public void setCallbackOnComplete(
@NonNull ContextHubTransaction.Callback<T> callback, @NonNull Handler handler) {
synchronized (this) {
if (callback == null) {
throw new NullPointerException("Callback cannot be null");
}
if (handler == null) {
throw new NullPointerException("Handler cannot be null");
}
if (mCallback != null) {
throw new IllegalStateException(
"Cannot set ContextHubTransaction callback multiple times");
}
mCallback = callback;
mHandler = handler;
if (mDoneSignal.getCount() == 0) {
boolean callbackPosted = mHandler.post(() -> {
mCallback.onComplete(this, mResponse);
});
if (!callbackPosted) {
Log.e(TAG, "Failed to post callback to Handler");
}
}
}
}
/**
* Sets a callback to be invoked when the transaction completes.
*
* Equivalent to {@link #setCallbackOnComplete(ContextHubTransaction.Callback, Handler)}
* with the handler being that of the main thread's Looper.
*
* This method or {@link #setCallbackOnComplete(ContextHubTransaction.Callback, Handler)}
* can only be invoked once, or an IllegalStateException will be thrown.
*
* @param callback the callback to be invoked upon completion
*
* @throws IllegalStateException if this method is called multiple times
* @throws NullPointerException if the callback is null
*/
public void setCallbackOnComplete(@NonNull ContextHubTransaction.Callback<T> callback) {
setCallbackOnComplete(callback, new Handler(Looper.getMainLooper()));
}
/**
* Sets the response of the transaction.
*
* This method should only be invoked by ContextHubManager as a result of a callback from
* the Context Hub Service indicating the response from a transaction. This method should not be
* invoked more than once.
*
* @param response the response to set
*
* @throws IllegalStateException if this method is invoked multiple times
* @throws NullPointerException if the response is null
*/
void setResponse(ContextHubTransaction.Response<T> response) {
synchronized (this) {
if (response == null) {
throw new NullPointerException("Response cannot be null");
}
if (mIsResponseSet) {
throw new IllegalStateException(
"Cannot set response of ContextHubTransaction multiple times");
}
mResponse = response;
mIsResponseSet = true;
mDoneSignal.countDown();
if (mCallback != null) {
boolean callbackPosted = mHandler.post(() -> {
mCallback.onComplete(this, mResponse);
});
if (!callbackPosted) {
Log.e(TAG, "Failed to post callback to Handler");
}
}
}
}
}