diff options
19 files changed, 1289 insertions, 1 deletions
@@ -156,6 +156,9 @@ LOCAL_SRC_FILES += \ core/java/android/hardware/hdmi/IHdmiVendorCommandListener.aidl \ core/java/android/hardware/input/IInputManager.aidl \ core/java/android/hardware/input/IInputDevicesChangedListener.aidl \ + core/java/android/hardware/location/IActivityRecognitionHardware.aidl \ + core/java/android/hardware/location/IActivityRecognitionHardwareSink.aidl \ + core/java/android/hardware/location/IActivityRecognitionHardwareWatcher.aidl \ core/java/android/hardware/location/IFusedLocationHardware.aidl \ core/java/android/hardware/location/IFusedLocationHardwareSink.aidl \ core/java/android/hardware/location/IGeofenceHardware.aidl \ diff --git a/core/java/android/hardware/location/ActivityChangedEvent.aidl b/core/java/android/hardware/location/ActivityChangedEvent.aidl new file mode 100644 index 0000000..21f2445 --- /dev/null +++ b/core/java/android/hardware/location/ActivityChangedEvent.aidl @@ -0,0 +1,19 @@ +/* + * 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; + +parcelable ActivityChangedEvent;
\ No newline at end of file diff --git a/core/java/android/hardware/location/ActivityChangedEvent.java b/core/java/android/hardware/location/ActivityChangedEvent.java new file mode 100644 index 0000000..0a89207 --- /dev/null +++ b/core/java/android/hardware/location/ActivityChangedEvent.java @@ -0,0 +1,79 @@ +/* + * 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.annotation.NonNull; +import android.os.Parcel; +import android.os.Parcelable; + +import java.security.InvalidParameterException; +import java.util.Arrays; +import java.util.List; + +/** + * A class representing an event for Activity changes. + * + * @hide + */ +public class ActivityChangedEvent implements Parcelable { + private final List<ActivityRecognitionEvent> mActivityRecognitionEvents; + + public ActivityChangedEvent(ActivityRecognitionEvent[] activityRecognitionEvents) { + if (activityRecognitionEvents == null) { + throw new InvalidParameterException( + "Parameter 'activityRecognitionEvents' must not be null."); + } + + mActivityRecognitionEvents = Arrays.asList(activityRecognitionEvents); + } + + @NonNull + public Iterable<ActivityRecognitionEvent> getActivityRecognitionEvents() { + return mActivityRecognitionEvents; + } + + public static final Creator<ActivityChangedEvent> CREATOR = + new Creator<ActivityChangedEvent>() { + @Override + public ActivityChangedEvent createFromParcel(Parcel source) { + int activityRecognitionEventsLength = source.readInt(); + ActivityRecognitionEvent[] activityRecognitionEvents = + new ActivityRecognitionEvent[activityRecognitionEventsLength]; + source.readTypedArray(activityRecognitionEvents, ActivityRecognitionEvent.CREATOR); + + return new ActivityChangedEvent(activityRecognitionEvents); + } + + @Override + public ActivityChangedEvent[] newArray(int size) { + return new ActivityChangedEvent[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel parcel, int flags) { + ActivityRecognitionEvent[] activityRecognitionEventArray = + mActivityRecognitionEvents.toArray(new ActivityRecognitionEvent[0]); + parcel.writeInt(activityRecognitionEventArray.length); + parcel.writeTypedArray(activityRecognitionEventArray, flags); + } +} diff --git a/core/java/android/hardware/location/ActivityRecognitionEvent.java b/core/java/android/hardware/location/ActivityRecognitionEvent.java new file mode 100644 index 0000000..5aeb899 --- /dev/null +++ b/core/java/android/hardware/location/ActivityRecognitionEvent.java @@ -0,0 +1,78 @@ +/* + * 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.os.Parcel; +import android.os.Parcelable; + +/** + * A class that represents an Activity Recognition Event. + * + * @hide + */ +public class ActivityRecognitionEvent implements Parcelable { + private final String mActivity; + private final int mEventType; + private final long mTimestampNs; + + public ActivityRecognitionEvent(String activity, int eventType, long timestampNs) { + mActivity = activity; + mEventType = eventType; + mTimestampNs = timestampNs; + } + + public String getActivity() { + return mActivity; + } + + public int getEventType() { + return mEventType; + } + + public long getTimestampNs() { + return mTimestampNs; + } + + public static final Creator<ActivityRecognitionEvent> CREATOR = + new Creator<ActivityRecognitionEvent>() { + @Override + public ActivityRecognitionEvent createFromParcel(Parcel source) { + String activity = source.readString(); + int eventType = source.readInt(); + long timestampNs = source.readLong(); + + return new ActivityRecognitionEvent(activity, eventType, timestampNs); + } + + @Override + public ActivityRecognitionEvent[] newArray(int size) { + return new ActivityRecognitionEvent[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel parcel, int flags) { + parcel.writeString(mActivity); + parcel.writeInt(mEventType); + parcel.writeLong(mTimestampNs); + } +} diff --git a/core/java/android/hardware/location/ActivityRecognitionHardware.java b/core/java/android/hardware/location/ActivityRecognitionHardware.java new file mode 100644 index 0000000..a4ce4ac --- /dev/null +++ b/core/java/android/hardware/location/ActivityRecognitionHardware.java @@ -0,0 +1,224 @@ +/* + * 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 = "ActivityRecognitionHardware"; + + private static final String HARDWARE_PERMISSION = Manifest.permission.LOCATION_HARDWARE; + private static final int INVALID_ACTIVITY_TYPE = -1; + private static final int NATIVE_SUCCESS_RESULT = 0; + + private static ActivityRecognitionHardware sSingletonInstance = null; + private static final Object sSingletonInstanceLock = new Object(); + + private final Context mContext; + private final String[] mSupportedActivities; + + private final RemoteCallbackList<IActivityRecognitionHardwareSink> mSinks = + new RemoteCallbackList<IActivityRecognitionHardwareSink>(); + + private static class Event { + public int activity; + public int type; + public long timestamp; + } + + private ActivityRecognitionHardware(Context context) { + nativeInitialize(); + + mContext = context; + mSupportedActivities = fetchSupportedActivities(); + } + + public static ActivityRecognitionHardware getInstance(Context context) { + synchronized (sSingletonInstanceLock) { + if (sSingletonInstance == null) { + sSingletonInstance = new ActivityRecognitionHardware(context); + } + + return sSingletonInstance; + } + } + + public static boolean isSupported() { + return nativeIsSupported(); + } + + @Override + public String[] getSupportedActivities() { + checkPermissions(); + return mSupportedActivities; + } + + @Override + public boolean isActivitySupported(String activity) { + checkPermissions(); + int activityType = getActivityType(activity); + return activityType != INVALID_ACTIVITY_TYPE; + } + + @Override + public boolean registerSink(IActivityRecognitionHardwareSink sink) { + checkPermissions(); + return mSinks.register(sink); + } + + @Override + public boolean unregisterSink(IActivityRecognitionHardwareSink sink) { + checkPermissions(); + return mSinks.unregister(sink); + } + + @Override + public boolean enableActivityEvent(String activity, int eventType, long reportLatencyNs) { + checkPermissions(); + + int activityType = getActivityType(activity); + if (activityType == INVALID_ACTIVITY_TYPE) { + return false; + } + + int result = nativeEnableActivityEvent(activityType, eventType, reportLatencyNs); + return result == NATIVE_SUCCESS_RESULT; + } + + @Override + public boolean disableActivityEvent(String activity, int eventType) { + checkPermissions(); + + int activityType = getActivityType(activity); + if (activityType == INVALID_ACTIVITY_TYPE) { + return false; + } + + int result = nativeDisableActivityEvent(activityType, eventType); + return result == NATIVE_SUCCESS_RESULT; + } + + @Override + public boolean flush() { + checkPermissions(); + int result = nativeFlush(); + return result == NATIVE_SUCCESS_RESULT; + } + + /** + * Called by the Activity-Recognition HAL. + */ + private void onActivityChanged(Event[] events) { + int size = mSinks.beginBroadcast(); + if (size == 0 || events == null || events.length == 0) { + 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); + + 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 supporteActivitiesLength = mSupportedActivities.length; + for (int i = 0; i < supporteActivitiesLength; ++i) { + if (activity.equals(mSupportedActivities[i])) { + return i; + } + } + + return INVALID_ACTIVITY_TYPE; + } + + private void checkPermissions() { + String message = String.format( + "Permission '%s' not granted to access ActivityRecognitionHardware", + HARDWARE_PERMISSION); + mContext.enforceCallingPermission(HARDWARE_PERMISSION, message); + } + + private static String[] fetchSupportedActivities() { + String[] supportedActivities = nativeGetSupportedActivities(); + if (supportedActivities != null) { + return supportedActivities; + } + + return new String[0]; + } + + // native bindings + static { nativeClassInit(); } + + private static native void nativeClassInit(); + private static native void nativeInitialize(); + private static native void nativeRelease(); + private static native boolean nativeIsSupported(); + private static native String[] nativeGetSupportedActivities(); + private static native int nativeEnableActivityEvent( + int activityType, + int eventType, + long reportLatenceNs); + private static native int nativeDisableActivityEvent(int activityType, int eventType); + private static native int nativeFlush(); +} diff --git a/core/java/android/hardware/location/IActivityRecognitionHardware.aidl b/core/java/android/hardware/location/IActivityRecognitionHardware.aidl new file mode 100644 index 0000000..bc6b183 --- /dev/null +++ b/core/java/android/hardware/location/IActivityRecognitionHardware.aidl @@ -0,0 +1,62 @@ +/* + * 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/license/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.hardware.location.IActivityRecognitionHardwareSink; + +/** + * Activity Recognition Hardware provider interface. + * This interface can be used to implement hardware based activity recognition. + * + * @hide + */ +interface IActivityRecognitionHardware { + /** + * Gets an array of supported activities by hardware. + */ + String[] getSupportedActivities(); + + /** + * Returns true if the given activity is supported, false otherwise. + */ + boolean isActivitySupported(in String activityType); + + /** + * Registers a sink with Hardware Activity-Recognition. + */ + boolean registerSink(in IActivityRecognitionHardwareSink sink); + + /** + * Unregisters a sink with Hardware Activity-Recognition. + */ + boolean unregisterSink(in IActivityRecognitionHardwareSink sink); + + /** + * Enables tracking of a given activity/event type, if the activity is supported. + */ + boolean enableActivityEvent(in String activityType, int eventType, long reportLatencyNs); + + /** + * Disables tracking of a given activity/eventy type. + */ + boolean disableActivityEvent(in String activityType, int eventType); + + /** + * Requests hardware for all the activity events detected up to the given point in time. + */ + boolean flush(); +}
\ No newline at end of file diff --git a/core/java/android/hardware/location/IActivityRecognitionHardwareSink.aidl b/core/java/android/hardware/location/IActivityRecognitionHardwareSink.aidl new file mode 100644 index 0000000..21c8e87 --- /dev/null +++ b/core/java/android/hardware/location/IActivityRecognitionHardwareSink.aidl @@ -0,0 +1,32 @@ +/* + * 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/license/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.hardware.location.ActivityChangedEvent; + +/** + * Activity Recognition Hardware provider Sink interface. + * This interface can be used to implement sinks to receive activity notifications. + * + * @hide + */ +interface IActivityRecognitionHardwareSink { + /** + * Activity changed event. + */ + void onActivityChanged(in ActivityChangedEvent event); +}
\ No newline at end of file diff --git a/core/java/android/hardware/location/IActivityRecognitionHardwareWatcher.aidl b/core/java/android/hardware/location/IActivityRecognitionHardwareWatcher.aidl new file mode 100644 index 0000000..0507f52 --- /dev/null +++ b/core/java/android/hardware/location/IActivityRecognitionHardwareWatcher.aidl @@ -0,0 +1,32 @@ +/* + * 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/license/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.hardware.location.IActivityRecognitionHardware; + +/** + * Activity Recognition Hardware watcher. This interface can be used to receive interfaces to + * implementations of {@link IActivityRecognitionHardware}. + * + * @hide + */ +interface IActivityRecognitionHardwareWatcher { + /** + * Hardware Activity-Recognition availability event. + */ + void onInstanceChanged(in IActivityRecognitionHardware instance); +}
\ No newline at end of file diff --git a/core/jni/Android.mk b/core/jni/Android.mk index ef7ef0a..0e22174 100644 --- a/core/jni/Android.mk +++ b/core/jni/Android.mk @@ -147,6 +147,7 @@ LOCAL_SRC_FILES:= \ android_hardware_UsbDevice.cpp \ android_hardware_UsbDeviceConnection.cpp \ android_hardware_UsbRequest.cpp \ + android_hardware_location_ActivityRecognitionHardware.cpp \ android_util_FileObserver.cpp \ android/opengl/poly_clip.cpp.arm \ android/opengl/util.cpp.arm \ diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index 9b66734..92a8fca 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -87,6 +87,7 @@ extern int register_android_hardware_SoundTrigger(JNIEnv *env); extern int register_android_hardware_UsbDevice(JNIEnv *env); extern int register_android_hardware_UsbDeviceConnection(JNIEnv *env); extern int register_android_hardware_UsbRequest(JNIEnv *env); +extern int register_android_hardware_location_ActivityRecognitionHardware(JNIEnv* env); extern int register_android_media_AudioRecord(JNIEnv *env); extern int register_android_media_AudioSystem(JNIEnv *env); @@ -1323,6 +1324,7 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_hardware_UsbDevice), REG_JNI(register_android_hardware_UsbDeviceConnection), REG_JNI(register_android_hardware_UsbRequest), + REG_JNI(register_android_hardware_location_ActivityRecognitionHardware), REG_JNI(register_android_media_AudioRecord), REG_JNI(register_android_media_AudioSystem), REG_JNI(register_android_media_AudioTrack), diff --git a/core/jni/android_hardware_location_ActivityRecognitionHardware.cpp b/core/jni/android_hardware_location_ActivityRecognitionHardware.cpp new file mode 100644 index 0000000..5b542ba --- /dev/null +++ b/core/jni/android_hardware_location_ActivityRecognitionHardware.cpp @@ -0,0 +1,304 @@ +/* + * Copyright 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. + */ + +#define LOG_TAG "ActivityRecognitionHardware" + +#include <jni.h> +#include <JNIHelp.h> + +#include <android_runtime/AndroidRuntime.h> +#include <android_runtime/Log.h> + +#include "activity_recognition.h" + + +// keep base connection data from the HAL +static activity_recognition_module_t* sModule = NULL; +static activity_recognition_device_t* sDevice = NULL; + +static jobject sCallbacksObject = NULL; +static jmethodID sOnActivityChanged = NULL; + + +static void check_and_clear_exceptions(JNIEnv* env, const char* method_name) { + if (!env->ExceptionCheck()) { + return; + } + + ALOGE("An exception was thrown by '%s'.", method_name); + LOGE_EX(env); + env->ExceptionClear(); +} + +static jint attach_thread(JNIEnv** env) { + JavaVM* java_vm = android::AndroidRuntime::getJavaVM(); + assert(java_vm != NULL); + + JavaVMAttachArgs args = { + JNI_VERSION_1_6, + "ActivityRecognition HAL callback.", + NULL /* group */ + }; + + jint result = java_vm->AttachCurrentThread(env, &args); + if (result != JNI_OK) { + ALOGE("Attach to callback thread failed: %d", result); + } + + return result; +} + +static jint detach_thread() { + JavaVM* java_vm = android::AndroidRuntime::getJavaVM(); + assert(java_vm != NULL); + + jint result = java_vm->DetachCurrentThread(); + if (result != JNI_OK) { + ALOGE("Detach of callback thread failed: %d", result); + } + + return result; +} + + +/** + * Handle activity recognition events from HAL. + */ +static void activity_callback( + const activity_recognition_callback_procs_t* procs, + const activity_event_t* events, + int count) { + if (sOnActivityChanged == NULL) { + ALOGE("Dropping activity_callback because onActivityChanged handler is null."); + return; + } + + if (events == NULL || count <= 0) { + ALOGE("Invalid activity_callback. Count: %d, Events: %p", count, events); + return; + } + + JNIEnv* env = NULL; + int result = attach_thread(&env); + if (result != JNI_OK) { + return; + } + + jclass event_class = + env->FindClass("android/hardware/location/ActivityRecognitionHardware$Event"); + jmethodID event_ctor = env->GetMethodID(event_class, "<init>", "()V"); + jfieldID activity_field = env->GetFieldID(event_class, "activity", "I"); + jfieldID type_field = env->GetFieldID(event_class, "type", "I"); + jfieldID timestamp_field = env->GetFieldID(event_class, "timestamp", "J"); + + jobjectArray events_array = env->NewObjectArray(count, event_class, NULL); + for (int i = 0; i < count; ++i) { + const activity_event_t* event = &events[i]; + jobject event_object = env->NewObject(event_class, event_ctor); + env->SetIntField(event_object, activity_field, event->activity); + env->SetIntField(event_object, type_field, event->event_type); + env->SetLongField(event_object, timestamp_field, event->timestamp); + env->SetObjectArrayElement(events_array, i, event_object); + env->DeleteLocalRef(event_object); + } + + env->CallVoidMethod(sCallbacksObject, sOnActivityChanged, events_array); + check_and_clear_exceptions(env, __FUNCTION__); + + // TODO: ideally we'd let the HAL register the callback thread only once + detach_thread(); +} + +activity_recognition_callback_procs_t sCallbacks { + activity_callback, +}; + +/** + * Initializes the ActivityRecognitionHardware class from the native side. + */ +static void class_init(JNIEnv* env, jclass clazz) { + // open the hardware module + int error = hw_get_module( + ACTIVITY_RECOGNITION_HARDWARE_MODULE_ID, + (const hw_module_t**) &sModule); + if (error != 0) { + ALOGE("Error hw_get_module: %d", error); + return; + } + + error = activity_recognition_open(&sModule->common, &sDevice); + if (error != 0) { + ALOGE("Error opening device: %d", error); + return; + } + + // get references to the Java provided methods + sOnActivityChanged = env->GetMethodID( + clazz, + "onActivityChanged", + "([Landroid/hardware/location/ActivityRecognitionHardware$Event;)V"); + if (sOnActivityChanged == NULL) { + ALOGE("Error obtaining ActivityChanged callback."); + return; + } + + // register callbacks + sDevice->register_activity_callback(sDevice, &sCallbacks); +} + +/** + * Initializes and connect the callbacks handlers in the HAL. + */ +static void initialize(JNIEnv* env, jobject obj) { + if (sCallbacksObject == NULL) { + sCallbacksObject = env->NewGlobalRef(obj); + } else { + ALOGD("Callbacks Object was already initialized."); + } + + if (sDevice != NULL) { + sDevice->register_activity_callback(sDevice, &sCallbacks); + } else { + ALOGD("ActivityRecognition device not found during initialization."); + } +} + +/** + * De-initializes the ActivityRecognitionHardware from the native side. + */ +static void release(JNIEnv* env, jobject obj) { + if (sDevice == NULL) { + return; + } + + int error = activity_recognition_close(sDevice); + if (error != 0) { + ALOGE("Error closing device: %d", error); + return; + } +} + +/** + * Returns true if ActivityRecognition HAL is supported, false otherwise. + */ +static jboolean is_supported(JNIEnv* env, jclass clazz) { + if (sModule != NULL && sDevice != NULL ) { + return JNI_TRUE; + } + return JNI_FALSE; +} + +/** + * Gets an array representing the supported activities. + */ +static jobjectArray get_supported_activities(JNIEnv* env, jobject obj) { + if (sModule == NULL) { + return NULL; + } + + char const* const* list = NULL; + int list_size = sModule->get_supported_activities_list(sModule, &list); + if (list_size <= 0 || list == NULL) { + return NULL; + } + + jclass string_class = env->FindClass("java/lang/String;"); + if (string_class == NULL) { + ALOGE("Unable to find String class for supported activities."); + return NULL; + } + + jobjectArray string_array = env->NewObjectArray(list_size, string_class, NULL); + if (string_array == NULL) { + ALOGE("Unable to create string array for supported activities."); + return NULL; + } + + for (int i = 0; i < list_size; ++i) { + const char* string_ptr = const_cast<const char*>(list[i]); + jsize string_length = strlen(string_ptr); + jstring string = env->NewString((const jchar*) string_ptr, string_length); + env->SetObjectArrayElement(string_array, i, string); + + // log debugging information in case we need to try to trace issues with the strings + if (string_length) { + ALOGD("Invalid activity (index=%d) name size: %d", i, string_length); + } + } + + return string_array; +} + +/** + * Enables a given activity event to be actively monitored. + */ +static int enable_activity_event( + JNIEnv* env, + jobject obj, + jint activity_handle, + jint event_type, + jlong report_latency_ns) { + return sDevice->enable_activity_event( + sDevice, + (uint32_t) activity_handle, + (uint32_t) event_type, + report_latency_ns); +} + +/** + * Disables a given activity event from being actively monitored. + */ +static int disable_activity_event( + JNIEnv* env, + jobject obj, + jint activity_handle, + jint event_type) { + return sDevice->disable_activity_event( + sDevice, + (uint32_t) activity_handle, + (uint32_t) event_type); +} + +/** + * Request flush for al batch buffers. + */ +static int flush(JNIEnv* env, jobject obj) { + return sDevice->flush(sDevice); +} + + +static JNINativeMethod sMethods[] = { + // {"name", "signature", (void*) functionPointer }, + { "nativeClassInit", "()V", (void*) class_init }, + { "nativeInitialize", "()V", (void*) initialize }, + { "nativeRelease", "()V", (void*) release }, + { "nativeIsSupported", "()Z", (void*) is_supported }, + { "nativeGetSupportedActivities", "()[Ljava/lang/String;", (void*) get_supported_activities }, + { "nativeEnableActivityEvent", "(IIJ)I", (void*) enable_activity_event }, + { "nativeDisableActivityEvent", "(II)I", (void*) disable_activity_event }, + { "nativeFlush", "()I", (void*) flush }, +}; + +/** + * Registration method invoked in JNI load. + */ +int register_android_hardware_location_ActivityRecognitionHardware(JNIEnv* env) { + return jniRegisterNativeMethods( + env, + "android/hardware/location/ActivityRecognitionHardware", + sMethods, + NELEM(sMethods)); +} diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index d67d5b3..00f49a1 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -835,6 +835,19 @@ config_enableGeofenceOverlay is false. --> <string name="config_geofenceProviderPackageName" translatable="false">@null</string> + <!-- Whether to enable Hardware Activity-Recognition overlay which allows Hardware + Activity-Recognition to be replaced by an app at run-time. When disabled, only the + config_activityRecognitionHardwarePackageName package will be searched for + its implementation, otherwise packages whose signature matches the + signatures of config_locationProviderPackageNames will be searched, and + the service with the highest version number will be picked. Anyone who + wants to disable the overlay mechanism can set it to false. + --> + <bool name="config_enableActivityRecognitionHardwareOverlay" translatable="false">true</bool> + <!-- Package name providing Hardware Activity-Recognition API support. Used only when + config_enableActivityRecognitionHardwareOverlay is false. --> + <string name="config_activityRecognitionHardwarePackageName" translatable="false">@null</string> + <!-- Package name(s) containing location provider support. These packages can contain services implementing location providers, such as the Geocode Provider, Network Location Provider, and diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 963be2e..08b1836 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -1478,6 +1478,7 @@ <java-symbol type="bool" name="config_useAttentionLight" /> <java-symbol type="bool" name="config_animateScreenLights" /> <java-symbol type="bool" name="config_automatic_brightness_available" /> + <java-symbol type="bool" name="config_enableActivityRecognitionHardwareOverlay" /> <java-symbol type="bool" name="config_enableFusedLocationOverlay" /> <java-symbol type="bool" name="config_enableHardwareFlpOverlay" /> <java-symbol type="bool" name="config_enableGeocoderOverlay" /> @@ -1570,6 +1571,7 @@ <java-symbol type="string" name="car_mode_disable_notification_title" /> <java-symbol type="string" name="chooser_wallpaper" /> <java-symbol type="string" name="config_datause_iface" /> + <java-symbol type="string" name="config_activityRecognitionHardwarePackageName" /> <java-symbol type="string" name="config_fusedLocationProviderPackageName" /> <java-symbol type="string" name="config_hardwareFlpPackageName" /> <java-symbol type="string" name="config_geocoderProviderPackageName" /> diff --git a/location/lib/java/com/android/location/provider/ActivityChangedEvent.java b/location/lib/java/com/android/location/provider/ActivityChangedEvent.java new file mode 100644 index 0000000..8707a10 --- /dev/null +++ b/location/lib/java/com/android/location/provider/ActivityChangedEvent.java @@ -0,0 +1,43 @@ +/* + * 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 com.android.location.provider; + +import android.annotation.NonNull; + +import java.security.InvalidParameterException; +import java.util.List; + +/** + * A class representing an event for Activity changes. + */ +public class ActivityChangedEvent { + private final List<ActivityRecognitionEvent> mActivityRecognitionEvents; + + public ActivityChangedEvent(List<ActivityRecognitionEvent> activityRecognitionEvents) { + if (activityRecognitionEvents == null) { + throw new InvalidParameterException( + "Parameter 'activityRecognitionEvents' must not be null."); + } + + mActivityRecognitionEvents = activityRecognitionEvents; + } + + @NonNull + public Iterable<ActivityRecognitionEvent> getActivityRecognitionEvents() { + return mActivityRecognitionEvents; + } +} diff --git a/location/lib/java/com/android/location/provider/ActivityRecognitionEvent.java b/location/lib/java/com/android/location/provider/ActivityRecognitionEvent.java new file mode 100644 index 0000000..8c719ce --- /dev/null +++ b/location/lib/java/com/android/location/provider/ActivityRecognitionEvent.java @@ -0,0 +1,44 @@ +/* + * 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 com.android.location.provider; + +/** + * A class that represents an Activity Recognition Event. + */ +public class ActivityRecognitionEvent { + private final String mActivity; + private final int mEventType; + private final long mTimestampNs; + + public ActivityRecognitionEvent(String activity, int eventType, long timestampNs) { + mActivity = activity; + mEventType = eventType; + mTimestampNs = timestampNs; + } + + public String getActivity() { + return mActivity; + } + + public int getEventType() { + return mEventType; + } + + public long getTimestampNs() { + return mTimestampNs; + } +} diff --git a/location/lib/java/com/android/location/provider/ActivityRecognitionProvider.java b/location/lib/java/com/android/location/provider/ActivityRecognitionProvider.java new file mode 100644 index 0000000..da33464 --- /dev/null +++ b/location/lib/java/com/android/location/provider/ActivityRecognitionProvider.java @@ -0,0 +1,134 @@ +/* + * 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 com.android.location.provider; + +import com.android.internal.util.Preconditions; + +import android.hardware.location.IActivityRecognitionHardware; +import android.hardware.location.IActivityRecognitionHardwareSink; +import android.os.RemoteException; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; + +/** + * A class that exposes {@link IActivityRecognitionHardware} functionality to unbundled services. + */ +public final class ActivityRecognitionProvider { + private final IActivityRecognitionHardware mService; + private final HashSet<Sink> mSinkSet = new HashSet<Sink>(); + private final SinkTransport mSinkTransport = new SinkTransport(); + + // the following constants must remain in sync with activity_recognition.h + + public static final String ACTIVITY_IN_VEHICLE = "android.activity_recognition.in_vehicle"; + public static final String ACTIVITY_ON_BICYCLE = "android.activity_recognition.on_bicycle"; + public static final String ACTIVITY_WALKING = "android.activity_recognition.walking"; + public static final String ACTIVITY_RUNNING = "android.activity_recognition.running"; + public static final String ACTIVITY_STILL = "android.activity_recognition.still"; + public static final String ACTIVITY_TILTING = "android.activity_recognition.tilting"; + + public static final int EVENT_TYPE_FLUSH_COMPLETE = 0; + public static final int EVENT_TYPE_ENTER = 1; + public static final int EVENT_TYPE_EXIT = 2; + + // end constants activity_recognition.h + + /** + * Used to receive Activity-Recognition events. + */ + public interface Sink { + void onActivityChanged(ActivityChangedEvent event); + } + + public ActivityRecognitionProvider(IActivityRecognitionHardware service) + throws RemoteException { + Preconditions.checkNotNull(service); + mService = service; + mService.registerSink(mSinkTransport); + } + + public String[] getSupportedActivities() throws RemoteException { + return mService.getSupportedActivities(); + } + + public boolean isActivitySupported(String activity) throws RemoteException { + return mService.isActivitySupported(activity); + } + + public void registerSink(Sink sink) { + Preconditions.checkNotNull(sink); + synchronized (mSinkSet) { + mSinkSet.add(sink); + } + } + + // TODO: if this functionality is exposed to 3rd party developers, handle unregistration (here + // and in the service) of all sinks while failing to disable all events + public void unregisterSink(Sink sink) { + Preconditions.checkNotNull(sink); + synchronized (mSinkSet) { + mSinkSet.remove(sink); + } + } + + public boolean enableActivityEvent(String activity, int eventType, long reportLatencyNs) + throws RemoteException { + return mService.enableActivityEvent(activity, eventType, reportLatencyNs); + } + + public boolean disableActivityEvent(String activity, int eventType) throws RemoteException { + return mService.disableActivityEvent(activity, eventType); + } + + public boolean flush() throws RemoteException { + return mService.flush(); + } + + private final class SinkTransport extends IActivityRecognitionHardwareSink.Stub { + @Override + public void onActivityChanged( + android.hardware.location.ActivityChangedEvent activityChangedEvent) { + Collection<Sink> sinks; + synchronized (mSinkSet) { + if (mSinkSet.isEmpty()) { + return; + } + + sinks = new ArrayList<Sink>(mSinkSet); + } + + // translate the event from platform internal and GmsCore types + ArrayList<ActivityRecognitionEvent> gmsEvents = + new ArrayList<ActivityRecognitionEvent>(); + for (android.hardware.location.ActivityRecognitionEvent event + : activityChangedEvent.getActivityRecognitionEvents()) { + ActivityRecognitionEvent gmsEvent = new ActivityRecognitionEvent( + event.getActivity(), + event.getEventType(), + event.getTimestampNs()); + gmsEvents.add(gmsEvent); + } + ActivityChangedEvent gmsEvent = new ActivityChangedEvent(gmsEvents); + + for (Sink sink : sinks) { + sink.onActivityChanged(gmsEvent); + } + } + } +} diff --git a/location/lib/java/com/android/location/provider/ActivityRecognitionProviderWatcher.java b/location/lib/java/com/android/location/provider/ActivityRecognitionProviderWatcher.java new file mode 100644 index 0000000..03dd042 --- /dev/null +++ b/location/lib/java/com/android/location/provider/ActivityRecognitionProviderWatcher.java @@ -0,0 +1,86 @@ +/* + * 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 com.android.location.provider; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.hardware.location.IActivityRecognitionHardware; +import android.hardware.location.IActivityRecognitionHardwareWatcher; +import android.os.Binder; +import android.os.IBinder; +import android.os.Process; +import android.os.RemoteException; +import android.util.Log; + +/** + * A watcher class for Activity-Recognition instances. + */ +public class ActivityRecognitionProviderWatcher { + private static final String TAG = "ActivityRecognitionProviderWatcher"; + + private static ActivityRecognitionProviderWatcher sWatcher; + private static final Object sWatcherLock = new Object(); + + private ActivityRecognitionProvider mActivityRecognitionProvider; + + private ActivityRecognitionProviderWatcher() {} + + public static ActivityRecognitionProviderWatcher getInstance() { + synchronized (sWatcherLock) { + if (sWatcher == null) { + sWatcher = new ActivityRecognitionProviderWatcher(); + } + return sWatcher; + } + } + + private IActivityRecognitionHardwareWatcher.Stub mWatcherStub = + new IActivityRecognitionHardwareWatcher.Stub() { + @Override + public void onInstanceChanged(IActivityRecognitionHardware instance) { + int callingUid = Binder.getCallingUid(); + if (callingUid != Process.SYSTEM_UID) { + Log.d(TAG, "Ignoring calls from non-system server. Uid: " + callingUid); + return; + } + + try { + mActivityRecognitionProvider = new ActivityRecognitionProvider(instance); + } catch (RemoteException e) { + Log.e(TAG, "Error creating Hardware Activity-Recognition", e); + } + } + }; + + /** + * Gets the binder needed to interact with proxy provider in the platform. + */ + @NonNull + public IBinder getBinder() { + return mWatcherStub; + } + + /** + * Gets an object that supports the functionality of {@link ActivityRecognitionProvider}. + * + * @return Non-null value if the functionality is supported by the platform, false otherwise. + */ + @Nullable + public ActivityRecognitionProvider getActivityRecognitionProvider() { + return mActivityRecognitionProvider; + } +} diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java index 67e58a6..bae2d22 100644 --- a/services/core/java/com/android/server/LocationManagerService.java +++ b/services/core/java/com/android/server/LocationManagerService.java @@ -20,6 +20,7 @@ import com.android.internal.content.PackageMonitor; import com.android.internal.location.ProviderProperties; import com.android.internal.location.ProviderRequest; import com.android.internal.os.BackgroundThread; +import com.android.server.location.ActivityRecognitionProxy; import com.android.server.location.FlpHardwareProvider; import com.android.server.location.FusedProxy; import com.android.server.location.GeocoderProxy; @@ -53,6 +54,7 @@ import android.content.pm.Signature; import android.content.pm.UserInfo; import android.content.res.Resources; import android.database.ContentObserver; +import android.hardware.location.ActivityRecognitionHardware; import android.location.Address; import android.location.Criteria; import android.location.GeocoderParams; @@ -475,7 +477,7 @@ public class LocationManagerService extends ILocationManager.Stub { Slog.e(TAG, "no geocoder provider found"); } - // bind to fused provider if supported + // bind to fused hardware provider if supported if (FlpHardwareProvider.isSupported()) { FlpHardwareProvider flpHardwareProvider = FlpHardwareProvider.getInstance(mContext); @@ -505,6 +507,23 @@ public class LocationManagerService extends ILocationManager.Stub { Slog.e(TAG, "FLP HAL not supported."); } + // bind to the hardware activity recognition if supported + if (ActivityRecognitionHardware.isSupported()) { + ActivityRecognitionProxy proxy = ActivityRecognitionProxy.createAndBind( + mContext, + mLocationHandler, + ActivityRecognitionHardware.getInstance(mContext), + com.android.internal.R.bool.config_enableActivityRecognitionHardwareOverlay, + com.android.internal.R.string.config_activityRecognitionHardwarePackageName, + com.android.internal.R.array.config_locationProviderPackageNames); + + if (proxy == null) { + Slog.e(TAG, "Unable to bind ActivityRecognitionProxy."); + } + } else { + Slog.e(TAG, "Hardware Activity-Recognition not supported."); + } + String[] testProviderStrings = resources.getStringArray( com.android.internal.R.array.config_testLocationProviders); for (String testProviderString : testProviderStrings) { diff --git a/services/core/java/com/android/server/location/ActivityRecognitionProxy.java b/services/core/java/com/android/server/location/ActivityRecognitionProxy.java new file mode 100644 index 0000000..7e7f2e4 --- /dev/null +++ b/services/core/java/com/android/server/location/ActivityRecognitionProxy.java @@ -0,0 +1,111 @@ +/* + * 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 com.android.server.location; + +import com.android.server.ServiceWatcher; + +import android.content.Context; +import android.hardware.location.ActivityRecognitionHardware; +import android.hardware.location.IActivityRecognitionHardwareWatcher; +import android.os.Handler; +import android.os.RemoteException; +import android.util.Log; + +/** + * Proxy class to bind GmsCore to the ActivityRecognitionHardware. + * + * @hide + */ +public class ActivityRecognitionProxy { + private final String TAG = "ActivityRecognitionProxy"; + private final ServiceWatcher mServiceWatcher; + private final ActivityRecognitionHardware mActivityRecognitionHardware; + + private ActivityRecognitionProxy( + Context context, + Handler handler, + ActivityRecognitionHardware activityRecognitionHardware, + int overlaySwitchResId, + int defaultServicePackageNameResId, + int initialPackageNameResId) { + mActivityRecognitionHardware = activityRecognitionHardware; + + Runnable newServiceWork = new Runnable() { + @Override + public void run() { + bindProvider(mActivityRecognitionHardware); + } + }; + + // prepare the connection to the provider + mServiceWatcher = new ServiceWatcher( + context, + TAG, + "com.android.location.service.ActivityRecognitionProvider", + overlaySwitchResId, + defaultServicePackageNameResId, + initialPackageNameResId, + newServiceWork, + handler); + } + + /** + * Creates an instance of the proxy and binds it to the appropriate FusedProvider. + * + * @return An instance of the proxy if it could be bound, null otherwise. + */ + public static ActivityRecognitionProxy createAndBind( + Context context, + Handler handler, + ActivityRecognitionHardware activityRecognitionHardware, + int overlaySwitchResId, + int defaultServicePackageNameResId, + int initialPackageNameResId) { + ActivityRecognitionProxy activityRecognitionProxy = new ActivityRecognitionProxy( + context, + handler, + activityRecognitionHardware, + overlaySwitchResId, + defaultServicePackageNameResId, + initialPackageNameResId); + + // try to bind the provider + if (!activityRecognitionProxy.mServiceWatcher.start()) { + return null; + } + + return activityRecognitionProxy; + } + + /** + * Helper function to bind the FusedLocationHardware to the appropriate FusedProvider instance. + */ + private void bindProvider(ActivityRecognitionHardware activityRecognitionHardware) { + IActivityRecognitionHardwareWatcher watcher = + IActivityRecognitionHardwareWatcher.Stub.asInterface(mServiceWatcher.getBinder()); + if (watcher == null) { + Log.e(TAG, "No provider instance found on connection."); + return; + } + + try { + watcher.onInstanceChanged(mActivityRecognitionHardware); + } catch (RemoteException e) { + Log.e(TAG, "Error delivering hardware interface.", e); + } + } +} |