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