Add capability callback for FLP HAL.

Let HAL implementation tell if geofencing/batching is
supported and which technologies (GNNS, wifi, etc)
can be used.

Still todo: Add ability for GmsCore geofencing to
tell which technologies are supported (instead of
just using it to update monitoring).  This requires
SystemApi change + approval so will do in separate CL.

Note that the classes in the lib are not copied
directly into GmsCore.  The instance will always
be whatever is in the platform.  This is why
the callback is backwards compatible as long as
their is a default implementation (but not if
it's abstract).

Change-Id: I7d6adeb049b89935bc4443785df5d7ef4c730e5d
diff --git a/core/java/android/hardware/location/GeofenceHardwareImpl.java b/core/java/android/hardware/location/GeofenceHardwareImpl.java
index 4696b2a..5d40e94 100644
--- a/core/java/android/hardware/location/GeofenceHardwareImpl.java
+++ b/core/java/android/hardware/location/GeofenceHardwareImpl.java
@@ -53,6 +53,7 @@
 
     private IFusedGeofenceHardware mFusedService;
     private IGpsGeofenceHardware mGpsService;
+    private int mCapabilities;
 
     private int[] mSupportedMonitorTypes = new int[GeofenceHardware.NUM_MONITORS];
 
@@ -89,6 +90,9 @@
     private static final int RESOLUTION_LEVEL_COARSE = 2;
     private static final int RESOLUTION_LEVEL_FINE = 3;
 
+    // Capability constant corresponding to fused_location.h entry when geofencing supports GNNS.
+    private static final int CAPABILITY_GNSS = 1;
+
     public synchronized static GeofenceHardwareImpl getInstance(Context context) {
         if (sInstance == null) {
             sInstance = new GeofenceHardwareImpl(context);
@@ -141,7 +145,9 @@
     private void updateFusedHardwareAvailability() {
         boolean fusedSupported;
         try {
-            fusedSupported = (mFusedService != null ? mFusedService.isSupported() : false);
+            fusedSupported = (mFusedService != null
+                    ? mFusedService.isSupported() && (mCapabilities & CAPABILITY_GNSS) != 0
+                    : false);
         } catch (RemoteException e) {
             Log.e(TAG, "RemoteException calling LocationManagerService");
             fusedSupported = false;
@@ -166,6 +172,11 @@
         }
     }
 
+    public void onCapabilities(int capabilities) {
+        mCapabilities = capabilities;
+        updateFusedHardwareAvailability();
+    }
+
     public void setFusedGeofenceHardware(IFusedGeofenceHardware service) {
         if(mFusedService == null) {
             mFusedService = service;
@@ -212,6 +223,20 @@
         }
     }
 
+    public int getCapabilitiesForMonitoringType(int monitoringType) {
+        switch (mSupportedMonitorTypes[monitoringType]) {
+            case GeofenceHardware.MONITOR_CURRENTLY_AVAILABLE:
+                switch (monitoringType) {
+                    case GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE:
+                        return CAPABILITY_GNSS;
+                    case GeofenceHardware.MONITORING_TYPE_FUSED_HARDWARE:
+                        return mCapabilities;
+                }
+                break;
+        }
+        return 0;
+    }
+
     public boolean addCircularFence(
             int monitoringType,
             GeofenceHardwareRequestParcelable request,
diff --git a/core/java/android/hardware/location/GeofenceHardwareService.java b/core/java/android/hardware/location/GeofenceHardwareService.java
index 4816c5f..c0bcb27 100644
--- a/core/java/android/hardware/location/GeofenceHardwareService.java
+++ b/core/java/android/hardware/location/GeofenceHardwareService.java
@@ -65,14 +65,17 @@
     }
 
     private IBinder mBinder = new IGeofenceHardware.Stub() {
+        @Override
         public void setGpsGeofenceHardware(IGpsGeofenceHardware service) {
             mGeofenceHardwareImpl.setGpsHardwareGeofence(service);
         }
 
+        @Override
         public void setFusedGeofenceHardware(IFusedGeofenceHardware service) {
             mGeofenceHardwareImpl.setFusedGeofenceHardware(service);
         }
 
+        @Override
         public int[] getMonitoringTypes() {
             mContext.enforceCallingPermission(Manifest.permission.LOCATION_HARDWARE,
                     "Location Hardware permission not granted to access hardware geofence");
@@ -80,12 +83,15 @@
             return mGeofenceHardwareImpl.getMonitoringTypes();
         }
 
+        @Override
         public int getStatusOfMonitoringType(int monitoringType) {
             mContext.enforceCallingPermission(Manifest.permission.LOCATION_HARDWARE,
                     "Location Hardware permission not granted to access hardware geofence");
 
             return mGeofenceHardwareImpl.getStatusOfMonitoringType(monitoringType);
         }
+
+        @Override
         public boolean addCircularFence(
                 int monitoringType,
                 GeofenceHardwareRequestParcelable request,
@@ -96,6 +102,7 @@
             return mGeofenceHardwareImpl.addCircularFence(monitoringType, request, callback);
         }
 
+        @Override
         public boolean removeGeofence(int id, int monitoringType) {
             mContext.enforceCallingPermission(Manifest.permission.LOCATION_HARDWARE,
                     "Location Hardware permission not granted to access hardware geofence");
@@ -104,6 +111,7 @@
             return mGeofenceHardwareImpl.removeGeofence(id, monitoringType);
         }
 
+        @Override
         public boolean pauseGeofence(int id, int monitoringType) {
             mContext.enforceCallingPermission(Manifest.permission.LOCATION_HARDWARE,
                     "Location Hardware permission not granted to access hardware geofence");
@@ -112,6 +120,7 @@
             return mGeofenceHardwareImpl.pauseGeofence(id, monitoringType);
         }
 
+        @Override
         public boolean resumeGeofence(int id, int monitoringType, int monitorTransitions) {
             mContext.enforceCallingPermission(Manifest.permission.LOCATION_HARDWARE,
                     "Location Hardware permission not granted to access hardware geofence");
@@ -120,6 +129,7 @@
             return mGeofenceHardwareImpl.resumeGeofence(id, monitoringType, monitorTransitions);
         }
 
+        @Override
         public boolean registerForMonitorStateChangeCallback(int monitoringType,
                 IGeofenceHardwareMonitorCallback callback) {
             mContext.enforceCallingPermission(Manifest.permission.LOCATION_HARDWARE,
@@ -130,6 +140,7 @@
                     callback);
         }
 
+        @Override
         public boolean unregisterForMonitorStateChangeCallback(int monitoringType,
                 IGeofenceHardwareMonitorCallback callback) {
             mContext.enforceCallingPermission(Manifest.permission.LOCATION_HARDWARE,
diff --git a/core/java/android/hardware/location/IFusedLocationHardwareSink.aidl b/core/java/android/hardware/location/IFusedLocationHardwareSink.aidl
index a11d8ab..2107ae8 100644
--- a/core/java/android/hardware/location/IFusedLocationHardwareSink.aidl
+++ b/core/java/android/hardware/location/IFusedLocationHardwareSink.aidl
@@ -30,12 +30,18 @@
      *
      * @param locations     The batch of location information available.
      */
-    void onLocationAvailable(in Location[] locations);
+    void onLocationAvailable(in Location[] locations) = 0;
 
     /**
      * Event generated from FLP HAL to provide diagnostic data to the platform.
      *
      * @param data      The diagnostic data provided by FLP HAL.
      */
-    void onDiagnosticDataAvailable(in String data);
+    void onDiagnosticDataAvailable(in String data) = 1;
+
+    /**
+     * Event generated from FLP HAL to provide a mask of supported
+     * capabilities.  Should be called immediatly after init.
+     */
+    void onCapabilities(int capabilities) = 2;
 }
\ No newline at end of file
diff --git a/location/lib/java/com/android/location/provider/FusedLocationHardware.java b/location/lib/java/com/android/location/provider/FusedLocationHardware.java
index bc5a8a1..44cb1990 100644
--- a/location/lib/java/com/android/location/provider/FusedLocationHardware.java
+++ b/location/lib/java/com/android/location/provider/FusedLocationHardware.java
@@ -34,7 +34,7 @@
  * Class that exposes IFusedLocationHardware functionality to unbundled services.
  */
 public final class FusedLocationHardware {
-    private final String TAG = "FusedLocationHardware";
+    private static final String TAG = "FusedLocationHardware";
 
     private IFusedLocationHardware mLocationHardware;
 
@@ -52,6 +52,11 @@
         public void onDiagnosticDataAvailable(String data) {
             dispatchDiagnosticData(data);
         }
+
+        @Override
+        public void onCapabilities(int capabilities) {
+            dispatchCapabilities(capabilities);
+        }
     };
 
     /**
@@ -204,6 +209,7 @@
     private class DispatcherHandler extends Handler {
         public static final int DISPATCH_LOCATION = 1;
         public static final int DISPATCH_DIAGNOSTIC_DATA = 2;
+        public static final int DISPATCH_CAPABILITIES = 3;
 
         public DispatcherHandler(Looper looper) {
             super(looper, null /*callback*/ , true /*async*/);
@@ -218,6 +224,10 @@
                     break;
                 case DISPATCH_DIAGNOSTIC_DATA:
                     command.dispatchDiagnosticData();
+                    break;
+                case DISPATCH_CAPABILITIES:
+                    command.dispatchCapabilities();
+                    break;
                 default:
                     Log.e(TAG, "Invalid dispatch message");
                     break;
@@ -229,14 +239,17 @@
         private final FusedLocationHardwareSink mSink;
         private final Location[] mLocations;
         private final String mData;
+        private final int mCapabilities;
 
         public MessageCommand(
                 FusedLocationHardwareSink sink,
                 Location[] locations,
-                String data) {
+                String data,
+                int capabilities) {
             mSink = sink;
             mLocations = locations;
             mData = data;
+            mCapabilities = capabilities;
         }
 
         public void dispatchLocation() {
@@ -246,6 +259,10 @@
         public void dispatchDiagnosticData() {
             mSink.onDiagnosticDataAvailable(mData);
         }
+
+        public void dispatchCapabilities() {
+            mSink.onCapabilities(mCapabilities);
+        }
     }
 
     private void dispatchLocations(Location[] locations) {
@@ -258,7 +275,7 @@
             Message message = Message.obtain(
                     entry.getValue(),
                     DispatcherHandler.DISPATCH_LOCATION,
-                    new MessageCommand(entry.getKey(), locations, null /*data*/));
+                    new MessageCommand(entry.getKey(), locations, null /*data*/, 0));
             message.sendToTarget();
         }
     }
@@ -273,7 +290,22 @@
             Message message = Message.obtain(
                     entry.getValue(),
                     DispatcherHandler.DISPATCH_DIAGNOSTIC_DATA,
-                    new MessageCommand(entry.getKey(), null /*locations*/, data));
+                    new MessageCommand(entry.getKey(), null /*locations*/, data, 0));
+            message.sendToTarget();
+        }
+    }
+
+    private void dispatchCapabilities(int capabilities) {
+        HashMap<FusedLocationHardwareSink, DispatcherHandler> sinks;
+        synchronized(mSinkList) {
+            sinks = mSinkList;
+        }
+
+        for(Map.Entry<FusedLocationHardwareSink, DispatcherHandler> entry : sinks.entrySet()) {
+            Message message = Message.obtain(
+                    entry.getValue(),
+                    DispatcherHandler.DISPATCH_CAPABILITIES,
+                    new MessageCommand(entry.getKey(), null /*locations*/, null, capabilities));
             message.sendToTarget();
         }
     }
diff --git a/location/lib/java/com/android/location/provider/FusedLocationHardwareSink.java b/location/lib/java/com/android/location/provider/FusedLocationHardwareSink.java
index 2c39fa8..aaef773 100644
--- a/location/lib/java/com/android/location/provider/FusedLocationHardwareSink.java
+++ b/location/lib/java/com/android/location/provider/FusedLocationHardwareSink.java
@@ -20,11 +20,34 @@
 
 /**
  * Base class for sinks to interact with FusedLocationHardware.
+ *
+ * <p>Default implementations allow new methods to be added without crashing
+ * clients compiled against an old library version.
  */
-public abstract class FusedLocationHardwareSink {
-    /*
-     * Methods to provide a facade for IFusedLocationHardware
+public class FusedLocationHardwareSink {
+    /**
+     * Called when one or more locations are available from the FLP
+     * HAL.
      */
-    public abstract void onLocationAvailable(Location[] locations);
-    public abstract void onDiagnosticDataAvailable(String data);
+    public void onLocationAvailable(Location[] locations) {
+        // default do nothing
+    }
+
+    /**
+     * Called when diagnostic data is available from the FLP HAL.
+     */
+    public void onDiagnosticDataAvailable(String data) {
+        // default do nothing
+    }
+
+    /**
+     * Called when capabilities are available from the FLP HAL.
+     * Should be called once right after initialization.
+     *
+     * @param capabilities A bitmask of capabilities defined in
+     *                     fused_location.h.
+     */
+    public void onCapabilities(int capabilities) {
+        // default do nothing
+    }
 }
\ No newline at end of file
diff --git a/services/core/java/com/android/server/location/FlpHardwareProvider.java b/services/core/java/com/android/server/location/FlpHardwareProvider.java
index 530ad4b..94a1cd8 100644
--- a/services/core/java/com/android/server/location/FlpHardwareProvider.java
+++ b/services/core/java/com/android/server/location/FlpHardwareProvider.java
@@ -44,6 +44,9 @@
 public class FlpHardwareProvider {
     private GeofenceHardwareImpl mGeofenceHardwareSink = null;
     private IFusedLocationHardwareSink mLocationSink = null;
+    // Capabilities provided by FlpCallbacks
+    private boolean mHaveBatchingCapabilities;
+    private int mBatchingCapabilities;
 
     private static FlpHardwareProvider sSingletonInstance = null;
 
@@ -124,6 +127,33 @@
         }
     }
 
+    private void onBatchingCapabilities(int capabilities) {
+        synchronized (mLocationSinkLock) {
+            mHaveBatchingCapabilities = true;
+            mBatchingCapabilities = capabilities;
+        }
+
+        maybeSendCapabilities();
+    }
+
+    private void maybeSendCapabilities() {
+        IFusedLocationHardwareSink sink;
+        boolean haveBatchingCapabilities;
+        int batchingCapabilities;
+        synchronized (mLocationSinkLock) {
+            sink = mLocationSink;
+            haveBatchingCapabilities = mHaveBatchingCapabilities;
+            batchingCapabilities = mBatchingCapabilities;
+        }
+        try {
+            if (sink != null && haveBatchingCapabilities) {
+                sink.onCapabilities(batchingCapabilities);
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "RemoteException calling onLocationAvailable");
+        }
+    }
+
     // FlpDiagnosticCallbacks members
     private void onDataReport(String data) {
         IFusedLocationHardwareSink sink;
@@ -209,6 +239,10 @@
                 translateToGeofenceHardwareStatus(result));
     }
 
+    private void onGeofencingCapabilities(int capabilities) {
+        getGeofenceHardwareSink().onCapabilities(capabilities);
+    }
+
     /**
      * Private native methods accessing FLP HAL.
      */
@@ -277,6 +311,7 @@
 
                 mLocationSink = eventSink;
             }
+            maybeSendCapabilities();
         }
 
         @Override
diff --git a/services/core/jni/com_android_server_location_FlpHardwareProvider.cpp b/services/core/jni/com_android_server_location_FlpHardwareProvider.cpp
index d5508bc..5b5634b 100644
--- a/services/core/jni/com_android_server_location_FlpHardwareProvider.cpp
+++ b/services/core/jni/com_android_server_location_FlpHardwareProvider.cpp
@@ -33,12 +33,14 @@
 
 static jmethodID sOnLocationReport = NULL;
 static jmethodID sOnDataReport = NULL;
+static jmethodID sOnBatchingCapabilities = NULL;
 static jmethodID sOnGeofenceTransition = NULL;
 static jmethodID sOnGeofenceMonitorStatus = NULL;
 static jmethodID sOnGeofenceAdd = NULL;
 static jmethodID sOnGeofenceRemove = NULL;
 static jmethodID sOnGeofencePause = NULL;
 static jmethodID sOnGeofenceResume = NULL;
+static jmethodID sOnGeofencingCapabilities = NULL;
 
 static const FlpLocationInterface* sFlpInterface = NULL;
 static const FlpDiagnosticInterface* sFlpDiagnosticInterface = NULL;
@@ -85,6 +87,19 @@
   return true;
 }
 
+static void BatchingCapabilitiesCallback(int32_t capabilities) {
+  if(!IsValidCallbackThread()) {
+    return;
+  }
+
+  sCallbackEnv->CallVoidMethod(
+      sCallbacksObj,
+      sOnBatchingCapabilities,
+      capabilities
+      );
+  CheckExceptions(sCallbackEnv, __FUNCTION__);
+}
+
 static int SetThreadEvent(ThreadEvent event) {
   JavaVM* javaVm = AndroidRuntime::getJavaVM();
 
@@ -156,6 +171,14 @@
       "onDataReport",
       "(Ljava/lang/String;)V"
       );
+    sOnBatchingCapabilities = env->GetMethodID(
+        clazz,
+        "onBatchingCapabilities",
+        "(I)V");
+    sOnGeofencingCapabilities = env->GetMethodID(
+            clazz,
+            "onGeofencingCapabilities",
+            "(I)V");
   sOnGeofenceTransition = env->GetMethodID(
       clazz,
       "onGeofenceTransition",
@@ -534,7 +557,8 @@
   LocationCallback,
   AcquireWakelock,
   ReleaseWakelock,
-  SetThreadEvent
+  SetThreadEvent,
+  BatchingCapabilitiesCallback
 };
 
 static void ReportData(char* data, int length) {
@@ -670,6 +694,19 @@
   CheckExceptions(sCallbackEnv, __FUNCTION__);
 }
 
+static void GeofencingCapabilitiesCallback(int32_t capabilities) {
+  if(!IsValidCallbackThread()) {
+    return;
+  }
+
+  sCallbackEnv->CallVoidMethod(
+      sCallbacksObj,
+      sOnGeofencingCapabilities,
+      capabilities
+      );
+  CheckExceptions(sCallbackEnv, __FUNCTION__);
+}
+
 FlpGeofenceCallbacks sFlpGeofenceCallbacks = {
   sizeof(FlpGeofenceCallbacks),
   GeofenceTransitionCallback,
@@ -678,7 +715,8 @@
   GeofenceRemoveCallback,
   GeofencePauseCallback,
   GeofenceResumeCallback,
-  SetThreadEvent
+  SetThreadEvent,
+  GeofencingCapabilitiesCallback
 };
 
 /*