1/* 2 * Copyright (C) 2016 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; 18 19import android.content.BroadcastReceiver; 20import android.content.Context; 21import android.content.Intent; 22import android.content.IntentFilter; 23import android.hardware.GeomagneticField; 24import android.hardware.Sensor; 25import android.hardware.SensorAdditionalInfo; 26import android.hardware.SensorEvent; 27import android.hardware.SensorEventListener; 28import android.hardware.SensorManager; 29import android.location.Location; 30import android.location.LocationListener; 31import android.location.LocationManager; 32import android.os.Bundle; 33import android.os.SystemClock; 34import android.os.SystemProperties; 35import android.os.UserHandle; 36import android.provider.Settings; 37import android.util.Slog; 38 39public class SensorNotificationService extends SystemService 40 implements SensorEventListener, LocationListener { 41 private static final boolean DBG = false; 42 private static final String TAG = "SensorNotificationService"; 43 44 private static final long MINUTE_IN_MS = 60 * 1000; 45 private static final long KM_IN_M = 1000; 46 47 private static final long LOCATION_MIN_TIME = 30 * MINUTE_IN_MS; 48 private static final long LOCATION_MIN_DISTANCE = 100 * KM_IN_M; 49 50 private static final String PROPERTY_USE_MOCKED_LOCATION = 51 "sensor.notification.use_mocked"; // max key length is 32 52 53 private static final long MILLIS_2010_1_1 = 1262358000000l; 54 55 private Context mContext; 56 private SensorManager mSensorManager; 57 private LocationManager mLocationManager; 58 private Sensor mMetaSensor; 59 60 // for rate limiting 61 private long mLocalGeomagneticFieldUpdateTime = -LOCATION_MIN_TIME; 62 63 public SensorNotificationService(Context context) { 64 super(context); 65 mContext = context; 66 } 67 68 public void onStart() { 69 LocalServices.addService(SensorNotificationService.class, this); 70 } 71 72 public void onBootPhase(int phase) { 73 if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) { 74 mSensorManager = (SensorManager) mContext.getSystemService(Context.SENSOR_SERVICE); 75 mMetaSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_DYNAMIC_SENSOR_META); 76 if (mMetaSensor == null) { 77 if (DBG) Slog.d(TAG, "Cannot obtain dynamic meta sensor, not supported."); 78 } else { 79 mSensorManager.registerListener(this, mMetaSensor, 80 SensorManager.SENSOR_DELAY_FASTEST); 81 } 82 } 83 84 if (phase == PHASE_BOOT_COMPLETED) { 85 // LocationManagerService is initialized after PHASE_THIRD_PARTY_APPS_CAN_START 86 mLocationManager = 87 (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE); 88 if (mLocationManager == null) { 89 if (DBG) Slog.d(TAG, "Cannot obtain location service."); 90 } else { 91 mLocationManager.requestLocationUpdates( 92 LocationManager.PASSIVE_PROVIDER, 93 LOCATION_MIN_TIME, 94 LOCATION_MIN_DISTANCE, 95 this); 96 } 97 } 98 } 99 100 private void broadcastDynamicSensorChanged() { 101 Intent i = new Intent(Intent.ACTION_DYNAMIC_SENSOR_CHANGED); 102 i.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); // avoid waking up manifest receivers 103 mContext.sendBroadcastAsUser(i, UserHandle.ALL); 104 if (DBG) Slog.d(TAG, "dynamic sensor broadcast sent"); 105 } 106 107 @Override 108 public void onSensorChanged(SensorEvent event) { 109 if (event.sensor == mMetaSensor) { 110 broadcastDynamicSensorChanged(); 111 } 112 } 113 114 @Override 115 public void onLocationChanged(Location location) { 116 if (DBG) Slog.d(TAG, String.format( 117 "Location is (%f, %f), h %f, acc %f, mocked %b", 118 location.getLatitude(), location.getLongitude(), 119 location.getAltitude(), location.getAccuracy(), 120 location.isFromMockProvider())); 121 122 // lat long == 0 usually means invalid location 123 if (location.getLatitude() == 0 && location.getLongitude() == 0) { 124 return; 125 } 126 127 // update too often, ignore 128 if (SystemClock.elapsedRealtime() - mLocalGeomagneticFieldUpdateTime < 10 * MINUTE_IN_MS) { 129 return; 130 } 131 132 long time = System.currentTimeMillis(); 133 // Mocked location should not be used. Except in test, only use mocked location 134 // Wrong system clock also gives bad values so ignore as well. 135 if (useMockedLocation() == location.isFromMockProvider() || time < MILLIS_2010_1_1) { 136 return; 137 } 138 139 GeomagneticField field = new GeomagneticField( 140 (float) location.getLatitude(), (float) location.getLongitude(), 141 (float) location.getAltitude(), time); 142 if (DBG) Slog.d(TAG, String.format( 143 "Nominal mag field, norm %fuT, decline %f deg, incline %f deg", 144 field.getFieldStrength() / 1000, field.getDeclination(), field.getInclination())); 145 146 try { 147 SensorAdditionalInfo info = SensorAdditionalInfo.createLocalGeomagneticField( 148 field.getFieldStrength() / 1000, // convert from nT to uT 149 (float)(field.getDeclination() * Math.PI / 180), // from degree to rad 150 (float)(field.getInclination() * Math.PI / 180)); // from degree to rad 151 if (info != null) { 152 mSensorManager.setOperationParameter(info); 153 mLocalGeomagneticFieldUpdateTime = SystemClock.elapsedRealtime(); 154 } 155 } catch (IllegalArgumentException e) { 156 Slog.e(TAG, "Invalid local geomagnetic field, ignore."); 157 } 158 } 159 160 @Override 161 public void onAccuracyChanged(Sensor sensor, int accuracy) {} 162 @Override 163 public void onStatusChanged(String provider, int status, Bundle extras) {} 164 @Override 165 public void onProviderEnabled(String provider) {} 166 @Override 167 public void onProviderDisabled(String provider) {} 168 169 private boolean useMockedLocation() { 170 return "false".equals(System.getProperty(PROPERTY_USE_MOCKED_LOCATION, "false")); 171 } 172} 173 174