Add support in the platform for Activity Recognition Hardware.

Change-Id: I7c4fff3526583475a5edf1f4ba8fede4e9419ead
diff --git a/Android.mk b/Android.mk
index a595d88..01085eb 100644
--- a/Android.mk
+++ b/Android.mk
@@ -156,6 +156,9 @@
 	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 @@
 	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_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 @@
     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..8c719ce4
--- /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.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.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 @@
             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 @@
           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);
+        }
+    }
+}