Cherry-pick: Add callback-based support for HW Activity Recognition.

Add a callback-based mechanism for GmsCore to connect to Hardware Activity
Recognition. This allows GmsCore to stop polling to identify if the Android
platform supports the functionality or not.

Bug: 17112184
Change-Id: I8f9459cbd15eecd70f6919c6551e6f7a663c732f
diff --git a/Android.mk b/Android.mk
index 2a87fd2..6f5f8f9 100644
--- a/Android.mk
+++ b/Android.mk
@@ -173,6 +173,7 @@
 	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/IActivityRecognitionHardwareClient.aidl \
 	core/java/android/hardware/location/IActivityRecognitionHardwareSink.aidl \
 	core/java/android/hardware/location/IActivityRecognitionHardwareWatcher.aidl \
 	core/java/android/hardware/location/IFusedLocationHardware.aidl \
diff --git a/core/java/android/hardware/location/IActivityRecognitionHardwareClient.aidl b/core/java/android/hardware/location/IActivityRecognitionHardwareClient.aidl
new file mode 100644
index 0000000..d2c3d75
--- /dev/null
+++ b/core/java/android/hardware/location/IActivityRecognitionHardwareClient.aidl
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2015, 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 client interface.
+ * This interface can be used to receive interfaces to implementations of
+ * {@link IActivityRecognitionHardware}.
+ *
+ * @hide
+ */
+interface IActivityRecognitionHardwareClient {
+    /**
+     * Hardware Activity-Recognition availability event.
+     *
+     * @param isSupported whether the platform has hardware support for the feature
+     * @param instance the available instance to provide access to the feature
+     */
+    void onAvailabilityChanged(in boolean isSupported, in IActivityRecognitionHardware instance);
+}
diff --git a/core/java/android/hardware/location/IActivityRecognitionHardwareWatcher.aidl b/core/java/android/hardware/location/IActivityRecognitionHardwareWatcher.aidl
index 0507f52..12e3117 100644
--- a/core/java/android/hardware/location/IActivityRecognitionHardwareWatcher.aidl
+++ b/core/java/android/hardware/location/IActivityRecognitionHardwareWatcher.aidl
@@ -22,6 +22,8 @@
  * Activity Recognition Hardware watcher. This interface can be used to receive interfaces to
  * implementations of {@link IActivityRecognitionHardware}.
  *
+ * @deprecated use {@link IActivityRecognitionHardwareClient} instead.
+
  * @hide
  */
 interface IActivityRecognitionHardwareWatcher {
@@ -29,4 +31,4 @@
      * Hardware Activity-Recognition availability event.
      */
     void onInstanceChanged(in IActivityRecognitionHardware instance);
-}
\ No newline at end of file
+}
diff --git a/location/lib/java/com/android/location/provider/ActivityRecognitionProvider.java b/location/lib/java/com/android/location/provider/ActivityRecognitionProvider.java
index da33464..f4943d5 100644
--- a/location/lib/java/com/android/location/provider/ActivityRecognitionProvider.java
+++ b/location/lib/java/com/android/location/provider/ActivityRecognitionProvider.java
@@ -31,8 +31,7 @@
  */
 public final class ActivityRecognitionProvider {
     private final IActivityRecognitionHardware mService;
-    private final HashSet<Sink> mSinkSet = new HashSet<Sink>();
-    private final SinkTransport mSinkTransport = new SinkTransport();
+    private final HashSet<Sink> mSinkSet = new HashSet<>();
 
     // the following constants must remain in sync with activity_recognition.h
 
@@ -60,7 +59,7 @@
             throws RemoteException {
         Preconditions.checkNotNull(service);
         mService = service;
-        mService.registerSink(mSinkTransport);
+        mService.registerSink(new SinkTransport());
     }
 
     public String[] getSupportedActivities() throws RemoteException {
@@ -102,26 +101,23 @@
 
     private final class SinkTransport extends IActivityRecognitionHardwareSink.Stub {
         @Override
-        public void onActivityChanged(
-                android.hardware.location.ActivityChangedEvent activityChangedEvent) {
+        public void onActivityChanged(android.hardware.location.ActivityChangedEvent event) {
             Collection<Sink> sinks;
             synchronized (mSinkSet) {
                 if (mSinkSet.isEmpty()) {
                     return;
                 }
-
-                sinks = new ArrayList<Sink>(mSinkSet);
+                sinks = new ArrayList<>(mSinkSet);
             }
 
             // translate the event from platform internal and GmsCore types
-            ArrayList<ActivityRecognitionEvent> gmsEvents =
-                    new ArrayList<ActivityRecognitionEvent>();
-            for (android.hardware.location.ActivityRecognitionEvent event
-                    : activityChangedEvent.getActivityRecognitionEvents()) {
+            ArrayList<ActivityRecognitionEvent> gmsEvents = new ArrayList<>();
+            for (android.hardware.location.ActivityRecognitionEvent reportingEvent
+                    : event.getActivityRecognitionEvents()) {
                 ActivityRecognitionEvent gmsEvent = new ActivityRecognitionEvent(
-                        event.getActivity(),
-                        event.getEventType(),
-                        event.getTimestampNs());
+                        reportingEvent.getActivity(),
+                        reportingEvent.getEventType(),
+                        reportingEvent.getTimestampNs());
                 gmsEvents.add(gmsEvent);
             }
             ActivityChangedEvent gmsEvent = new ActivityChangedEvent(gmsEvents);
diff --git a/location/lib/java/com/android/location/provider/ActivityRecognitionProviderClient.java b/location/lib/java/com/android/location/provider/ActivityRecognitionProviderClient.java
new file mode 100644
index 0000000..0b878d7
--- /dev/null
+++ b/location/lib/java/com/android/location/provider/ActivityRecognitionProviderClient.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2015 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.hardware.location.IActivityRecognitionHardware;
+import android.hardware.location.IActivityRecognitionHardwareClient;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.Process;
+import android.os.RemoteException;
+import android.util.Log;
+
+/**
+ * A client class for interaction with an Activity-Recognition provider.
+ */
+public abstract class ActivityRecognitionProviderClient {
+    private static final String TAG = "ArProviderClient";
+
+    protected ActivityRecognitionProviderClient() {}
+
+    private IActivityRecognitionHardwareClient.Stub mClient =
+            new IActivityRecognitionHardwareClient.Stub() {
+                @Override
+                public void onAvailabilityChanged(
+                        boolean isSupported,
+                        IActivityRecognitionHardware instance) {
+                    int callingUid = Binder.getCallingUid();
+                    if (callingUid != Process.SYSTEM_UID) {
+                        Log.d(TAG, "Ignoring calls from non-system server. Uid: " + callingUid);
+                        return;
+                    }
+                    ActivityRecognitionProvider provider;
+                    try {
+                        provider = isSupported ? new ActivityRecognitionProvider(instance) : null;
+                    } catch (RemoteException e) {
+                        Log.e(TAG, "Error creating Hardware Activity-Recognition Provider.", e);
+                        return;
+                    }
+                    onProviderChanged(isSupported, provider);
+                }
+            };
+
+    /**
+     * Gets the binder needed to interact with proxy provider in the platform.
+     */
+    @NonNull
+    public IBinder getBinder() {
+        return mClient;
+    }
+
+    /**
+     * Called when a change in the availability of {@link ActivityRecognitionProvider} is detected.
+     *
+     * @param isSupported whether the platform supports the provider natively
+     * @param instance the available provider's instance
+     */
+    public abstract void onProviderChanged(
+            boolean isSupported,
+            ActivityRecognitionProvider instance);
+}
diff --git a/location/lib/java/com/android/location/provider/ActivityRecognitionProviderWatcher.java b/location/lib/java/com/android/location/provider/ActivityRecognitionProviderWatcher.java
index 03dd042..7139025 100644
--- a/location/lib/java/com/android/location/provider/ActivityRecognitionProviderWatcher.java
+++ b/location/lib/java/com/android/location/provider/ActivityRecognitionProviderWatcher.java
@@ -28,7 +28,10 @@
 
 /**
  * A watcher class for Activity-Recognition instances.
+ *
+ * @deprecated use {@link ActivityRecognitionProviderClient} instead.
  */
+@Deprecated
 public class ActivityRecognitionProviderWatcher {
     private static final String TAG = "ActivityRecognitionProviderWatcher";
 
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index cae060a..468ead0 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -542,22 +542,25 @@
             Slog.e(TAG,  "Unable to bind FLP Geofence proxy.");
         }
 
-        // 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.");
-            }
+        // bind to hardware activity recognition
+        boolean activityRecognitionHardwareIsSupported = ActivityRecognitionHardware.isSupported();
+        ActivityRecognitionHardware activityRecognitionHardware = null;
+        if (activityRecognitionHardwareIsSupported) {
+            activityRecognitionHardware = ActivityRecognitionHardware.getInstance(mContext);
         } else {
             Slog.e(TAG, "Hardware Activity-Recognition not supported.");
         }
+        ActivityRecognitionProxy proxy = ActivityRecognitionProxy.createAndBind(
+                mContext,
+                mLocationHandler,
+                activityRecognitionHardwareIsSupported,
+                activityRecognitionHardware,
+                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.");
+        }
 
         String[] testProviderStrings = resources.getStringArray(
                 com.android.internal.R.array.config_testLocationProviders);
diff --git a/services/core/java/com/android/server/location/ActivityRecognitionProxy.java b/services/core/java/com/android/server/location/ActivityRecognitionProxy.java
index 607805b..2eb58bf 100644
--- a/services/core/java/com/android/server/location/ActivityRecognitionProxy.java
+++ b/services/core/java/com/android/server/location/ActivityRecognitionProxy.java
@@ -20,8 +20,10 @@
 
 import android.content.Context;
 import android.hardware.location.ActivityRecognitionHardware;
+import android.hardware.location.IActivityRecognitionHardwareClient;
 import android.hardware.location.IActivityRecognitionHardwareWatcher;
 import android.os.Handler;
+import android.os.IBinder;
 import android.os.RemoteException;
 import android.util.Log;
 
@@ -34,21 +36,24 @@
     private static final String TAG = "ActivityRecognitionProxy";
 
     private final ServiceWatcher mServiceWatcher;
-    private final ActivityRecognitionHardware mActivityRecognitionHardware;
+    private final boolean mIsSupported;
+    private final ActivityRecognitionHardware mInstance;
 
     private ActivityRecognitionProxy(
             Context context,
             Handler handler,
+            boolean activityRecognitionHardwareIsSupported,
             ActivityRecognitionHardware activityRecognitionHardware,
             int overlaySwitchResId,
             int defaultServicePackageNameResId,
             int initialPackageNameResId) {
-        mActivityRecognitionHardware = activityRecognitionHardware;
+        mIsSupported = activityRecognitionHardwareIsSupported;
+        mInstance = activityRecognitionHardware;
 
         Runnable newServiceWork = new Runnable() {
             @Override
             public void run() {
-                bindProvider(mActivityRecognitionHardware);
+                bindProvider();
             }
         };
 
@@ -72,6 +77,7 @@
     public static ActivityRecognitionProxy createAndBind(
             Context context,
             Handler handler,
+            boolean activityRecognitionHardwareIsSupported,
             ActivityRecognitionHardware activityRecognitionHardware,
             int overlaySwitchResId,
             int defaultServicePackageNameResId,
@@ -79,6 +85,7 @@
         ActivityRecognitionProxy activityRecognitionProxy = new ActivityRecognitionProxy(
                 context,
                 handler,
+                activityRecognitionHardwareIsSupported,
                 activityRecognitionHardware,
                 overlaySwitchResId,
                 defaultServicePackageNameResId,
@@ -89,25 +96,52 @@
             Log.e(TAG, "ServiceWatcher could not 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.");
+    private void bindProvider() {
+        IBinder binder = mServiceWatcher.getBinder();
+        if (binder == null) {
+            Log.e(TAG, "Null binder found on connection.");
+            return;
+        }
+        String descriptor;
+        try {
+            descriptor = binder.getInterfaceDescriptor();
+        } catch (RemoteException e) {
+            Log.e(TAG, "Unable to get interface descriptor.", e);
             return;
         }
 
-        try {
-            watcher.onInstanceChanged(mActivityRecognitionHardware);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Error delivering hardware interface.", e);
+        if (IActivityRecognitionHardwareWatcher.class.getCanonicalName().equals(descriptor)) {
+            IActivityRecognitionHardwareWatcher watcher =
+                    IActivityRecognitionHardwareWatcher.Stub.asInterface(binder);
+            if (watcher == null) {
+                Log.e(TAG, "No watcher found on connection.");
+                return;
+            }
+            try {
+                watcher.onInstanceChanged(mInstance);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Error delivering hardware interface to watcher.", e);
+            }
+        } else if (IActivityRecognitionHardwareClient.class.getCanonicalName().equals(descriptor)) {
+            IActivityRecognitionHardwareClient client =
+                    IActivityRecognitionHardwareClient.Stub.asInterface(binder);
+            if (client == null) {
+                Log.e(TAG, "No client found on connection.");
+                return;
+            }
+            try {
+                client.onAvailabilityChanged(mIsSupported, mInstance);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Error delivering hardware interface to client.", e);
+            }
+        } else {
+            Log.e(TAG, "Invalid descriptor found on connection: " + descriptor);
         }
     }
 }