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.content.Context; 20 import android.hardware.location.GeofenceHardware; 21 import android.hardware.location.GeofenceHardwareImpl; 22 import android.hardware.location.GeofenceHardwareRequestParcelable; 23 import android.hardware.location.IFusedLocationHardware; 24 import android.hardware.location.IFusedLocationHardwareSink; 25 import android.location.FusedBatchOptions; 26 import android.location.IFusedGeofenceHardware; 27 import android.location.Location; 28 import android.location.LocationListener; 29 import android.location.LocationManager; 30 import android.location.LocationRequest; 31 import android.os.Bundle; 32 import android.os.Looper; 33 import android.os.RemoteException; 34 import android.os.SystemClock; 35 import android.util.Log; 36 37 /** 38 * This class is an interop layer for JVM types and the JNI code that interacts 39 * with the FLP HAL implementation. 40 * 41 * {@hide} 42 */ 43 public class FlpHardwareProvider { 44 private static final int FIRST_VERSION_WITH_FLUSH_LOCATIONS = 2; 45 private GeofenceHardwareImpl mGeofenceHardwareSink = null; 46 private IFusedLocationHardwareSink mLocationSink = null; 47 // Capabilities provided by FlpCallbacks 48 private boolean mHaveBatchingCapabilities; 49 private int mBatchingCapabilities; 50 private int mVersion = 1; 51 52 private static FlpHardwareProvider sSingletonInstance = null; 53 54 private final static String TAG = "FlpHardwareProvider"; 55 private final Context mContext; 56 private final Object mLocationSinkLock = new Object(); 57 58 // FlpHal result codes, they must be equal to the ones in fused_location.h 59 private static final int FLP_RESULT_SUCCESS = 0; 60 private static final int FLP_RESULT_ERROR = -1; 61 private static final int FLP_RESULT_INSUFFICIENT_MEMORY = -2; 62 private static final int FLP_RESULT_TOO_MANY_GEOFENCES = -3; 63 private static final int FLP_RESULT_ID_EXISTS = -4; 64 private static final int FLP_RESULT_ID_UNKNOWN = -5; 65 private static final int FLP_RESULT_INVALID_GEOFENCE_TRANSITION = -6; 66 67 // FlpHal monitor status codes, they must be equal to the ones in fused_location.h 68 private static final int FLP_GEOFENCE_MONITOR_STATUS_UNAVAILABLE = 1<<0; 69 private static final int FLP_GEOFENCE_MONITOR_STATUS_AVAILABLE = 1<<1; 70 71 public static FlpHardwareProvider getInstance(Context context) { 72 if (sSingletonInstance == null) { 73 sSingletonInstance = new FlpHardwareProvider(context); 74 sSingletonInstance.nativeInit(); 75 } 76 77 return sSingletonInstance; 78 } 79 80 private FlpHardwareProvider(Context context) { 81 mContext = context; 82 83 // register for listening for passive provider data 84 LocationManager manager = (LocationManager) mContext.getSystemService( 85 Context.LOCATION_SERVICE); 86 final long minTime = 0; 87 final float minDistance = 0; 88 final boolean oneShot = false; 89 LocationRequest request = LocationRequest.createFromDeprecatedProvider( 90 LocationManager.PASSIVE_PROVIDER, 91 minTime, 92 minDistance, 93 oneShot); 94 // Don't keep track of this request since it's done on behalf of other clients 95 // (which are kept track of separately). 96 request.setHideFromAppOps(true); 97 manager.requestLocationUpdates( 98 request, 99 new NetworkLocationListener(), 100 Looper.myLooper()); 101 } 102 103 public static boolean isSupported() { 104 return nativeIsSupported(); 105 } 106 107 /** 108 * Private callback functions used by FLP HAL. 109 */ 110 // FlpCallbacks members 111 private void onLocationReport(Location[] locations) { 112 for (Location location : locations) { 113 location.setProvider(LocationManager.FUSED_PROVIDER); 114 // set the elapsed time-stamp just as GPS provider does 115 location.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos()); 116 } 117 118 IFusedLocationHardwareSink sink; 119 synchronized (mLocationSinkLock) { 120 sink = mLocationSink; 121 } 122 try { 123 if (sink != null) { 124 sink.onLocationAvailable(locations); 125 } 126 } catch (RemoteException e) { 127 Log.e(TAG, "RemoteException calling onLocationAvailable"); 128 } 129 } 130 131 private void onBatchingCapabilities(int capabilities) { 132 synchronized (mLocationSinkLock) { 133 mHaveBatchingCapabilities = true; 134 mBatchingCapabilities = capabilities; 135 } 136 137 maybeSendCapabilities(); 138 139 if (mGeofenceHardwareSink != null) { 140 mGeofenceHardwareSink.setVersion(getVersion()); 141 } 142 } 143 144 private void onBatchingStatus(int status) { 145 IFusedLocationHardwareSink sink; 146 synchronized (mLocationSinkLock) { 147 sink = mLocationSink; 148 } 149 try { 150 if (sink != null) { 151 sink.onStatusChanged(status); 152 } 153 } catch (RemoteException e) { 154 Log.e(TAG, "RemoteException calling onBatchingStatus"); 155 } 156 } 157 158 // Returns the current version of the FLP HAL. This depends both on the version of the 159 // structure returned by the hardware layer, and whether or not we've received the 160 // capabilities callback on initialization. Assume original version until we get 161 // the new initialization callback. 162 private int getVersion() { 163 synchronized (mLocationSinkLock) { 164 if (mHaveBatchingCapabilities) { 165 return mVersion; 166 } 167 } 168 return 1; 169 } 170 171 private void setVersion(int version) { 172 mVersion = version; 173 if (mGeofenceHardwareSink != null) { 174 mGeofenceHardwareSink.setVersion(getVersion()); 175 } 176 } 177 178 private void maybeSendCapabilities() { 179 IFusedLocationHardwareSink sink; 180 boolean haveBatchingCapabilities; 181 int batchingCapabilities; 182 synchronized (mLocationSinkLock) { 183 sink = mLocationSink; 184 haveBatchingCapabilities = mHaveBatchingCapabilities; 185 batchingCapabilities = mBatchingCapabilities; 186 } 187 try { 188 if (sink != null && haveBatchingCapabilities) { 189 sink.onCapabilities(batchingCapabilities); 190 } 191 } catch (RemoteException e) { 192 Log.e(TAG, "RemoteException calling onLocationAvailable"); 193 } 194 } 195 196 // FlpDiagnosticCallbacks members 197 private void onDataReport(String data) { 198 IFusedLocationHardwareSink sink; 199 synchronized (mLocationSinkLock) { 200 sink = mLocationSink; 201 } 202 try { 203 if (mLocationSink != null) { 204 sink.onDiagnosticDataAvailable(data); 205 } 206 } catch (RemoteException e) { 207 Log.e(TAG, "RemoteException calling onDiagnosticDataAvailable"); 208 } 209 } 210 211 // FlpGeofenceCallbacks members 212 private void onGeofenceTransition( 213 int geofenceId, 214 Location location, 215 int transition, 216 long timestamp, 217 int sourcesUsed) { 218 // the transition Id does not require translation because the values in fused_location.h 219 // and GeofenceHardware are in sync 220 getGeofenceHardwareSink().reportGeofenceTransition( 221 geofenceId, 222 updateLocationInformation(location), 223 transition, 224 timestamp, 225 GeofenceHardware.MONITORING_TYPE_FUSED_HARDWARE, 226 sourcesUsed); 227 } 228 229 private void onGeofenceMonitorStatus(int status, int source, Location location) { 230 // allow the location to be optional in this event 231 Location updatedLocation = null; 232 if(location != null) { 233 updatedLocation = updateLocationInformation(location); 234 } 235 236 int monitorStatus; 237 switch (status) { 238 case FLP_GEOFENCE_MONITOR_STATUS_UNAVAILABLE: 239 monitorStatus = GeofenceHardware.MONITOR_CURRENTLY_UNAVAILABLE; 240 break; 241 case FLP_GEOFENCE_MONITOR_STATUS_AVAILABLE: 242 monitorStatus = GeofenceHardware.MONITOR_CURRENTLY_AVAILABLE; 243 break; 244 default: 245 Log.e(TAG, "Invalid FlpHal Geofence monitor status: " + status); 246 monitorStatus = GeofenceHardware.MONITOR_CURRENTLY_UNAVAILABLE; 247 break; 248 } 249 250 getGeofenceHardwareSink().reportGeofenceMonitorStatus( 251 GeofenceHardware.MONITORING_TYPE_FUSED_HARDWARE, 252 monitorStatus, 253 updatedLocation, 254 source); 255 } 256 257 private void onGeofenceAdd(int geofenceId, int result) { 258 getGeofenceHardwareSink().reportGeofenceAddStatus( 259 geofenceId, 260 translateToGeofenceHardwareStatus(result)); 261 } 262 263 private void onGeofenceRemove(int geofenceId, int result) { 264 getGeofenceHardwareSink().reportGeofenceRemoveStatus( 265 geofenceId, 266 translateToGeofenceHardwareStatus(result)); 267 } 268 269 private void onGeofencePause(int geofenceId, int result) { 270 getGeofenceHardwareSink().reportGeofencePauseStatus( 271 geofenceId, 272 translateToGeofenceHardwareStatus(result)); 273 } 274 275 private void onGeofenceResume(int geofenceId, int result) { 276 getGeofenceHardwareSink().reportGeofenceResumeStatus( 277 geofenceId, 278 translateToGeofenceHardwareStatus(result)); 279 } 280 281 private void onGeofencingCapabilities(int capabilities) { 282 getGeofenceHardwareSink().onCapabilities(capabilities); 283 } 284 285 /** 286 * Private native methods accessing FLP HAL. 287 */ 288 static { nativeClassInit(); } 289 290 // Core members 291 private static native void nativeClassInit(); 292 private static native boolean nativeIsSupported(); 293 294 // FlpLocationInterface members 295 private native void nativeInit(); 296 private native int nativeGetBatchSize(); 297 private native void nativeStartBatching(int requestId, FusedBatchOptions options); 298 private native void nativeUpdateBatchingOptions(int requestId, FusedBatchOptions optionsObject); 299 private native void nativeStopBatching(int id); 300 private native void nativeRequestBatchedLocation(int lastNLocations); 301 private native void nativeFlushBatchedLocations(); 302 private native void nativeInjectLocation(Location location); 303 private native void nativeCleanup(); 304 305 // FlpDiagnosticsInterface members 306 private native boolean nativeIsDiagnosticSupported(); 307 private native void nativeInjectDiagnosticData(String data); 308 309 // FlpDeviceContextInterface members 310 private native boolean nativeIsDeviceContextSupported(); 311 private native void nativeInjectDeviceContext(int deviceEnabledContext); 312 313 // FlpGeofencingInterface members 314 private native boolean nativeIsGeofencingSupported(); 315 private native void nativeAddGeofences( 316 GeofenceHardwareRequestParcelable[] geofenceRequestsArray); 317 private native void nativePauseGeofence(int geofenceId); 318 private native void nativeResumeGeofence(int geofenceId, int monitorTransitions); 319 private native void nativeModifyGeofenceOption( 320 int geofenceId, 321 int lastTransition, 322 int monitorTransitions, 323 int notificationResponsiveness, 324 int unknownTimer, 325 int sourcesToUse); 326 private native void nativeRemoveGeofences(int[] geofenceIdsArray); 327 328 /** 329 * Interface implementations for services built on top of this functionality. 330 */ 331 public static final String LOCATION = "Location"; 332 public static final String GEOFENCING = "Geofencing"; 333 334 public IFusedLocationHardware getLocationHardware() { 335 return mLocationHardware; 336 } 337 338 public IFusedGeofenceHardware getGeofenceHardware() { 339 return mGeofenceHardwareService; 340 } 341 342 public void cleanup() { 343 Log.i(TAG, "Calling nativeCleanup()"); 344 nativeCleanup(); 345 } 346 347 private final IFusedLocationHardware mLocationHardware = new IFusedLocationHardware.Stub() { 348 @Override 349 public void registerSink(IFusedLocationHardwareSink eventSink) { 350 synchronized (mLocationSinkLock) { 351 // only one sink is allowed at the moment 352 if (mLocationSink != null) { 353 Log.e(TAG, "Replacing an existing IFusedLocationHardware sink"); 354 } 355 356 mLocationSink = eventSink; 357 } 358 maybeSendCapabilities(); 359 } 360 361 @Override 362 public void unregisterSink(IFusedLocationHardwareSink eventSink) { 363 synchronized (mLocationSinkLock) { 364 // don't throw if the sink is not registered, simply make it a no-op 365 if (mLocationSink == eventSink) { 366 mLocationSink = null; 367 } 368 } 369 } 370 371 @Override 372 public int getSupportedBatchSize() { 373 return nativeGetBatchSize(); 374 } 375 376 @Override 377 public void startBatching(int requestId, FusedBatchOptions options) { 378 nativeStartBatching(requestId, options); 379 } 380 381 @Override 382 public void stopBatching(int requestId) { 383 nativeStopBatching(requestId); 384 } 385 386 @Override 387 public void updateBatchingOptions(int requestId, FusedBatchOptions options) { 388 nativeUpdateBatchingOptions(requestId, options); 389 } 390 391 @Override 392 public void requestBatchOfLocations(int batchSizeRequested) { 393 nativeRequestBatchedLocation(batchSizeRequested); 394 } 395 396 @Override 397 public void flushBatchedLocations() { 398 if (getVersion() >= FIRST_VERSION_WITH_FLUSH_LOCATIONS) { 399 nativeFlushBatchedLocations(); 400 } else { 401 Log.wtf(TAG, 402 "Tried to call flushBatchedLocations on an unsupported implementation"); 403 } 404 } 405 406 @Override 407 public boolean supportsDiagnosticDataInjection() { 408 return nativeIsDiagnosticSupported(); 409 } 410 411 @Override 412 public void injectDiagnosticData(String data) { 413 nativeInjectDiagnosticData(data); 414 } 415 416 @Override 417 public boolean supportsDeviceContextInjection() { 418 return nativeIsDeviceContextSupported(); 419 } 420 421 @Override 422 public void injectDeviceContext(int deviceEnabledContext) { 423 nativeInjectDeviceContext(deviceEnabledContext); 424 } 425 426 @Override 427 public int getVersion() { 428 return FlpHardwareProvider.this.getVersion(); 429 } 430 }; 431 432 private final IFusedGeofenceHardware mGeofenceHardwareService = 433 new IFusedGeofenceHardware.Stub() { 434 @Override 435 public boolean isSupported() { 436 return nativeIsGeofencingSupported(); 437 } 438 439 @Override 440 public void addGeofences(GeofenceHardwareRequestParcelable[] geofenceRequestsArray) { 441 nativeAddGeofences(geofenceRequestsArray); 442 } 443 444 @Override 445 public void removeGeofences(int[] geofenceIds) { 446 nativeRemoveGeofences(geofenceIds); 447 } 448 449 @Override 450 public void pauseMonitoringGeofence(int geofenceId) { 451 nativePauseGeofence(geofenceId); 452 } 453 454 @Override 455 public void resumeMonitoringGeofence(int geofenceId, int monitorTransitions) { 456 nativeResumeGeofence(geofenceId, monitorTransitions); 457 } 458 459 @Override 460 public void modifyGeofenceOptions(int geofenceId, 461 int lastTransition, 462 int monitorTransitions, 463 int notificationResponsiveness, 464 int unknownTimer, 465 int sourcesToUse) { 466 nativeModifyGeofenceOption( 467 geofenceId, 468 lastTransition, 469 monitorTransitions, 470 notificationResponsiveness, 471 unknownTimer, 472 sourcesToUse); 473 } 474 }; 475 476 /** 477 * Internal classes and functions used by the provider. 478 */ 479 private final class NetworkLocationListener implements LocationListener { 480 @Override 481 public void onLocationChanged(Location location) { 482 if ( 483 !LocationManager.NETWORK_PROVIDER.equals(location.getProvider()) || 484 !location.hasAccuracy() 485 ) { 486 return; 487 } 488 489 nativeInjectLocation(location); 490 } 491 492 @Override 493 public void onStatusChanged(String provider, int status, Bundle extras) { } 494 495 @Override 496 public void onProviderEnabled(String provider) { } 497 498 @Override 499 public void onProviderDisabled(String provider) { } 500 } 501 502 private GeofenceHardwareImpl getGeofenceHardwareSink() { 503 if (mGeofenceHardwareSink == null) { 504 mGeofenceHardwareSink = GeofenceHardwareImpl.getInstance(mContext); 505 mGeofenceHardwareSink.setVersion(getVersion()); 506 } 507 508 return mGeofenceHardwareSink; 509 } 510 511 private static int translateToGeofenceHardwareStatus(int flpHalResult) { 512 switch(flpHalResult) { 513 case FLP_RESULT_SUCCESS: 514 return GeofenceHardware.GEOFENCE_SUCCESS; 515 case FLP_RESULT_ERROR: 516 return GeofenceHardware.GEOFENCE_FAILURE; 517 case FLP_RESULT_INSUFFICIENT_MEMORY: 518 return GeofenceHardware.GEOFENCE_ERROR_INSUFFICIENT_MEMORY; 519 case FLP_RESULT_TOO_MANY_GEOFENCES: 520 return GeofenceHardware.GEOFENCE_ERROR_TOO_MANY_GEOFENCES; 521 case FLP_RESULT_ID_EXISTS: 522 return GeofenceHardware.GEOFENCE_ERROR_ID_EXISTS; 523 case FLP_RESULT_ID_UNKNOWN: 524 return GeofenceHardware.GEOFENCE_ERROR_ID_UNKNOWN; 525 case FLP_RESULT_INVALID_GEOFENCE_TRANSITION: 526 return GeofenceHardware.GEOFENCE_ERROR_INVALID_TRANSITION; 527 default: 528 Log.e(TAG, String.format("Invalid FlpHal result code: %d", flpHalResult)); 529 return GeofenceHardware.GEOFENCE_FAILURE; 530 } 531 } 532 533 private Location updateLocationInformation(Location location) { 534 location.setProvider(LocationManager.FUSED_PROVIDER); 535 // set the elapsed time-stamp just as GPS provider does 536 location.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos()); 537 return location; 538 } 539 } 540