Disable AR HW when client binder dies.

Ensure that activities are unregistered from AR HW when the client's
binder dies. This clean up is required to prevent AR HW to keep active
when there are no clients listening for events.

Bug: 19894637
Change-Id: Iccd609cf1d2d4a4453c7a96cb8645b61639c3234
diff --git a/core/java/android/hardware/location/ActivityRecognitionHardware.java b/core/java/android/hardware/location/ActivityRecognitionHardware.java
index 5d3953a..8acd1ff 100644
--- a/core/java/android/hardware/location/ActivityRecognitionHardware.java
+++ b/core/java/android/hardware/location/ActivityRecognitionHardware.java
@@ -30,20 +30,34 @@
  * @hide
  */
 public class ActivityRecognitionHardware extends IActivityRecognitionHardware.Stub {
-    private static final String TAG = "ActivityRecognitionHardware";
+    private static final String TAG = "ActivityRecognitionHW";
+    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
     private static final String HARDWARE_PERMISSION = Manifest.permission.LOCATION_HARDWARE;
+    private static final String ENFORCE_HW_PERMISSION_MESSAGE = "Permission '"
+            + HARDWARE_PERMISSION + "' not granted to access ActivityRecognitionHardware";
+
     private static final int INVALID_ACTIVITY_TYPE = -1;
     private static final int NATIVE_SUCCESS_RESULT = 0;
+    private static final int EVENT_TYPE_DISABLED = 0;
+    private static final int EVENT_TYPE_ENABLED = 1;
 
-    private static ActivityRecognitionHardware sSingletonInstance = null;
+    /**
+     * Contains the number of supported Event Types.
+     *
+     * NOTE: increment this counter every time a new EVENT_TYPE_ is added to
+     *       com.android.location.provider.ActivityRecognitionProvider
+     */
+    private static final int EVENT_TYPE_COUNT = 3;
+
+    private static ActivityRecognitionHardware sSingletonInstance;
     private static final Object sSingletonInstanceLock = new Object();
 
     private final Context mContext;
+    private final int mSupportedActivitiesCount;
     private final String[] mSupportedActivities;
-
-    private final RemoteCallbackList<IActivityRecognitionHardwareSink> mSinks =
-            new RemoteCallbackList<IActivityRecognitionHardwareSink>();
+    private final int[][] mSupportedActivitiesEnabledEvents;
+    private final SinkList mSinks = new SinkList();
 
     private static class Event {
         public int activity;
@@ -56,6 +70,8 @@
 
         mContext = context;
         mSupportedActivities = fetchSupportedActivities();
+        mSupportedActivitiesCount = mSupportedActivities.length;
+        mSupportedActivitiesEnabledEvents = new int[mSupportedActivitiesCount][EVENT_TYPE_COUNT];
     }
 
     public static ActivityRecognitionHardware getInstance(Context context) {
@@ -107,7 +123,11 @@
         }
 
         int result = nativeEnableActivityEvent(activityType, eventType, reportLatencyNs);
-        return result == NATIVE_SUCCESS_RESULT;
+        if (result == NATIVE_SUCCESS_RESULT) {
+            mSupportedActivitiesEnabledEvents[activityType][eventType] = EVENT_TYPE_ENABLED;
+            return true;
+        }
+        return false;
     }
 
     @Override
@@ -120,7 +140,11 @@
         }
 
         int result = nativeDisableActivityEvent(activityType, eventType);
-        return result == NATIVE_SUCCESS_RESULT;
+        if (result == NATIVE_SUCCESS_RESULT) {
+            mSupportedActivitiesEnabledEvents[activityType][eventType] = EVENT_TYPE_DISABLED;
+            return true;
+        }
+        return false;
     }
 
     @Override
@@ -135,7 +159,7 @@
      */
     private void onActivityChanged(Event[] events) {
         if (events == null || events.length == 0) {
-            Log.d(TAG, "No events to broadcast for onActivityChanged.");
+            if (DEBUG) Log.d(TAG, "No events to broadcast for onActivityChanged.");
             return;
         }
 
@@ -161,7 +185,6 @@
             }
         }
         mSinks.finishBroadcast();
-
     }
 
     private String getActivityName(int activityType) {
@@ -193,10 +216,7 @@
     }
 
     private void checkPermissions() {
-        String message = String.format(
-                "Permission '%s' not granted to access ActivityRecognitionHardware",
-                HARDWARE_PERMISSION);
-        mContext.enforceCallingPermission(HARDWARE_PERMISSION, message);
+        mContext.enforceCallingPermission(HARDWARE_PERMISSION, ENFORCE_HW_PERMISSION_MESSAGE);
     }
 
     private String[] fetchSupportedActivities() {
@@ -208,6 +228,39 @@
         return new String[0];
     }
 
+    private class SinkList extends RemoteCallbackList<IActivityRecognitionHardwareSink> {
+        @Override
+        public void onCallbackDied(IActivityRecognitionHardwareSink callback) {
+            int callbackCount = mSinks.getRegisteredCallbackCount();
+            if (DEBUG) Log.d(TAG, "RegisteredCallbackCount: " + callbackCount);
+            if (callbackCount != 0) {
+                return;
+            }
+            // currently there is only one client for this, so if all its sinks have died, we clean
+            // up after them, this ensures that the AR HAL is not out of sink
+            for (int activity = 0; activity < mSupportedActivitiesCount; ++activity) {
+                for (int event = 0; event < EVENT_TYPE_COUNT; ++event) {
+                    disableActivityEventIfEnabled(activity, event);
+                }
+            }
+        }
+
+        private void disableActivityEventIfEnabled(int activityType, int eventType) {
+            if (mSupportedActivitiesEnabledEvents[activityType][eventType] != EVENT_TYPE_ENABLED) {
+                return;
+            }
+
+            int result = nativeDisableActivityEvent(activityType, eventType);
+            mSupportedActivitiesEnabledEvents[activityType][eventType] = EVENT_TYPE_DISABLED;
+            String message = String.format(
+                    "DisableActivityEvent: activityType=%d, eventType=%d, result=%d",
+                    activityType,
+                    eventType,
+                    result);
+            Log.e(TAG, message);
+        }
+    }
+
     // native bindings
     static { nativeClassInit(); }
 
diff --git a/location/lib/java/com/android/location/provider/ActivityRecognitionProvider.java b/location/lib/java/com/android/location/provider/ActivityRecognitionProvider.java
index da33464..25f9fa2 100644
--- a/location/lib/java/com/android/location/provider/ActivityRecognitionProvider.java
+++ b/location/lib/java/com/android/location/provider/ActivityRecognitionProvider.java
@@ -43,6 +43,8 @@
     public static final String ACTIVITY_STILL = "android.activity_recognition.still";
     public static final String ACTIVITY_TILTING = "android.activity_recognition.tilting";
 
+    // NOTE: when adding an additional EVENT_TYPE_, EVENT_TYPE_COUNT needs to be updated in
+    // android.hardware.location.ActivityRecognitionHardware
     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;