294 lines
10 KiB
Java
294 lines
10 KiB
Java
/*
|
|
* Copyright (C) 2014 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.Manifest;
|
|
import android.content.Context;
|
|
import android.os.RemoteCallbackList;
|
|
import android.os.RemoteException;
|
|
import android.text.TextUtils;
|
|
import android.util.Log;
|
|
|
|
/**
|
|
* A class that implements an {@link IActivityRecognitionHardware} backed up by the Activity
|
|
* Recognition HAL.
|
|
*
|
|
* @hide
|
|
*/
|
|
public class ActivityRecognitionHardware extends IActivityRecognitionHardware.Stub {
|
|
private static final String TAG = "ActivityRecognitionHW";
|
|
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
|
|
|
|
private static final String HARDWARE_PERMISSION = Manifest.permission.LOCATION_HARDWARE;
|
|
private static final String ENFORCE_HW_PERMISSION_MESSAGE = "Permission '"
|
|
+ HARDWARE_PERMISSION + "' not granted to access ActivityRecognitionHardware";
|
|
|
|
private static final int INVALID_ACTIVITY_TYPE = -1;
|
|
private static final int NATIVE_SUCCESS_RESULT = 0;
|
|
private static final int EVENT_TYPE_DISABLED = 0;
|
|
private static final int EVENT_TYPE_ENABLED = 1;
|
|
|
|
/**
|
|
* Contains the number of supported Event Types.
|
|
*
|
|
* NOTE: increment this counter every time a new EVENT_TYPE_ is added to
|
|
* com.android.location.provider.ActivityRecognitionProvider
|
|
*/
|
|
private static final int EVENT_TYPE_COUNT = 3;
|
|
|
|
private static ActivityRecognitionHardware sSingletonInstance;
|
|
private static final Object sSingletonInstanceLock = new Object();
|
|
|
|
private final Context mContext;
|
|
private final int mSupportedActivitiesCount;
|
|
private final String[] mSupportedActivities;
|
|
private final int[][] mSupportedActivitiesEnabledEvents;
|
|
private final SinkList mSinks = new SinkList();
|
|
|
|
private static class Event {
|
|
public int activity;
|
|
public int type;
|
|
public long timestamp;
|
|
}
|
|
|
|
private ActivityRecognitionHardware(Context context) {
|
|
nativeInitialize();
|
|
|
|
mContext = context;
|
|
mSupportedActivities = fetchSupportedActivities();
|
|
mSupportedActivitiesCount = mSupportedActivities.length;
|
|
mSupportedActivitiesEnabledEvents = new int[mSupportedActivitiesCount][EVENT_TYPE_COUNT];
|
|
}
|
|
|
|
public static ActivityRecognitionHardware getInstance(Context context) {
|
|
synchronized (sSingletonInstanceLock) {
|
|
if (sSingletonInstance == null) {
|
|
sSingletonInstance = new ActivityRecognitionHardware(context);
|
|
}
|
|
|
|
return sSingletonInstance;
|
|
}
|
|
}
|
|
|
|
public static boolean isSupported() {
|
|
return nativeIsSupported();
|
|
}
|
|
|
|
@android.annotation.EnforcePermission(android.Manifest.permission.LOCATION_HARDWARE)
|
|
@Override
|
|
public String[] getSupportedActivities() {
|
|
super.getSupportedActivities_enforcePermission();
|
|
|
|
return mSupportedActivities;
|
|
}
|
|
|
|
@android.annotation.EnforcePermission(android.Manifest.permission.LOCATION_HARDWARE)
|
|
@Override
|
|
public boolean isActivitySupported(String activity) {
|
|
super.isActivitySupported_enforcePermission();
|
|
|
|
int activityType = getActivityType(activity);
|
|
return activityType != INVALID_ACTIVITY_TYPE;
|
|
}
|
|
|
|
@android.annotation.EnforcePermission(android.Manifest.permission.LOCATION_HARDWARE)
|
|
@Override
|
|
public boolean registerSink(IActivityRecognitionHardwareSink sink) {
|
|
super.registerSink_enforcePermission();
|
|
|
|
return mSinks.register(sink);
|
|
}
|
|
|
|
@android.annotation.EnforcePermission(android.Manifest.permission.LOCATION_HARDWARE)
|
|
@Override
|
|
public boolean unregisterSink(IActivityRecognitionHardwareSink sink) {
|
|
super.unregisterSink_enforcePermission();
|
|
|
|
return mSinks.unregister(sink);
|
|
}
|
|
|
|
@android.annotation.EnforcePermission(android.Manifest.permission.LOCATION_HARDWARE)
|
|
@Override
|
|
public boolean enableActivityEvent(String activity, int eventType, long reportLatencyNs) {
|
|
|
|
super.enableActivityEvent_enforcePermission();
|
|
|
|
int activityType = getActivityType(activity);
|
|
if (activityType == INVALID_ACTIVITY_TYPE) {
|
|
return false;
|
|
}
|
|
|
|
int result = nativeEnableActivityEvent(activityType, eventType, reportLatencyNs);
|
|
if (result == NATIVE_SUCCESS_RESULT) {
|
|
mSupportedActivitiesEnabledEvents[activityType][eventType] = EVENT_TYPE_ENABLED;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
@android.annotation.EnforcePermission(android.Manifest.permission.LOCATION_HARDWARE)
|
|
@Override
|
|
public boolean disableActivityEvent(String activity, int eventType) {
|
|
|
|
super.disableActivityEvent_enforcePermission();
|
|
|
|
int activityType = getActivityType(activity);
|
|
if (activityType == INVALID_ACTIVITY_TYPE) {
|
|
return false;
|
|
}
|
|
|
|
int result = nativeDisableActivityEvent(activityType, eventType);
|
|
if (result == NATIVE_SUCCESS_RESULT) {
|
|
mSupportedActivitiesEnabledEvents[activityType][eventType] = EVENT_TYPE_DISABLED;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
@android.annotation.EnforcePermission(android.Manifest.permission.LOCATION_HARDWARE)
|
|
@Override
|
|
public boolean flush() {
|
|
super.flush_enforcePermission();
|
|
|
|
int result = nativeFlush();
|
|
return result == NATIVE_SUCCESS_RESULT;
|
|
}
|
|
|
|
/**
|
|
* Called by the Activity-Recognition HAL.
|
|
*/
|
|
private void onActivityChanged(Event[] events) {
|
|
if (events == null || events.length == 0) {
|
|
if (DEBUG) Log.d(TAG, "No events to broadcast for onActivityChanged.");
|
|
return;
|
|
}
|
|
|
|
int eventsLength = events.length;
|
|
ActivityRecognitionEvent activityRecognitionEventArray[] =
|
|
new ActivityRecognitionEvent[eventsLength];
|
|
for (int i = 0; i < eventsLength; ++i) {
|
|
Event event = events[i];
|
|
String activityName = getActivityName(event.activity);
|
|
activityRecognitionEventArray[i] =
|
|
new ActivityRecognitionEvent(activityName, event.type, event.timestamp);
|
|
}
|
|
ActivityChangedEvent activityChangedEvent =
|
|
new ActivityChangedEvent(activityRecognitionEventArray);
|
|
|
|
int size = mSinks.beginBroadcast();
|
|
for (int i = 0; i < size; ++i) {
|
|
IActivityRecognitionHardwareSink sink = mSinks.getBroadcastItem(i);
|
|
try {
|
|
sink.onActivityChanged(activityChangedEvent);
|
|
} catch (RemoteException e) {
|
|
Log.e(TAG, "Error delivering activity changed event.", e);
|
|
}
|
|
}
|
|
mSinks.finishBroadcast();
|
|
}
|
|
|
|
private String getActivityName(int activityType) {
|
|
if (activityType < 0 || activityType >= mSupportedActivities.length) {
|
|
String message = String.format(
|
|
"Invalid ActivityType: %d, SupportedActivities: %d",
|
|
activityType,
|
|
mSupportedActivities.length);
|
|
Log.e(TAG, message);
|
|
return null;
|
|
}
|
|
|
|
return mSupportedActivities[activityType];
|
|
}
|
|
|
|
private int getActivityType(String activity) {
|
|
if (TextUtils.isEmpty(activity)) {
|
|
return INVALID_ACTIVITY_TYPE;
|
|
}
|
|
|
|
int supportedActivitiesLength = mSupportedActivities.length;
|
|
for (int i = 0; i < supportedActivitiesLength; ++i) {
|
|
if (activity.equals(mSupportedActivities[i])) {
|
|
return i;
|
|
}
|
|
}
|
|
|
|
return INVALID_ACTIVITY_TYPE;
|
|
}
|
|
|
|
private void checkPermissions() {
|
|
mContext.enforceCallingPermission(HARDWARE_PERMISSION, ENFORCE_HW_PERMISSION_MESSAGE);
|
|
}
|
|
|
|
private String[] fetchSupportedActivities() {
|
|
String[] supportedActivities = nativeGetSupportedActivities();
|
|
if (supportedActivities != null) {
|
|
return supportedActivities;
|
|
}
|
|
|
|
return new String[0];
|
|
}
|
|
|
|
private class SinkList extends RemoteCallbackList<IActivityRecognitionHardwareSink> {
|
|
@Override
|
|
public void onCallbackDied(IActivityRecognitionHardwareSink callback) {
|
|
int callbackCount = mSinks.getRegisteredCallbackCount();
|
|
if (DEBUG) Log.d(TAG, "RegisteredCallbackCount: " + callbackCount);
|
|
if (callbackCount != 0) {
|
|
return;
|
|
}
|
|
// currently there is only one client for this, so if all its sinks have died, we clean
|
|
// up after them, this ensures that the AR HAL is not out of sink
|
|
for (int activity = 0; activity < mSupportedActivitiesCount; ++activity) {
|
|
for (int event = 0; event < EVENT_TYPE_COUNT; ++event) {
|
|
disableActivityEventIfEnabled(activity, event);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void disableActivityEventIfEnabled(int activityType, int eventType) {
|
|
if (mSupportedActivitiesEnabledEvents[activityType][eventType] != EVENT_TYPE_ENABLED) {
|
|
return;
|
|
}
|
|
|
|
int result = nativeDisableActivityEvent(activityType, eventType);
|
|
mSupportedActivitiesEnabledEvents[activityType][eventType] = EVENT_TYPE_DISABLED;
|
|
String message = String.format(
|
|
"DisableActivityEvent: activityType=%d, eventType=%d, result=%d",
|
|
activityType,
|
|
eventType,
|
|
result);
|
|
Log.e(TAG, message);
|
|
}
|
|
}
|
|
|
|
// native bindings
|
|
static { nativeClassInit(); }
|
|
|
|
private static native void nativeClassInit();
|
|
private static native boolean nativeIsSupported();
|
|
|
|
private native void nativeInitialize();
|
|
private native void nativeRelease();
|
|
private native String[] nativeGetSupportedActivities();
|
|
private native int nativeEnableActivityEvent(
|
|
int activityType,
|
|
int eventType,
|
|
long reportLatenceNs);
|
|
private native int nativeDisableActivityEvent(int activityType, int eventType);
|
|
private native int nativeFlush();
|
|
}
|