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