blob: b31c7bcdb31e58aa61b8af0f8cef897ec9e1f784 [file] [log] [blame]
/*
* Copyright (C) 2016 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.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.ServiceManager.ServiceNotFoundException;
import android.util.Log;
import java.util.List;
/**
* A class that exposes the Context hubs on a device to applications.
*
* Please note that this class is not expected to be used by unbundled applications. Also, calling
* applications are expected to have LOCATION_HARDWARE permissions to use this class.
*
* @hide
*/
@SystemApi
@SystemService(Context.CONTEXTHUB_SERVICE)
public final class ContextHubManager {
private static final String TAG = "ContextHubManager";
private final Looper mMainLooper;
private final IContextHubService mService;
private Callback mCallback;
private Handler mCallbackHandler;
/**
* @deprecated Use {@code mCallback} instead.
*/
@Deprecated
private ICallback mLocalCallback;
/**
* An interface to receive asynchronous communication from the context hub.
*/
public abstract static class Callback {
protected Callback() {}
/**
* Callback function called on message receipt from context hub.
*
* @param hubHandle Handle (system-wide unique identifier) of the hub of the message.
* @param nanoAppHandle Handle (unique identifier) for app instance that sent the message.
* @param message The context hub message.
*
* @see ContextHubMessage
*/
public abstract void onMessageReceipt(
int hubHandle,
int nanoAppHandle,
ContextHubMessage message);
}
/**
* @deprecated Use {@link Callback} instead.
* @hide
*/
@Deprecated
public interface ICallback {
/**
* Callback function called on message receipt from context hub.
*
* @param hubHandle Handle (system-wide unique identifier) of the hub of the message.
* @param nanoAppHandle Handle (unique identifier) for app instance that sent the message.
* @param message The context hub message.
*
* @see ContextHubMessage
*/
void onMessageReceipt(int hubHandle, int nanoAppHandle, ContextHubMessage message);
}
/**
* Get a handle to all the context hubs in the system
* @return array of context hub handles
*/
@RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
public int[] getContextHubHandles() {
try {
return mService.getContextHubHandles();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Get more information about a specific hub.
*
* @param hubHandle Handle (system-wide unique identifier) of a context hub.
* @return ContextHubInfo Information about the requested context hub.
*
* @see ContextHubInfo
*/
@RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
public ContextHubInfo getContextHubInfo(int hubHandle) {
try {
return mService.getContextHubInfo(hubHandle);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Load a nano app on a specified context hub.
*
* Note that loading is asynchronous. When we return from this method,
* the nano app (probably) hasn't loaded yet. Assuming a return of 0
* from this method, then the final success/failure for the load, along
* with the "handle" for the nanoapp, is all delivered in a byte
* string via a call to Callback.onMessageReceipt.
*
* TODO(b/30784270): Provide a better success/failure and "handle" delivery.
*
* @param hubHandle handle of context hub to load the app on.
* @param app the nanoApp to load on the hub
*
* @return 0 if the command for loading was sent to the context hub;
* -1 otherwise
*
* @see NanoApp
*/
@RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
public int loadNanoApp(int hubHandle, NanoApp app) {
try {
return mService.loadNanoApp(hubHandle, app);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Unload a specified nanoApp
*
* Note that unloading is asynchronous. When we return from this method,
* the nano app (probably) hasn't unloaded yet. Assuming a return of 0
* from this method, then the final success/failure for the unload is
* delivered in a byte string via a call to Callback.onMessageReceipt.
*
* TODO(b/30784270): Provide a better success/failure delivery.
*
* @param nanoAppHandle handle of the nanoApp to unload
*
* @return 0 if the command for unloading was sent to the context hub;
* -1 otherwise
*/
@RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
public int unloadNanoApp(int nanoAppHandle) {
try {
return mService.unloadNanoApp(nanoAppHandle);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* get information about the nano app instance
*
* NOTE: The returned NanoAppInstanceInfo does _not_ contain correct
* information for several fields, specifically:
* - getName()
* - getPublisher()
* - getNeededExecMemBytes()
* - getNeededReadMemBytes()
* - getNeededWriteMemBytes()
*
* For example, say you call loadNanoApp() with a NanoApp that has
* getName() returning "My Name". Later, if you call getNanoAppInstanceInfo
* for that nanoapp, the returned NanoAppInstanceInfo's getName()
* method will claim "Preloaded app, unknown", even though you would
* have expected "My Name". For now, as the user, you'll need to
* separately track the above fields if they are of interest to you.
*
* TODO(b/30943489): Have the returned NanoAppInstanceInfo contain the
* correct information.
*
* @param nanoAppHandle handle of the nanoAppInstance
* @return NanoAppInstanceInfo Information about the nano app instance.
*
* @see NanoAppInstanceInfo
*/
@RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
public NanoAppInstanceInfo getNanoAppInstanceInfo(int nanoAppHandle) {
try {
return mService.getNanoAppInstanceInfo(nanoAppHandle);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Find a specified nano app on the system
*
* @param hubHandle handle of hub to search for nano app
* @param filter filter specifying the search criteria for app
*
* @see NanoAppFilter
*
* @return int[] Array of handles to any found nano apps
*/
@RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
public int[] findNanoAppOnHub(int hubHandle, NanoAppFilter filter) {
try {
return mService.findNanoAppOnHub(hubHandle, filter);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Send a message to a specific nano app instance on a context hub.
*
* Note that the return value of this method only speaks of success
* up to the point of sending this to the Context Hub. It is not
* an assurance that the Context Hub successfully sent this message
* on to the nanoapp. If assurance is desired, a protocol should be
* established between your code and the nanoapp, with the nanoapp
* sending a confirmation message (which will be reported via
* Callback.onMessageReceipt).
*
* @param hubHandle handle of the hub to send the message to
* @param nanoAppHandle handle of the nano app to send to
* @param message Message to be sent
*
* @see ContextHubMessage
*
* @return int 0 on success, -1 otherwise
*/
@RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
public int sendMessage(int hubHandle, int nanoAppHandle, ContextHubMessage message) {
try {
return mService.sendMessage(hubHandle, nanoAppHandle, message);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Returns the list of context hubs in the system.
*
* @return the list of context hub informations
*
* @see ContextHubInfo
*
* @hide
*/
@RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
public List<ContextHubInfo> getContextHubs() {
throw new UnsupportedOperationException("TODO: Implement this");
}
/*
* Helper function to generate a stub for a non-query transaction callback.
*
* @param transaction the transaction to unblock when complete
*
* @return the callback
*
* @hide
*/
private IContextHubTransactionCallback createTransactionCallback(
ContextHubTransaction<Void> transaction) {
return new IContextHubTransactionCallback.Stub() {
@Override
public void onQueryResponse(int result, List<NanoAppState> nanoappList) {
Log.e(TAG, "Received a query callback on a non-query request");
transaction.setResponse(new ContextHubTransaction.Response<Void>(
ContextHubTransaction.TRANSACTION_FAILED_SERVICE_INTERNAL_FAILURE, null));
}
@Override
public void onTransactionComplete(int result) {
transaction.setResponse(new ContextHubTransaction.Response<Void>(result, null));
}
};
}
/*
* Helper function to generate a stub for a query transaction callback.
*
* @param transaction the transaction to unblock when complete
*
* @return the callback
*
* @hide
*/
private IContextHubTransactionCallback createQueryCallback(
ContextHubTransaction<List<NanoAppState>> transaction) {
return new IContextHubTransactionCallback.Stub() {
@Override
public void onQueryResponse(int result, List<NanoAppState> nanoappList) {
transaction.setResponse(new ContextHubTransaction.Response<List<NanoAppState>>(
result, nanoappList));
}
@Override
public void onTransactionComplete(int result) {
Log.e(TAG, "Received a non-query callback on a query request");
transaction.setResponse(new ContextHubTransaction.Response<List<NanoAppState>>(
ContextHubTransaction.TRANSACTION_FAILED_SERVICE_INTERNAL_FAILURE, null));
}
};
}
/**
* Loads a nanoapp at the specified Context Hub.
*
* After the nanoapp binary is successfully loaded at the specified hub, the nanoapp will be in
* the enabled state.
*
* @param hubInfo the hub to load the nanoapp on
* @param appBinary The app binary to load
*
* @return the ContextHubTransaction of the request
*
* @see NanoAppBinary
*
* @hide
*/
@RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
public ContextHubTransaction<Void> loadNanoApp(
ContextHubInfo hubInfo, NanoAppBinary appBinary) {
throw new UnsupportedOperationException("TODO: Implement this");
}
/**
* Unloads a nanoapp at the specified Context Hub.
*
* @param hubInfo the hub to unload the nanoapp from
* @param nanoAppId the app to unload
*
* @return the ContextHubTransaction of the request
*
* @hide
*/
@RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
public ContextHubTransaction<Void> unloadNanoApp(ContextHubInfo hubInfo, long nanoAppId) {
throw new UnsupportedOperationException("TODO: Implement this");
}
/**
* Enables a nanoapp at the specified Context Hub.
*
* @param hubInfo the hub to enable the nanoapp on
* @param nanoAppId the app to enable
*
* @return the ContextHubTransaction of the request
*
* @hide
*/
@RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
public ContextHubTransaction<Void> enableNanoApp(ContextHubInfo hubInfo, long nanoAppId) {
throw new UnsupportedOperationException("TODO: Implement this");
}
/**
* Disables a nanoapp at the specified Context Hub.
*
* @param hubInfo the hub to disable the nanoapp on
* @param nanoAppId the app to disable
*
* @return the ContextHubTransaction of the request
*
* @hide
*/
@RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
public ContextHubTransaction<Void> disableNanoApp(ContextHubInfo hubInfo, long nanoAppId) {
throw new UnsupportedOperationException("TODO: Implement this");
}
/**
* Requests a query for nanoapps loaded at the specified Context Hub.
*
* @param hubInfo the hub to query a list of nanoapps from
*
* @return the ContextHubTransaction of the request
*
* @hide
*/
@RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
public ContextHubTransaction<List<NanoAppState>> queryNanoApps(ContextHubInfo hubInfo) {
throw new UnsupportedOperationException("TODO: Implement this");
}
/**
* Set a callback to receive messages from the context hub
*
* @param callback Callback object
*
* @see Callback
*
* @return int 0 on success, -1 otherwise
*/
@SuppressLint("Doclava125")
public int registerCallback(Callback callback) {
return registerCallback(callback, null);
}
/**
* @deprecated Use {@link #registerCallback(Callback)} instead.
* @hide
*/
@Deprecated
public int registerCallback(ICallback callback) {
if (mLocalCallback != null) {
Log.w(TAG, "Max number of local callbacks reached!");
return -1;
}
mLocalCallback = callback;
return 0;
}
/**
* Set a callback to receive messages from the context hub
*
* @param callback Callback object
* @param handler Handler object
*
* @see Callback
*
* @return int 0 on success, -1 otherwise
*/
@SuppressLint("Doclava125")
public int registerCallback(Callback callback, Handler handler) {
synchronized(this) {
if (mCallback != null) {
Log.w(TAG, "Max number of callbacks reached!");
return -1;
}
mCallback = callback;
mCallbackHandler = handler;
}
return 0;
}
/**
* Creates an interface to the ContextHubClient to send down to the service.
*
* @param callback the callback to invoke at the client process
* @param handler the handler to post callbacks for this client
*
* @return the callback interface
*/
private IContextHubClientCallback createClientCallback(
ContextHubClientCallback callback, Handler handler) {
return new IContextHubClientCallback.Stub() {
@Override
public void onMessageFromNanoApp(NanoAppMessage message) {
handler.post(() -> callback.onMessageFromNanoApp(message));
}
@Override
public void onHubReset() {
handler.post(() -> callback.onHubReset());
}
@Override
public void onNanoAppAborted(long nanoAppId, int abortCode) {
handler.post(() -> callback.onNanoAppAborted(nanoAppId, abortCode));
}
@Override
public void onNanoAppLoaded(long nanoAppId) {
handler.post(() -> callback.onNanoAppLoaded(nanoAppId));
}
@Override
public void onNanoAppUnloaded(long nanoAppId) {
handler.post(() -> callback.onNanoAppUnloaded(nanoAppId));
}
@Override
public void onNanoAppEnabled(long nanoAppId) {
handler.post(() -> callback.onNanoAppEnabled(nanoAppId));
}
@Override
public void onNanoAppDisabled(long nanoAppId) {
handler.post(() -> callback.onNanoAppDisabled(nanoAppId));
}
};
}
/**
* Creates and registers a client and its callback with the Context Hub Service.
*
* A client is registered with the Context Hub Service for a specified Context Hub. When the
* registration succeeds, the client can send messages to nanoapps through the returned
* {@link ContextHubClient} object, and receive notifications through the provided callback.
*
* @param callback the notification callback to register
* @param hubInfo the hub to attach this client to
* @param handler the handler to invoke the callback, if null uses the main thread's Looper
* @return the registered client object
*
* @throws IllegalArgumentException if hubInfo does not represent a valid hub
* @throws IllegalStateException if there were too many registered clients at the service
* @throws NullPointerException if callback or hubInfo is null
*
* @hide
* @see ContextHubClientCallback
*/
public ContextHubClient createClient(
ContextHubClientCallback callback, ContextHubInfo hubInfo, @Nullable Handler handler) {
if (callback == null) {
throw new NullPointerException("Callback cannot be null");
}
if (hubInfo == null) {
throw new NullPointerException("Hub info cannot be null");
}
Handler realHandler = (handler == null) ? new Handler(mMainLooper) : handler;
IContextHubClientCallback clientInterface = createClientCallback(callback, realHandler);
IContextHubClient client;
try {
client = mService.createClient(clientInterface, hubInfo.getId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
return new ContextHubClient(client, clientInterface, hubInfo);
}
/**
* Unregister a callback for receive messages from the context hub.
*
* @see Callback
*
* @param callback method to deregister
*
* @return int 0 on success, -1 otherwise
*/
@SuppressLint("Doclava125")
public int unregisterCallback(Callback callback) {
synchronized(this) {
if (callback != mCallback) {
Log.w(TAG, "Cannot recognize callback!");
return -1;
}
mCallback = null;
mCallbackHandler = null;
}
return 0;
}
/**
* @deprecated Use {@link #unregisterCallback(Callback)} instead.
* @hide
*/
@Deprecated
public synchronized int unregisterCallback(ICallback callback) {
if (callback != mLocalCallback) {
Log.w(TAG, "Cannot recognize local callback!");
return -1;
}
mLocalCallback = null;
return 0;
}
private final IContextHubCallback.Stub mClientCallback = new IContextHubCallback.Stub() {
@Override
public void onMessageReceipt(final int hubId, final int nanoAppId,
final ContextHubMessage message) {
if (mCallback != null) {
synchronized(this) {
final Callback callback = mCallback;
Handler handler = mCallbackHandler == null ?
new Handler(mMainLooper) : mCallbackHandler;
handler.post(new Runnable() {
@Override
public void run() {
callback.onMessageReceipt(hubId, nanoAppId, message);
}
});
}
} else if (mLocalCallback != null) {
// we always ensure that mCallback takes precedence, because mLocalCallback is only
// for internal compatibility
synchronized (this) {
mLocalCallback.onMessageReceipt(hubId, nanoAppId, message);
}
} else {
Log.d(TAG, "Context hub manager client callback is NULL");
}
}
};
/** @throws ServiceNotFoundException
* @hide */
public ContextHubManager(Context context, Looper mainLooper) throws ServiceNotFoundException {
mMainLooper = mainLooper;
mService = IContextHubService.Stub.asInterface(
ServiceManager.getServiceOrThrow(Context.CONTEXTHUB_SERVICE));
try {
mService.registerCallback(mClientCallback);
} catch (RemoteException e) {
Log.w(TAG, "Could not register callback:" + e);
}
}
}