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 // FlpHal monitor status codes, they must be equal to the ones in fused_location.h 64 private static final int FLP_GEOFENCE_MONITOR_STATUS_UNAVAILABLE = 1<<0; 65 private static final int FLP_GEOFENCE_MONITOR_STATUS_AVAILABLE = 1<<1; 66 67 public static FlpHardwareProvider getInstance(Context context) { 68 if (sSingletonInstance == null) { 69 sSingletonInstance = new FlpHardwareProvider(context); 70 sSingletonInstance.nativeInit(); 71 } 72 73 return sSingletonInstance; 74 } 75 76 private FlpHardwareProvider(Context context) { 77 mContext = context; 78 79 // register for listening for passive provider data 80 LocationManager manager = (LocationManager) mContext.getSystemService( 81 Context.LOCATION_SERVICE); 82 final long minTime = 0; 83 final float minDistance = 0; 84 final boolean oneShot = false; 85 LocationRequest request = LocationRequest.createFromDeprecatedProvider( 86 LocationManager.PASSIVE_PROVIDER, 87 minTime, 88 minDistance, 89 oneShot); 90 // Don't keep track of this request since it's done on behalf of other clients 91 // (which are kept track of separately). 92 request.setHideFromAppOps(true); 93 manager.requestLocationUpdates( 94 request, 95 new NetworkLocationListener(), 96 Looper.myLooper()); 97 } 98 99 public static boolean isSupported() { 100 return nativeIsSupported(); 101 } 102 103 /** 104 * Private callback functions used by FLP HAL. 105 */ 106 // FlpCallbacks members 107 private void onLocationReport(Location[] locations) { 108 for (Location location : locations) { 109 location.setProvider(LocationManager.FUSED_PROVIDER); 110 // set the elapsed time-stamp just as GPS provider does 111 location.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos()); 112 } 113 114 IFusedLocationHardwareSink sink; 115 synchronized (mLocationSinkLock) { 116 sink = mLocationSink; 117 } 118 try { 119 if (sink != null) { 120 sink.onLocationAvailable(locations); 121 } 122 } catch (RemoteException e) { 123 Log.e(TAG, "RemoteException calling onLocationAvailable"); 124 } 125 } 126 127 // FlpDiagnosticCallbacks members 128 private void onDataReport(String data) { 129 IFusedLocationHardwareSink sink; 130 synchronized (mLocationSinkLock) { 131 sink = mLocationSink; 132 } 133 try { 134 if (mLocationSink != null) { 135 sink.onDiagnosticDataAvailable(data); 136 } 137 } catch (RemoteException e) { 138 Log.e(TAG, "RemoteException calling onDiagnosticDataAvailable"); 139 } 140 } 141 142 // FlpGeofenceCallbacks members 143 private void onGeofenceTransition( 144 int geofenceId, 145 Location location, 146 int transition, 147 long timestamp, 148 int sourcesUsed) { 149 // the transition Id does not require translation because the values in fused_location.h 150 // and GeofenceHardware are in sync 151 getGeofenceHardwareSink().reportGeofenceTransition( 152 geofenceId, 153 updateLocationInformation(location), 154 transition, 155 timestamp, 156 GeofenceHardware.MONITORING_TYPE_FUSED_HARDWARE, 157 sourcesUsed); 158 } 159 160 private void onGeofenceMonitorStatus(int status, int source, Location location) { 161 // allow the location to be optional in this event 162 Location updatedLocation = null; 163 if(location != null) { 164 updatedLocation = updateLocationInformation(location); 165 } 166 167 int monitorStatus; 168 switch (status) { 169 case FLP_GEOFENCE_MONITOR_STATUS_UNAVAILABLE: 170 monitorStatus = GeofenceHardware.MONITOR_CURRENTLY_UNAVAILABLE; 171 break; 172 case FLP_GEOFENCE_MONITOR_STATUS_AVAILABLE: 173 monitorStatus = GeofenceHardware.MONITOR_CURRENTLY_AVAILABLE; 174 break; 175 default: 176 Log.e(TAG, "Invalid FlpHal Geofence monitor status: " + status); 177 monitorStatus = GeofenceHardware.MONITOR_CURRENTLY_UNAVAILABLE; 178 break; 179 } 180 181 getGeofenceHardwareSink().reportGeofenceMonitorStatus( 182 GeofenceHardware.MONITORING_TYPE_FUSED_HARDWARE, 183 monitorStatus, 184 updatedLocation, 185 source); 186 } 187 188 private void onGeofenceAdd(int geofenceId, int result) { 189 getGeofenceHardwareSink().reportGeofenceAddStatus( 190 geofenceId, 191 translateToGeofenceHardwareStatus(result)); 192 } 193 194 private void onGeofenceRemove(int geofenceId, int result) { 195 getGeofenceHardwareSink().reportGeofenceRemoveStatus( 196 geofenceId, 197 translateToGeofenceHardwareStatus(result)); 198 } 199 200 private void onGeofencePause(int geofenceId, int result) { 201 getGeofenceHardwareSink().reportGeofencePauseStatus( 202 geofenceId, 203 translateToGeofenceHardwareStatus(result)); 204 } 205 206 private void onGeofenceResume(int geofenceId, int result) { 207 getGeofenceHardwareSink().reportGeofenceResumeStatus( 208 geofenceId, 209 translateToGeofenceHardwareStatus(result)); 210 } 211 212 /** 213 * Private native methods accessing FLP HAL. 214 */ 215 static { nativeClassInit(); } 216 217 // Core members 218 private static native void nativeClassInit(); 219 private static native boolean nativeIsSupported(); 220 221 // FlpLocationInterface members 222 private native void nativeInit(); 223 private native int nativeGetBatchSize(); 224 private native void nativeStartBatching(int requestId, FusedBatchOptions options); 225 private native void nativeUpdateBatchingOptions(int requestId, FusedBatchOptions optionsObject); 226 private native void nativeStopBatching(int id); 227 private native void nativeRequestBatchedLocation(int lastNLocations); 228 private native void nativeInjectLocation(Location location); 229 // TODO [Fix] sort out the lifetime of the instance 230 private native void nativeCleanup(); 231 232 // FlpDiagnosticsInterface members 233 private native boolean nativeIsDiagnosticSupported(); 234 private native void nativeInjectDiagnosticData(String data); 235 236 // FlpDeviceContextInterface members 237 private native boolean nativeIsDeviceContextSupported(); 238 private native void nativeInjectDeviceContext(int deviceEnabledContext); 239 240 // FlpGeofencingInterface members 241 private native boolean nativeIsGeofencingSupported(); 242 private native void nativeAddGeofences( 243 GeofenceHardwareRequestParcelable[] geofenceRequestsArray); 244 private native void nativePauseGeofence(int geofenceId); 245 private native void nativeResumeGeofence(int geofenceId, int monitorTransitions); 246 private native void nativeModifyGeofenceOption( 247 int geofenceId, 248 int lastTransition, 249 int monitorTransitions, 250 int notificationResponsiveness, 251 int unknownTimer, 252 int sourcesToUse); 253 private native void nativeRemoveGeofences(int[] geofenceIdsArray); 254 255 /** 256 * Interface implementations for services built on top of this functionality. 257 */ 258 public static final String LOCATION = "Location"; 259 public static final String GEOFENCING = "Geofencing"; 260 261 public IFusedLocationHardware getLocationHardware() { 262 return mLocationHardware; 263 } 264 265 public IFusedGeofenceHardware getGeofenceHardware() { 266 return mGeofenceHardwareService; 267 } 268 269 private final IFusedLocationHardware mLocationHardware = new IFusedLocationHardware.Stub() { 270 @Override 271 public void registerSink(IFusedLocationHardwareSink eventSink) { 272 synchronized (mLocationSinkLock) { 273 // only one sink is allowed at the moment 274 if (mLocationSink != null) { 275 Log.e(TAG, "Replacing an existing IFusedLocationHardware sink"); 276 } 277 278 mLocationSink = eventSink; 279 } 280 } 281 282 @Override 283 public void unregisterSink(IFusedLocationHardwareSink eventSink) { 284 synchronized (mLocationSinkLock) { 285 // don't throw if the sink is not registered, simply make it a no-op 286 if (mLocationSink == eventSink) { 287 mLocationSink = null; 288 } 289 } 290 } 291 292 @Override 293 public int getSupportedBatchSize() { 294 return nativeGetBatchSize(); 295 } 296 297 @Override 298 public void startBatching(int requestId, FusedBatchOptions options) { 299 nativeStartBatching(requestId, options); 300 } 301 302 @Override 303 public void stopBatching(int requestId) { 304 nativeStopBatching(requestId); 305 } 306 307 @Override 308 public void updateBatchingOptions(int requestId, FusedBatchOptions options) { 309 nativeUpdateBatchingOptions(requestId, options); 310 } 311 312 @Override 313 public void requestBatchOfLocations(int batchSizeRequested) { 314 nativeRequestBatchedLocation(batchSizeRequested); 315 } 316 317 @Override 318 public boolean supportsDiagnosticDataInjection() { 319 return nativeIsDiagnosticSupported(); 320 } 321 322 @Override 323 public void injectDiagnosticData(String data) { 324 nativeInjectDiagnosticData(data); 325 } 326 327 @Override 328 public boolean supportsDeviceContextInjection() { 329 return nativeIsDeviceContextSupported(); 330 } 331 332 @Override 333 public void injectDeviceContext(int deviceEnabledContext) { 334 nativeInjectDeviceContext(deviceEnabledContext); 335 } 336 }; 337 338 private final IFusedGeofenceHardware mGeofenceHardwareService = 339 new IFusedGeofenceHardware.Stub() { 340 @Override 341 public boolean isSupported() { 342 return nativeIsGeofencingSupported(); 343 } 344 345 @Override 346 public void addGeofences(GeofenceHardwareRequestParcelable[] geofenceRequestsArray) { 347 nativeAddGeofences(geofenceRequestsArray); 348 } 349 350 @Override 351 public void removeGeofences(int[] geofenceIds) { 352 nativeRemoveGeofences(geofenceIds); 353 } 354 355 @Override 356 public void pauseMonitoringGeofence(int geofenceId) { 357 nativePauseGeofence(geofenceId); 358 } 359 360 @Override 361 public void resumeMonitoringGeofence(int geofenceId, int monitorTransitions) { 362 nativeResumeGeofence(geofenceId, monitorTransitions); 363 } 364 365 @Override 366 public void modifyGeofenceOptions(int geofenceId, 367 int lastTransition, 368 int monitorTransitions, 369 int notificationResponsiveness, 370 int unknownTimer, 371 int sourcesToUse) { 372 nativeModifyGeofenceOption( 373 geofenceId, 374 lastTransition, 375 monitorTransitions, 376 notificationResponsiveness, 377 unknownTimer, 378 sourcesToUse); 379 } 380 }; 381 382 /** 383 * Internal classes and functions used by the provider. 384 */ 385 private final class NetworkLocationListener implements LocationListener { 386 @Override 387 public void onLocationChanged(Location location) { 388 if ( 389 !LocationManager.NETWORK_PROVIDER.equals(location.getProvider()) || 390 !location.hasAccuracy() 391 ) { 392 return; 393 } 394 395 nativeInjectLocation(location); 396 } 397 398 @Override 399 public void onStatusChanged(String provider, int status, Bundle extras) { } 400 401 @Override 402 public void onProviderEnabled(String provider) { } 403 404 @Override 405 public void onProviderDisabled(String provider) { } 406 } 407 408 private GeofenceHardwareImpl getGeofenceHardwareSink() { 409 if (mGeofenceHardwareSink == null) { 410 mGeofenceHardwareSink = GeofenceHardwareImpl.getInstance(mContext); 411 } 412 413 return mGeofenceHardwareSink; 414 } 415 416 private static int translateToGeofenceHardwareStatus(int flpHalResult) { 417 switch(flpHalResult) { 418 case FLP_RESULT_SUCCESS: 419 return GeofenceHardware.GEOFENCE_SUCCESS; 420 case FLP_RESULT_ERROR: 421 return GeofenceHardware.GEOFENCE_FAILURE; 422 case FLP_RESULT_INSUFFICIENT_MEMORY: 423 return GeofenceHardware.GEOFENCE_ERROR_INSUFFICIENT_MEMORY; 424 case FLP_RESULT_TOO_MANY_GEOFENCES: 425 return GeofenceHardware.GEOFENCE_ERROR_TOO_MANY_GEOFENCES; 426 case FLP_RESULT_ID_EXISTS: 427 return GeofenceHardware.GEOFENCE_ERROR_ID_EXISTS; 428 case FLP_RESULT_ID_UNKNOWN: 429 return GeofenceHardware.GEOFENCE_ERROR_ID_UNKNOWN; 430 case FLP_RESULT_INVALID_GEOFENCE_TRANSITION: 431 return GeofenceHardware.GEOFENCE_ERROR_INVALID_TRANSITION; 432 default: 433 Log.e(TAG, String.format("Invalid FlpHal result code: %d", flpHalResult)); 434 return GeofenceHardware.GEOFENCE_FAILURE; 435 } 436 } 437 438 private Location updateLocationInformation(Location location) { 439 location.setProvider(LocationManager.FUSED_PROVIDER); 440 // set the elapsed time-stamp just as GPS provider does 441 location.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos()); 442 return location; 443 } 444 } 445