1114 lines
42 KiB
Java
1114 lines
42 KiB
Java
/*
|
|
* 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 static java.util.Objects.requireNonNull;
|
|
|
|
import android.annotation.CallbackExecutor;
|
|
import android.annotation.IntDef;
|
|
import android.annotation.NonNull;
|
|
import android.annotation.Nullable;
|
|
import android.annotation.RequiresFeature;
|
|
import android.annotation.RequiresPermission;
|
|
import android.annotation.SuppressLint;
|
|
import android.annotation.SystemApi;
|
|
import android.annotation.SystemService;
|
|
import android.annotation.TestApi;
|
|
import android.app.ActivityThread;
|
|
import android.app.PendingIntent;
|
|
import android.chre.flags.Flags;
|
|
import android.content.Context;
|
|
import android.content.Intent;
|
|
import android.content.pm.PackageManager;
|
|
import android.hardware.contexthub.ErrorCode;
|
|
import android.os.Handler;
|
|
import android.os.HandlerExecutor;
|
|
import android.os.Looper;
|
|
import android.os.RemoteException;
|
|
import android.util.Log;
|
|
|
|
import java.lang.annotation.Retention;
|
|
import java.lang.annotation.RetentionPolicy;
|
|
import java.util.List;
|
|
import java.util.Objects;
|
|
import java.util.concurrent.Executor;
|
|
|
|
/**
|
|
* 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 the ACCESS_CONTEXT_HUB permission to use this class.
|
|
*
|
|
* @hide
|
|
*/
|
|
@SystemApi
|
|
@SystemService(Context.CONTEXTHUB_SERVICE)
|
|
@RequiresFeature(PackageManager.FEATURE_CONTEXT_HUB)
|
|
public final class ContextHubManager {
|
|
private static final String TAG = "ContextHubManager";
|
|
|
|
/**
|
|
* An extra containing one of the {@code AUTHORIZATION_*} constants such as
|
|
* {@link #AUTHORIZATION_GRANTED} describing the client's authorization state.
|
|
*/
|
|
public static final String EXTRA_CLIENT_AUTHORIZATION_STATE =
|
|
"android.hardware.location.extra.CLIENT_AUTHORIZATION_STATE";
|
|
|
|
/**
|
|
* An extra of type {@link ContextHubInfo} describing the source of the event.
|
|
*/
|
|
public static final String EXTRA_CONTEXT_HUB_INFO =
|
|
"android.hardware.location.extra.CONTEXT_HUB_INFO";
|
|
|
|
/**
|
|
* An extra of type {@link ContextHubManager.Event} describing the event type.
|
|
*/
|
|
public static final String EXTRA_EVENT_TYPE = "android.hardware.location.extra.EVENT_TYPE";
|
|
|
|
/**
|
|
* An extra of type long describing the ID of the nanoapp an event is for.
|
|
*/
|
|
public static final String EXTRA_NANOAPP_ID = "android.hardware.location.extra.NANOAPP_ID";
|
|
|
|
/**
|
|
* An extra of type int describing the nanoapp-specific abort code.
|
|
*/
|
|
public static final String EXTRA_NANOAPP_ABORT_CODE =
|
|
"android.hardware.location.extra.NANOAPP_ABORT_CODE";
|
|
|
|
/**
|
|
* An extra of type {@link NanoAppMessage} describing contents of a message from a nanoapp.
|
|
*/
|
|
public static final String EXTRA_MESSAGE = "android.hardware.location.extra.MESSAGE";
|
|
|
|
/**
|
|
* Constants describing if a {@link ContextHubClient} and a {@link NanoApp} are authorized to
|
|
* communicate.
|
|
*
|
|
* @hide
|
|
*/
|
|
@Retention(RetentionPolicy.SOURCE)
|
|
@IntDef(prefix = { "AUTHORIZATION_" }, value = {
|
|
AUTHORIZATION_DENIED,
|
|
AUTHORIZATION_DENIED_GRACE_PERIOD,
|
|
AUTHORIZATION_GRANTED,
|
|
})
|
|
public @interface AuthorizationState { }
|
|
|
|
/**
|
|
* Indicates that the {@link ContextHubClient} can no longer communicate with a nanoapp. If the
|
|
* {@link ContextHubClient} attempts to send messages to the nanoapp, it will continue to
|
|
* receive this authorization state if the connection is still closed.
|
|
*/
|
|
public static final int AUTHORIZATION_DENIED = 0;
|
|
|
|
/**
|
|
* Indicates the {@link ContextHubClient} will soon lose its authorization to communicate with a
|
|
* nanoapp. After receiving this state event, the {@link ContextHubClient} has one minute to
|
|
* perform any cleanup with the nanoapp such that the nanoapp is no longer performing work on
|
|
* behalf of the {@link ContextHubClient}.
|
|
*/
|
|
public static final int AUTHORIZATION_DENIED_GRACE_PERIOD = 1;
|
|
|
|
/**
|
|
* The {@link ContextHubClient} is authorized to communicate with the nanoapp.
|
|
*/
|
|
public static final int AUTHORIZATION_GRANTED = 2;
|
|
|
|
/**
|
|
* Constants describing the type of events from a Context Hub, as defined in
|
|
* {@link ContextHubClientCallback}.
|
|
* {@hide}
|
|
*/
|
|
@Retention(RetentionPolicy.SOURCE)
|
|
@IntDef(prefix = { "EVENT_" }, value = {
|
|
EVENT_NANOAPP_LOADED,
|
|
EVENT_NANOAPP_UNLOADED,
|
|
EVENT_NANOAPP_ENABLED,
|
|
EVENT_NANOAPP_DISABLED,
|
|
EVENT_NANOAPP_ABORTED,
|
|
EVENT_NANOAPP_MESSAGE,
|
|
EVENT_HUB_RESET,
|
|
EVENT_CLIENT_AUTHORIZATION,
|
|
})
|
|
public @interface Event { }
|
|
|
|
/**
|
|
* An event describing that a nanoapp has been loaded. Contains the EXTRA_NANOAPP_ID extra.
|
|
*/
|
|
public static final int EVENT_NANOAPP_LOADED = 0;
|
|
|
|
/**
|
|
* An event describing that a nanoapp has been unloaded. Contains the EXTRA_NANOAPP_ID extra.
|
|
*/
|
|
public static final int EVENT_NANOAPP_UNLOADED = 1;
|
|
|
|
/**
|
|
* An event describing that a nanoapp has been enabled. Contains the EXTRA_NANOAPP_ID extra.
|
|
*/
|
|
public static final int EVENT_NANOAPP_ENABLED = 2;
|
|
|
|
/**
|
|
* An event describing that a nanoapp has been disabled. Contains the EXTRA_NANOAPP_ID extra.
|
|
*/
|
|
public static final int EVENT_NANOAPP_DISABLED = 3;
|
|
|
|
/**
|
|
* An event describing that a nanoapp has aborted. Contains the EXTRA_NANOAPP_ID and
|
|
* EXTRA_NANOAPP_ABORT_CODE extras.
|
|
*/
|
|
public static final int EVENT_NANOAPP_ABORTED = 4;
|
|
|
|
/**
|
|
* An event containing a message sent from a nanoapp. Contains the EXTRA_NANOAPP_ID and
|
|
* EXTRA_NANOAPP_MESSAGE extras.
|
|
*/
|
|
public static final int EVENT_NANOAPP_MESSAGE = 5;
|
|
|
|
/**
|
|
* An event describing that the Context Hub has reset.
|
|
*/
|
|
public static final int EVENT_HUB_RESET = 6;
|
|
|
|
/**
|
|
* An event describing a client authorization state change. See
|
|
* {@link ContextHubClientCallback#onClientAuthorizationChanged} for more details on when this
|
|
* event will be sent. Contains the EXTRA_NANOAPP_ID and EXTRA_CLIENT_AUTHORIZATION_STATE
|
|
* extras.
|
|
*/
|
|
public static final int EVENT_CLIENT_AUTHORIZATION = 7;
|
|
|
|
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.
|
|
*
|
|
* @deprecated Use the more refined {@link android.hardware.location.ContextHubClientCallback}
|
|
* instead for notification callbacks.
|
|
*/
|
|
@Deprecated
|
|
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,
|
|
@NonNull 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
|
|
*
|
|
* @deprecated Use {@link #getContextHubs()} instead. The use of handles are deprecated in the
|
|
* new APIs.
|
|
*/
|
|
@Deprecated
|
|
@RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
|
|
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
|
|
*
|
|
* @deprecated Use {@link #getContextHubs()} instead. The use of handles are deprecated in the
|
|
* new APIs.
|
|
*/
|
|
@Deprecated
|
|
@RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
|
|
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
|
|
*
|
|
* @deprecated Use {@link #loadNanoApp(ContextHubInfo, NanoAppBinary)} instead.
|
|
*/
|
|
@Deprecated
|
|
@RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
|
|
public int loadNanoApp(int hubHandle, @NonNull 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
|
|
*
|
|
* @deprecated Use {@link #unloadNanoApp(ContextHubInfo, long)} instead.
|
|
*/
|
|
@Deprecated
|
|
@RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
|
|
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 nanoapp instance
|
|
* @return NanoAppInstanceInfo the NanoAppInstanceInfo of the nanoapp, or null if the nanoapp
|
|
* does not exist
|
|
*
|
|
* @see NanoAppInstanceInfo
|
|
*
|
|
* @deprecated Use {@link #queryNanoApps(ContextHubInfo)} instead to explicitly query the hub
|
|
* for loaded nanoapps.
|
|
*/
|
|
@Deprecated
|
|
@RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
|
|
@Nullable 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
|
|
*
|
|
* @deprecated Use {@link #queryNanoApps(ContextHubInfo)} instead to explicitly query the hub
|
|
* for loaded nanoapps.
|
|
*/
|
|
@Deprecated
|
|
@RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
|
|
@NonNull public int[] findNanoAppOnHub(int hubHandle, @NonNull 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
|
|
*
|
|
* @deprecated Use {@link android.hardware.location.ContextHubClient#sendMessageToNanoApp(
|
|
* NanoAppMessage)} instead, after creating a
|
|
* {@link android.hardware.location.ContextHubClient} with
|
|
* {@link #createClient(ContextHubInfo, ContextHubClientCallback, Executor)}
|
|
* or {@link #createClient(ContextHubInfo, ContextHubClientCallback)}.
|
|
*/
|
|
@Deprecated
|
|
@RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
|
|
public int sendMessage(int hubHandle, int nanoAppHandle, @NonNull ContextHubMessage message) {
|
|
try {
|
|
return mService.sendMessage(hubHandle, nanoAppHandle, message);
|
|
} catch (RemoteException e) {
|
|
throw e.rethrowFromSystemServer();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns the list of ContextHubInfo objects describing the available Context Hubs.
|
|
*
|
|
* @return the list of ContextHubInfo objects
|
|
*
|
|
* @see ContextHubInfo
|
|
*/
|
|
@RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
|
|
@NonNull public List<ContextHubInfo> getContextHubs() {
|
|
try {
|
|
return mService.getContextHubs();
|
|
} catch (RemoteException e) {
|
|
throw e.rethrowFromSystemServer();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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.RESULT_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
|
|
*
|
|
* @throws NullPointerException if hubInfo or NanoAppBinary is null
|
|
*
|
|
* @see NanoAppBinary
|
|
*/
|
|
@RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
|
|
@NonNull public ContextHubTransaction<Void> loadNanoApp(
|
|
@NonNull ContextHubInfo hubInfo, @NonNull NanoAppBinary appBinary) {
|
|
Objects.requireNonNull(hubInfo, "ContextHubInfo cannot be null");
|
|
Objects.requireNonNull(appBinary, "NanoAppBinary cannot be null");
|
|
|
|
ContextHubTransaction<Void> transaction =
|
|
new ContextHubTransaction<>(ContextHubTransaction.TYPE_LOAD_NANOAPP);
|
|
IContextHubTransactionCallback callback =
|
|
ContextHubTransactionHelper.createTransactionCallback(transaction);
|
|
|
|
try {
|
|
mService.loadNanoAppOnHub(hubInfo.getId(), callback, appBinary);
|
|
} catch (RemoteException e) {
|
|
throw e.rethrowFromSystemServer();
|
|
}
|
|
|
|
return transaction;
|
|
}
|
|
|
|
/**
|
|
* 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
|
|
*
|
|
* @throws NullPointerException if hubInfo is null
|
|
*/
|
|
@RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
|
|
@NonNull public ContextHubTransaction<Void> unloadNanoApp(
|
|
@NonNull ContextHubInfo hubInfo, long nanoAppId) {
|
|
Objects.requireNonNull(hubInfo, "ContextHubInfo cannot be null");
|
|
|
|
ContextHubTransaction<Void> transaction =
|
|
new ContextHubTransaction<>(ContextHubTransaction.TYPE_UNLOAD_NANOAPP);
|
|
IContextHubTransactionCallback callback =
|
|
ContextHubTransactionHelper.createTransactionCallback(transaction);
|
|
|
|
try {
|
|
mService.unloadNanoAppFromHub(hubInfo.getId(), callback, nanoAppId);
|
|
} catch (RemoteException e) {
|
|
throw e.rethrowFromSystemServer();
|
|
}
|
|
|
|
return transaction;
|
|
}
|
|
|
|
/**
|
|
* 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
|
|
*
|
|
* @throws NullPointerException if hubInfo is null
|
|
*/
|
|
@RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
|
|
@NonNull public ContextHubTransaction<Void> enableNanoApp(
|
|
@NonNull ContextHubInfo hubInfo, long nanoAppId) {
|
|
Objects.requireNonNull(hubInfo, "ContextHubInfo cannot be null");
|
|
|
|
ContextHubTransaction<Void> transaction =
|
|
new ContextHubTransaction<>(ContextHubTransaction.TYPE_ENABLE_NANOAPP);
|
|
IContextHubTransactionCallback callback =
|
|
ContextHubTransactionHelper.createTransactionCallback(transaction);
|
|
|
|
try {
|
|
mService.enableNanoApp(hubInfo.getId(), callback, nanoAppId);
|
|
} catch (RemoteException e) {
|
|
throw e.rethrowFromSystemServer();
|
|
}
|
|
|
|
return transaction;
|
|
}
|
|
|
|
/**
|
|
* 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
|
|
*
|
|
* @throws NullPointerException if hubInfo is null
|
|
*/
|
|
@RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
|
|
@NonNull public ContextHubTransaction<Void> disableNanoApp(
|
|
@NonNull ContextHubInfo hubInfo, long nanoAppId) {
|
|
Objects.requireNonNull(hubInfo, "ContextHubInfo cannot be null");
|
|
|
|
ContextHubTransaction<Void> transaction =
|
|
new ContextHubTransaction<>(ContextHubTransaction.TYPE_DISABLE_NANOAPP);
|
|
IContextHubTransactionCallback callback =
|
|
ContextHubTransactionHelper.createTransactionCallback(transaction);
|
|
|
|
try {
|
|
mService.disableNanoApp(hubInfo.getId(), callback, nanoAppId);
|
|
} catch (RemoteException e) {
|
|
throw e.rethrowFromSystemServer();
|
|
}
|
|
|
|
return transaction;
|
|
}
|
|
|
|
/**
|
|
* 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
|
|
*
|
|
* @throws NullPointerException if hubInfo is null
|
|
*/
|
|
@RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
|
|
@NonNull public ContextHubTransaction<List<NanoAppState>> queryNanoApps(
|
|
@NonNull ContextHubInfo hubInfo) {
|
|
Objects.requireNonNull(hubInfo, "ContextHubInfo cannot be null");
|
|
|
|
ContextHubTransaction<List<NanoAppState>> transaction =
|
|
new ContextHubTransaction<>(ContextHubTransaction.TYPE_QUERY_NANOAPPS);
|
|
IContextHubTransactionCallback callback = createQueryCallback(transaction);
|
|
|
|
try {
|
|
mService.queryNanoApps(hubInfo.getId(), callback);
|
|
} catch (RemoteException e) {
|
|
throw e.rethrowFromSystemServer();
|
|
}
|
|
|
|
return transaction;
|
|
}
|
|
|
|
/**
|
|
* Set a callback to receive messages from the context hub
|
|
*
|
|
* @param callback Callback object
|
|
*
|
|
* @see Callback
|
|
*
|
|
* @return int 0 on success, -1 otherwise
|
|
*
|
|
* @deprecated Use {@link #createClient(ContextHubInfo, ContextHubClientCallback, Executor)}
|
|
* or {@link #createClient(ContextHubInfo, ContextHubClientCallback)} instead to
|
|
* register a {@link android.hardware.location.ContextHubClientCallback}.
|
|
*/
|
|
@Deprecated
|
|
@SuppressLint("RequiresPermission")
|
|
public int registerCallback(@NonNull 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, if null uses the Handler of the main Looper
|
|
*
|
|
* @see Callback
|
|
*
|
|
* @return int 0 on success, -1 otherwise
|
|
*
|
|
* @deprecated Use {@link #createClient(ContextHubInfo, ContextHubClientCallback, Executor)}
|
|
* or {@link #createClient(ContextHubInfo, ContextHubClientCallback)} instead to
|
|
* register a {@link android.hardware.location.ContextHubClientCallback}.
|
|
*/
|
|
@Deprecated
|
|
@SuppressLint("RequiresPermission")
|
|
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 == null) ? new Handler(mMainLooper) : handler;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Creates an interface to the ContextHubClient to send down to the service.
|
|
*
|
|
* @param client the ContextHubClient object associated with this callback
|
|
* @param callback the callback to invoke at the client process
|
|
* @param executor the executor to invoke callbacks for this client
|
|
*
|
|
* @return the callback interface
|
|
*/
|
|
private IContextHubClientCallback createClientCallback(
|
|
ContextHubClient client, ContextHubClientCallback callback, Executor executor) {
|
|
return new IContextHubClientCallback.Stub() {
|
|
@Override
|
|
public void onMessageFromNanoApp(NanoAppMessage message) {
|
|
executor.execute(
|
|
() -> {
|
|
callback.onMessageFromNanoApp(client, message);
|
|
if (Flags.reliableMessage()
|
|
&& Flags.reliableMessageImplementation()
|
|
&& message.isReliable()) {
|
|
client.reliableMessageCallbackFinished(
|
|
message.getMessageSequenceNumber(), ErrorCode.OK);
|
|
} else {
|
|
client.callbackFinished();
|
|
}
|
|
});
|
|
}
|
|
|
|
@Override
|
|
public void onHubReset() {
|
|
executor.execute(
|
|
() -> {
|
|
callback.onHubReset(client);
|
|
client.callbackFinished();
|
|
});
|
|
}
|
|
|
|
@Override
|
|
public void onNanoAppAborted(long nanoAppId, int abortCode) {
|
|
executor.execute(
|
|
() -> {
|
|
callback.onNanoAppAborted(client, nanoAppId, abortCode);
|
|
client.callbackFinished();
|
|
});
|
|
}
|
|
|
|
@Override
|
|
public void onNanoAppLoaded(long nanoAppId) {
|
|
executor.execute(
|
|
() -> {
|
|
callback.onNanoAppLoaded(client, nanoAppId);
|
|
client.callbackFinished();
|
|
});
|
|
}
|
|
|
|
@Override
|
|
public void onNanoAppUnloaded(long nanoAppId) {
|
|
executor.execute(
|
|
() -> {
|
|
callback.onNanoAppUnloaded(client, nanoAppId);
|
|
client.callbackFinished();
|
|
});
|
|
}
|
|
|
|
@Override
|
|
public void onNanoAppEnabled(long nanoAppId) {
|
|
executor.execute(
|
|
() -> {
|
|
callback.onNanoAppEnabled(client, nanoAppId);
|
|
client.callbackFinished();
|
|
});
|
|
}
|
|
|
|
@Override
|
|
public void onNanoAppDisabled(long nanoAppId) {
|
|
executor.execute(
|
|
() -> {
|
|
callback.onNanoAppDisabled(client, nanoAppId);
|
|
client.callbackFinished();
|
|
});
|
|
}
|
|
|
|
@Override
|
|
public void onClientAuthorizationChanged(
|
|
long nanoAppId, @ContextHubManager.AuthorizationState int authorization) {
|
|
executor.execute(
|
|
() -> {
|
|
callback.onClientAuthorizationChanged(client, nanoAppId, authorization);
|
|
client.callbackFinished();
|
|
});
|
|
}
|
|
};
|
|
}
|
|
|
|
/**
|
|
* 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 context the context of the application
|
|
* @param hubInfo the hub to attach this client to
|
|
* @param executor the executor to invoke the callback
|
|
* @param callback the notification callback to register
|
|
* @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, hubInfo, or executor is null
|
|
*
|
|
* @see ContextHubClientCallback
|
|
*/
|
|
@RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
|
|
@NonNull public ContextHubClient createClient(
|
|
@Nullable Context context, @NonNull ContextHubInfo hubInfo,
|
|
@NonNull @CallbackExecutor Executor executor,
|
|
@NonNull ContextHubClientCallback callback) {
|
|
Objects.requireNonNull(callback, "Callback cannot be null");
|
|
Objects.requireNonNull(hubInfo, "ContextHubInfo cannot be null");
|
|
Objects.requireNonNull(executor, "Executor cannot be null");
|
|
|
|
ContextHubClient client = new ContextHubClient(hubInfo, false /* persistent */);
|
|
IContextHubClientCallback clientInterface = createClientCallback(
|
|
client, callback, executor);
|
|
|
|
String attributionTag = null;
|
|
if (context != null) {
|
|
attributionTag = context.getAttributionTag();
|
|
}
|
|
|
|
// Workaround for old APIs not providing a context
|
|
String packageName;
|
|
if (context != null) {
|
|
packageName = context.getPackageName();
|
|
} else {
|
|
packageName = ActivityThread.currentPackageName();
|
|
}
|
|
|
|
IContextHubClient clientProxy;
|
|
try {
|
|
clientProxy = mService.createClient(
|
|
hubInfo.getId(), clientInterface, attributionTag, packageName);
|
|
} catch (RemoteException e) {
|
|
throw e.rethrowFromSystemServer();
|
|
}
|
|
|
|
client.setClientProxy(clientProxy);
|
|
return client;
|
|
}
|
|
|
|
|
|
/**
|
|
* Equivalent to
|
|
* {@link #createClient(Context, ContextHubInfo, Executor, ContextHubClientCallback)}
|
|
* with the {@link Context} being set to null.
|
|
*/
|
|
@RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
|
|
@NonNull public ContextHubClient createClient(
|
|
@NonNull ContextHubInfo hubInfo, @NonNull ContextHubClientCallback callback,
|
|
@NonNull @CallbackExecutor Executor executor) {
|
|
return createClient(null /* context */, hubInfo, executor, callback);
|
|
}
|
|
|
|
/**
|
|
* Equivalent to {@link #createClient(ContextHubInfo, ContextHubClientCallback, Executor)}
|
|
* with the executor using the main thread's Looper.
|
|
*/
|
|
@RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
|
|
@NonNull public ContextHubClient createClient(
|
|
@NonNull ContextHubInfo hubInfo, @NonNull ContextHubClientCallback callback) {
|
|
return createClient(null /* context */, hubInfo, new HandlerExecutor(Handler.getMain()),
|
|
callback);
|
|
}
|
|
|
|
/**
|
|
* Creates a ContextHubClient that will receive notifications based on Intent events.
|
|
*
|
|
* This method should be used instead of {@link #createClient(ContextHubInfo,
|
|
* ContextHubClientCallback)} or {@link #createClient(ContextHubInfo, ContextHubClientCallback,
|
|
* Executor)} if the caller wants to preserve the messaging endpoint of a ContextHubClient, even
|
|
* after a process exits. If the PendingIntent with the provided nanoapp has already been
|
|
* registered at the service, then the same ContextHubClient will be regenerated without
|
|
* creating a new client connection at the service. Note that the PendingIntent, nanoapp, and
|
|
* Context Hub must all match in identifying a previously registered ContextHubClient.
|
|
* If a client is regenerated, the host endpoint identifier attached to messages sent to the
|
|
* nanoapp remains consistent, even if the original process has exited.
|
|
*
|
|
* To avoid unintentionally spreading data from the Context Hub to external applications, it is
|
|
* strongly recommended that the PendingIntent supplied to this API is an explicit intent.
|
|
*
|
|
* If registered successfully, intents will be delivered regarding events or messages from the
|
|
* specified nanoapp from the attached Context Hub. The intent will have an extra
|
|
* {@link ContextHubManager.EXTRA_CONTEXT_HUB_INFO} of type {@link ContextHubInfo}, which
|
|
* describes the Context Hub the intent event was for. The intent will also have an extra
|
|
* {@link ContextHubManager.EXTRA_EVENT_TYPE} of type {@link ContextHubManager.Event}, which
|
|
* will contain the type of the event. See {@link ContextHubManager.Event} for description of
|
|
* each event type, along with event-specific extra fields. The client can also use
|
|
* {@link ContextHubIntentEvent.fromIntent(Intent)} to parse the Intent generated by the event.
|
|
*
|
|
* Intent events will be delivered until {@link ContextHubClient.close()} is called. Note that
|
|
* the registration of this ContextHubClient at the Context Hub Service will be maintained until
|
|
* {@link ContextHubClient.close()} is called. If {@link PendingIntent.cancel()} is called
|
|
* on the provided PendingIntent, then the client will be automatically unregistered by the
|
|
* service.
|
|
*
|
|
* Note that the {@link PendingIntent} supplied to this API must be mutable for Intent
|
|
* notifications to work.
|
|
*
|
|
* @param context the context of the application. If a PendingIntent client is recreated,
|
|
* the latest state in the context will be used and old state will be discarded
|
|
* @param hubInfo the hub to attach this client to
|
|
* @param pendingIntent the PendingIntent to register to the client
|
|
* @param nanoAppId the ID of the nanoapp that Intent events will be generated for
|
|
* @return the registered client object
|
|
*
|
|
* @throws IllegalArgumentException if hubInfo does not represent a valid hub, or an immutable
|
|
* PendingIntent was supplied
|
|
* @throws IllegalStateException if there were too many registered clients at the service
|
|
* @throws NullPointerException if pendingIntent or hubInfo is null
|
|
*/
|
|
@RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
|
|
@NonNull public ContextHubClient createClient(
|
|
@Nullable Context context, @NonNull ContextHubInfo hubInfo,
|
|
@NonNull PendingIntent pendingIntent, long nanoAppId) {
|
|
Objects.requireNonNull(pendingIntent);
|
|
Objects.requireNonNull(hubInfo);
|
|
if (pendingIntent.isImmutable()) {
|
|
throw new IllegalArgumentException("PendingIntent must be mutable");
|
|
}
|
|
|
|
ContextHubClient client = new ContextHubClient(hubInfo, true /* persistent */);
|
|
|
|
String attributionTag = null;
|
|
if (context != null) {
|
|
attributionTag = context.getAttributionTag();
|
|
}
|
|
|
|
IContextHubClient clientProxy;
|
|
try {
|
|
clientProxy = mService.createPendingIntentClient(
|
|
hubInfo.getId(), pendingIntent, nanoAppId, attributionTag);
|
|
} catch (RemoteException e) {
|
|
throw e.rethrowFromSystemServer();
|
|
}
|
|
|
|
client.setClientProxy(clientProxy);
|
|
return client;
|
|
}
|
|
|
|
/**
|
|
* Equivalent to {@link #createClient(ContextHubInfo, PendingIntent, long, String)}
|
|
* with {@link Context} being set to null.
|
|
*/
|
|
@RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
|
|
@NonNull public ContextHubClient createClient(
|
|
@NonNull ContextHubInfo hubInfo, @NonNull PendingIntent pendingIntent, long nanoAppId) {
|
|
return createClient(null /* context */, hubInfo, pendingIntent, nanoAppId);
|
|
}
|
|
|
|
/**
|
|
* Queries for the list of preloaded nanoapp IDs on the system.
|
|
*
|
|
* @param hubInfo The Context Hub to query a list of nanoapp IDs from.
|
|
*
|
|
* @return The list of 64-bit IDs of the preloaded nanoapps.
|
|
*
|
|
* @throws NullPointerException if hubInfo is null
|
|
* @hide
|
|
*/
|
|
@TestApi
|
|
@RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
|
|
@NonNull public long[] getPreloadedNanoAppIds(@NonNull ContextHubInfo hubInfo) {
|
|
Objects.requireNonNull(hubInfo, "hubInfo cannot be null");
|
|
|
|
long[] nanoappIds = null;
|
|
try {
|
|
nanoappIds = mService.getPreloadedNanoAppIds(hubInfo);
|
|
} catch (RemoteException e) {
|
|
throw e.rethrowFromSystemServer();
|
|
}
|
|
|
|
if (nanoappIds == null) {
|
|
nanoappIds = new long[0];
|
|
}
|
|
return nanoappIds;
|
|
}
|
|
|
|
/**
|
|
* Puts the Context Hub in test mode.
|
|
*
|
|
* The purpose of this API is to make testing CHRE/Context Hub more
|
|
* predictable and robust. This temporarily unloads all
|
|
* nanoapps.
|
|
*
|
|
* Note that this API must not cause CHRE/Context Hub to behave differently
|
|
* in test compared to production.
|
|
*
|
|
* @return true if the enable test mode operation succeeded.
|
|
* @hide
|
|
*/
|
|
@TestApi
|
|
@RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
|
|
@NonNull public boolean enableTestMode() {
|
|
try {
|
|
return mService.setTestMode(true);
|
|
} catch (RemoteException e) {
|
|
throw e.rethrowFromSystemServer();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Puts the Context Hub out of test mode.
|
|
*
|
|
* This API will undo any previously made enableTestMode() calls.
|
|
* After this API is called, it should restore the state of the system
|
|
* to the normal/production mode before any enableTestMode() call was
|
|
* made. If the enableTestMode() call unloaded any nanoapps
|
|
* to enter test mode, it should reload those nanoapps in this API call.
|
|
*
|
|
* @return true if the disable operation succeeded.
|
|
* @hide
|
|
*/
|
|
@TestApi
|
|
@RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
|
|
@NonNull public boolean disableTestMode() {
|
|
try {
|
|
return mService.setTestMode(false);
|
|
} catch (RemoteException e) {
|
|
throw e.rethrowFromSystemServer();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Unregister a callback for receive messages from the context hub.
|
|
*
|
|
* @see Callback
|
|
*
|
|
* @param callback method to deregister
|
|
*
|
|
* @return int 0 on success, -1 otherwise
|
|
*
|
|
* @deprecated Use {@link android.hardware.location.ContextHubClient#close()} to unregister
|
|
* a {@link android.hardware.location.ContextHubClientCallback}.
|
|
*/
|
|
@SuppressLint("RequiresPermission")
|
|
@Deprecated
|
|
public int unregisterCallback(@NonNull 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;
|
|
}
|
|
|
|
/**
|
|
* Invokes the ContextHubManager.Callback callback registered with the ContextHubManager.
|
|
*
|
|
* @param hubId The ID of the Context Hub the message came from
|
|
* @param nanoAppId The instance ID of the nanoapp the message came from
|
|
* @param message The message to provide the callback
|
|
*/
|
|
private synchronized void invokeOnMessageReceiptCallback(
|
|
int hubId, int nanoAppId, ContextHubMessage message) {
|
|
if (mCallback != null) {
|
|
mCallback.onMessageReceipt(hubId, nanoAppId, message);
|
|
}
|
|
}
|
|
|
|
private final IContextHubCallback.Stub mClientCallback = new IContextHubCallback.Stub() {
|
|
@Override
|
|
public void onMessageReceipt(
|
|
final int hubId, final int nanoAppId, final ContextHubMessage message) {
|
|
synchronized (ContextHubManager.this) {
|
|
if (mCallback != null) {
|
|
mCallbackHandler.post(
|
|
() -> invokeOnMessageReceiptCallback(hubId, nanoAppId, message));
|
|
} else if (mLocalCallback != null) {
|
|
// We always ensure that mCallback takes precedence, because mLocalCallback is
|
|
// only for internal compatibility
|
|
mLocalCallback.onMessageReceipt(hubId, nanoAppId, message);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
/** @hide */
|
|
public ContextHubManager(@NonNull IContextHubService service, @NonNull Looper mainLooper) {
|
|
requireNonNull(service, "service cannot be null");
|
|
requireNonNull(mainLooper, "mainLooper cannot be null");
|
|
mService = service;
|
|
mMainLooper = mainLooper;
|
|
try {
|
|
mService.registerCallback(mClientCallback);
|
|
} catch (RemoteException e) {
|
|
throw e.rethrowFromSystemServer();
|
|
}
|
|
}
|
|
}
|