1 /* 2 * Copyright (C) 2013 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.location; 18 19 import android.hardware.location.GeofenceHardware; 20 import android.hardware.location.GeofenceHardwareImpl; 21 import android.hardware.location.GeofenceHardwareRequestParcelable; 22 import android.hardware.location.IFusedLocationHardware; 23 import android.hardware.location.IFusedLocationHardwareSink; 24 import android.location.IFusedGeofenceHardware; 25 import android.location.FusedBatchOptions; 26 import android.location.Location; 27 import android.location.LocationListener; 28 import android.location.LocationManager; 29 import android.location.LocationRequest; 30 31 import android.content.Context; 32 import android.os.Bundle; 33 import android.os.Looper; 34 import android.os.RemoteException; 35 import android.os.SystemClock; 36 import android.util.Log; 37 38 /** 39 * This class is an interop layer for JVM types and the JNI code that interacts 40 * with the FLP HAL implementation. 41 * 42 * {@hide} 43 */ 44 public class FlpHardwareProvider { 45 private GeofenceHardwareImpl mGeofenceHardwareSink = null; 46 private IFusedLocationHardwareSink mLocationSink = null; 47 48 private static FlpHardwareProvider sSingletonInstance = null; 49 50 private final static String TAG = "FlpHardwareProvider"; 51 private final Context mContext; 52 private final Object mLocationSinkLock = new Object(); 53 54 // FlpHal result codes, they must be equal to the ones in fused_location.h 55 private static final int FLP_RESULT_SUCCESS = 0; 56 private static final int FLP_RESULT_ERROR = -1; 57 private static final int FLP_RESULT_INSUFFICIENT_MEMORY = -2; 58 private static final int FLP_RESULT_TOO_MANY_GEOFENCES = -3; 59 private static final int FLP_RESULT_ID_EXISTS = -4; 60 private static final int FLP_RESULT_ID_UNKNOWN = -5; 61 private static final int FLP_RESULT_INVALID_GEOFENCE_TRANSITION = -6; 62 63 public static FlpHardwareProvider getInstance(Context context) { 64 if (sSingletonInstance == null) { 65 sSingletonInstance = new FlpHardwareProvider(context); 66 } 67 68 return sSingletonInstance; 69 } 70 71 private FlpHardwareProvider(Context context) { 72 mContext = context; 73 74 // register for listening for passive provider data 75 LocationManager manager = (LocationManager) mContext.getSystemService( 76 Context.LOCATION_SERVICE); 77 final long minTime = 0; 78 final float minDistance = 0; 79 final boolean oneShot = false; 80 LocationRequest request = LocationRequest.createFromDeprecatedProvider( 81 LocationManager.PASSIVE_PROVIDER, 82 minTime, 83 minDistance, 84 oneShot); 85 // Don't keep track of this request since it's done on behalf of other clients 86 // (which are kept track of separately). 87 request.setHideFromAppOps(true); 88 manager.requestLocationUpdates( 89 request, 90 new NetworkLocationListener(), 91 Looper.myLooper()); 92 } 93 94 public static boolean isSupported() { 95 return nativeIsSupported(); 96 } 97 98 /** 99 * Private callback functions used by FLP HAL. 100 */ 101 // FlpCallbacks members 102 private void onLocationReport(Location[] locations) { 103 for (Location location : locations) { 104 location.setProvider(LocationManager.FUSED_PROVIDER); 105 // set the elapsed time-stamp just as GPS provider does 106 location.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos()); 107 } 108 109 IFusedLocationHardwareSink sink; 110 synchronized (mLocationSinkLock) { 111 sink = mLocationSink; 112 } 113 try { 114 if (sink != null) { 115 sink.onLocationAvailable(locations); 116 } 117 } catch (RemoteException e) { 118 Log.e(TAG, "RemoteException calling onLocationAvailable"); 119 } 120 } 121 122 // FlpDiagnosticCallbacks members 123 private void onDataReport(String data) { 124 IFusedLocationHardwareSink sink; 125 synchronized (mLocationSinkLock) { 126 sink = mLocationSink; 127 } 128 try { 129 if (mLocationSink != null) { 130 sink.onDiagnosticDataAvailable(data); 131 } 132 } catch (RemoteException e) { 133 Log.e(TAG, "RemoteException calling onDiagnosticDataAvailable"); 134 } 135 } 136 137 // FlpGeofenceCallbacks members 138 private void onGeofenceTransition( 139 int geofenceId, 140 Location location, 141 int transition, 142 long timestamp, 143 int sourcesUsed) { 144 getGeofenceHardwareSink().reportGeofenceTransition( 145 geofenceId, 146 updateLocationInformation(location), 147 transition, 148 timestamp, 149 GeofenceHardware.MONITORING_TYPE_FUSED_HARDWARE, 150 sourcesUsed); 151 } 152 153 private void onGeofenceMonitorStatus(int status, int source, Location location) { 154 // allow the location to be optional in this event 155 Location updatedLocation = null; 156 if(location != null) { 157 updatedLocation = updateLocationInformation(location); 158 } 159 160 getGeofenceHardwareSink().reportGeofenceMonitorStatus( 161 GeofenceHardware.MONITORING_TYPE_FUSED_HARDWARE, 162 status, 163 updatedLocation, 164 source); 165 } 166 167 private void onGeofenceAdd(int geofenceId, int result) { 168 getGeofenceHardwareSink().reportGeofenceAddStatus( 169 geofenceId, 170 translateToGeofenceHardwareStatus(result)); 171 } 172 173 private void onGeofenceRemove(int geofenceId, int result) { 174 getGeofenceHardwareSink().reportGeofenceRemoveStatus( 175 geofenceId, 176 translateToGeofenceHardwareStatus(result)); 177 } 178 179 private void onGeofencePause(int geofenceId, int result) { 180 getGeofenceHardwareSink().reportGeofencePauseStatus( 181 geofenceId, 182 translateToGeofenceHardwareStatus(result)); 183 } 184 185 private void onGeofenceResume(int geofenceId, int result) { 186 getGeofenceHardwareSink().reportGeofenceResumeStatus( 187 geofenceId, 188 translateToGeofenceHardwareStatus(result)); 189 } 190 191 /** 192 * Private native methods accessing FLP HAL. 193 */ 194 static { nativeClassInit(); } 195 196 // Core members 197 private static native void nativeClassInit(); 198 private static native boolean nativeIsSupported(); 199 200 // FlpLocationInterface members 201 private native void nativeInit(); 202 private native int nativeGetBatchSize(); 203 private native void nativeStartBatching(int requestId, FusedBatchOptions options); 204 private native void nativeUpdateBatchingOptions(int requestId, FusedBatchOptions optionsObject); 205 private native void nativeStopBatching(int id); 206 private native void nativeRequestBatchedLocation(int lastNLocations); 207 private native void nativeInjectLocation(Location location); 208 // TODO [Fix] sort out the lifetime of the instance 209 private native void nativeCleanup(); 210 211 // FlpDiagnosticsInterface members 212 private native boolean nativeIsDiagnosticSupported(); 213 private native void nativeInjectDiagnosticData(String data); 214 215 // FlpDeviceContextInterface members 216 private native boolean nativeIsDeviceContextSupported(); 217 private native void nativeInjectDeviceContext(int deviceEnabledContext); 218 219 // FlpGeofencingInterface members 220 private native boolean nativeIsGeofencingSupported(); 221 private native void nativeAddGeofences( 222 GeofenceHardwareRequestParcelable[] geofenceRequestsArray); 223 private native void nativePauseGeofence(int geofenceId); 224 private native void nativeResumeGeofence(int geofenceId, int monitorTransitions); 225 private native void nativeModifyGeofenceOption( 226 int geofenceId, 227 int lastTransition, 228 int monitorTransitions, 229 int notificationResponsiveness, 230 int unknownTimer, 231 int sourcesToUse); 232 private native void nativeRemoveGeofences(int[] geofenceIdsArray); 233 234 /** 235 * Interface implementations for services built on top of this functionality. 236 */ 237 public static final String LOCATION = "Location"; 238 public static final String GEOFENCING = "Geofencing"; 239 240 public IFusedLocationHardware getLocationHardware() { 241 nativeInit(); 242 return mLocationHardware; 243 } 244 245 public IFusedGeofenceHardware getGeofenceHardware() { 246 nativeInit(); 247 return mGeofenceHardwareService; 248 } 249 250 private final IFusedLocationHardware mLocationHardware = new IFusedLocationHardware.Stub() { 251 @Override 252 public void registerSink(IFusedLocationHardwareSink eventSink) { 253 synchronized (mLocationSinkLock) { 254 // only one sink is allowed at the moment 255 if (mLocationSink != null) { 256 throw new RuntimeException( 257 "IFusedLocationHardware does not support multiple sinks"); 258 } 259 260 mLocationSink = eventSink; 261 } 262 } 263 264 @Override 265 public void unregisterSink(IFusedLocationHardwareSink eventSink) { 266 synchronized (mLocationSinkLock) { 267 // don't throw if the sink is not registered, simply make it a no-op 268 if (mLocationSink == eventSink) { 269 mLocationSink = null; 270 } 271 } 272 } 273 274 @Override 275 public int getSupportedBatchSize() { 276 return nativeGetBatchSize(); 277 } 278 279 @Override 280 public void startBatching(int requestId, FusedBatchOptions options) { 281 nativeStartBatching(requestId, options); 282 } 283 284 @Override 285 public void stopBatching(int requestId) { 286 nativeStopBatching(requestId); 287 } 288 289 @Override 290 public void updateBatchingOptions(int requestId, FusedBatchOptions options) { 291 nativeUpdateBatchingOptions(requestId, options); 292 } 293 294 @Override 295 public void requestBatchOfLocations(int batchSizeRequested) { 296 nativeRequestBatchedLocation(batchSizeRequested); 297 } 298 299 @Override 300 public boolean supportsDiagnosticDataInjection() { 301 return nativeIsDiagnosticSupported(); 302 } 303 304 @Override 305 public void injectDiagnosticData(String data) { 306 nativeInjectDiagnosticData(data); 307 } 308 309 @Override 310 public boolean supportsDeviceContextInjection() { 311 return nativeIsDeviceContextSupported(); 312 } 313 314 @Override 315 public void injectDeviceContext(int deviceEnabledContext) { 316 nativeInjectDeviceContext(deviceEnabledContext); 317 } 318 }; 319 320 private final IFusedGeofenceHardware mGeofenceHardwareService = 321 new IFusedGeofenceHardware.Stub() { 322 @Override 323 public boolean isSupported() { 324 return nativeIsGeofencingSupported(); 325 } 326 327 @Override 328 public void addGeofences(GeofenceHardwareRequestParcelable[] geofenceRequestsArray) { 329 nativeAddGeofences(geofenceRequestsArray); 330 } 331 332 @Override 333 public void removeGeofences(int[] geofenceIds) { 334 nativeRemoveGeofences(geofenceIds); 335 } 336 337 @Override 338 public void pauseMonitoringGeofence(int geofenceId) { 339 nativePauseGeofence(geofenceId); 340 } 341 342 @Override 343 public void resumeMonitoringGeofence(int geofenceId, int monitorTransitions) { 344 nativeResumeGeofence(geofenceId, monitorTransitions); 345 } 346 347 @Override 348 public void modifyGeofenceOptions(int geofenceId, 349 int lastTransition, 350 int monitorTransitions, 351 int notificationResponsiveness, 352 int unknownTimer, 353 int sourcesToUse) { 354 nativeModifyGeofenceOption( 355 geofenceId, 356 lastTransition, 357 monitorTransitions, 358 notificationResponsiveness, 359 unknownTimer, 360 sourcesToUse); 361 } 362 }; 363 364 /** 365 * Internal classes and functions used by the provider. 366 */ 367 private final class NetworkLocationListener implements LocationListener { 368 @Override 369 public void onLocationChanged(Location location) { 370 if ( 371 !LocationManager.NETWORK_PROVIDER.equals(location.getProvider()) || 372 !location.hasAccuracy() 373 ) { 374 return; 375 } 376 377 nativeInjectLocation(location); 378 } 379 380 @Override 381 public void onStatusChanged(String provider, int status, Bundle extras) { } 382 383 @Override 384 public void onProviderEnabled(String provider) { } 385 386 @Override 387 public void onProviderDisabled(String provider) { } 388 } 389 390 private GeofenceHardwareImpl getGeofenceHardwareSink() { 391 if (mGeofenceHardwareSink == null) { 392 mGeofenceHardwareSink = GeofenceHardwareImpl.getInstance(mContext); 393 } 394 395 return mGeofenceHardwareSink; 396 } 397 398 private static int translateToGeofenceHardwareStatus(int flpHalResult) { 399 switch(flpHalResult) { 400 case FLP_RESULT_SUCCESS: 401 return GeofenceHardware.GEOFENCE_SUCCESS; 402 case FLP_RESULT_ERROR: 403 return GeofenceHardware.GEOFENCE_FAILURE; 404 // TODO: uncomment this once the ERROR definition is marked public 405 //case FLP_RESULT_INSUFFICIENT_MEMORY: 406 // return GeofenceHardware.GEOFENCE_ERROR_INSUFFICIENT_MEMORY; 407 case FLP_RESULT_TOO_MANY_GEOFENCES: 408 return GeofenceHardware.GEOFENCE_ERROR_TOO_MANY_GEOFENCES; 409 case FLP_RESULT_ID_EXISTS: 410 return GeofenceHardware.GEOFENCE_ERROR_ID_EXISTS; 411 case FLP_RESULT_ID_UNKNOWN: 412 return GeofenceHardware.GEOFENCE_ERROR_ID_UNKNOWN; 413 case FLP_RESULT_INVALID_GEOFENCE_TRANSITION: 414 return GeofenceHardware.GEOFENCE_ERROR_INVALID_TRANSITION; 415 default: 416 Log.e(TAG, String.format("Invalid FlpHal result code: %d", flpHalResult)); 417 return GeofenceHardware.GEOFENCE_FAILURE; 418 } 419 } 420 421 private Location updateLocationInformation(Location location) { 422 location.setProvider(LocationManager.FUSED_PROVIDER); 423 // set the elapsed time-stamp just as GPS provider does 424 location.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos()); 425 return location; 426 } 427 } 428