blob: db3db0c80e3c4b9a5f476fc9e5a8abf83f5648bc [file] [log] [blame]
Peng Xua35b5532016-01-20 00:05:45 -08001/*
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
Peng Xua35b5532016-01-20 00:05:45 -080019import android.content.Context;
20import android.content.Intent;
Peng Xu1cfde252017-01-19 17:10:09 -080021import android.hardware.GeomagneticField;
Peng Xua35b5532016-01-20 00:05:45 -080022import android.hardware.Sensor;
Peng Xu1cfde252017-01-19 17:10:09 -080023import android.hardware.SensorAdditionalInfo;
Peng Xua35b5532016-01-20 00:05:45 -080024import android.hardware.SensorEvent;
25import android.hardware.SensorEventListener;
26import android.hardware.SensorManager;
Peng Xu1cfde252017-01-19 17:10:09 -080027import android.location.Location;
28import android.location.LocationListener;
29import android.location.LocationManager;
30import android.os.Bundle;
Peng Xua35b5532016-01-20 00:05:45 -080031import android.os.SystemClock;
Peng Xua35b5532016-01-20 00:05:45 -080032import android.os.UserHandle;
Peng Xua35b5532016-01-20 00:05:45 -080033import android.util.Slog;
34
Peng Xu1cfde252017-01-19 17:10:09 -080035public class SensorNotificationService extends SystemService
36 implements SensorEventListener, LocationListener {
37 private static final boolean DBG = false;
Peng Xua35b5532016-01-20 00:05:45 -080038 private static final String TAG = "SensorNotificationService";
Peng Xua35b5532016-01-20 00:05:45 -080039
Peng Xu1cfde252017-01-19 17:10:09 -080040 private static final long MINUTE_IN_MS = 60 * 1000;
41 private static final long KM_IN_M = 1000;
42
43 private static final long LOCATION_MIN_TIME = 30 * MINUTE_IN_MS;
44 private static final long LOCATION_MIN_DISTANCE = 100 * KM_IN_M;
45
46 private static final String PROPERTY_USE_MOCKED_LOCATION =
47 "sensor.notification.use_mocked"; // max key length is 32
48
49 private static final long MILLIS_2010_1_1 = 1262358000000l;
50
Philip P. Moltmann12ac3f42020-03-05 15:01:29 -080051 private static final String ATTRIBUTION_TAG = "SensorNotificationService";
Philip P. Moltmann41ace3b2020-02-19 21:49:15 +000052
Peng Xu1cfde252017-01-19 17:10:09 -080053 private Context mContext;
Peng Xua35b5532016-01-20 00:05:45 -080054 private SensorManager mSensorManager;
Peng Xu1cfde252017-01-19 17:10:09 -080055 private LocationManager mLocationManager;
Peng Xua35b5532016-01-20 00:05:45 -080056 private Sensor mMetaSensor;
57
Peng Xu1cfde252017-01-19 17:10:09 -080058 // for rate limiting
59 private long mLocalGeomagneticFieldUpdateTime = -LOCATION_MIN_TIME;
60
Peng Xua35b5532016-01-20 00:05:45 -080061 public SensorNotificationService(Context context) {
Philip P. Moltmann12ac3f42020-03-05 15:01:29 -080062 super(context.createAttributionContext(ATTRIBUTION_TAG));
Philip P. Moltmann41ace3b2020-02-19 21:49:15 +000063 mContext = getContext();
Peng Xua35b5532016-01-20 00:05:45 -080064 }
65
66 public void onStart() {
67 LocalServices.addService(SensorNotificationService.class, this);
68 }
69
70 public void onBootPhase(int phase) {
71 if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
Peng Xua35b5532016-01-20 00:05:45 -080072 mSensorManager = (SensorManager) mContext.getSystemService(Context.SENSOR_SERVICE);
73 mMetaSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_DYNAMIC_SENSOR_META);
74 if (mMetaSensor == null) {
75 if (DBG) Slog.d(TAG, "Cannot obtain dynamic meta sensor, not supported.");
76 } else {
77 mSensorManager.registerListener(this, mMetaSensor,
78 SensorManager.SENSOR_DELAY_FASTEST);
79 }
80 }
Peng Xu1cfde252017-01-19 17:10:09 -080081
82 if (phase == PHASE_BOOT_COMPLETED) {
83 // LocationManagerService is initialized after PHASE_THIRD_PARTY_APPS_CAN_START
84 mLocationManager =
85 (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE);
86 if (mLocationManager == null) {
87 if (DBG) Slog.d(TAG, "Cannot obtain location service.");
88 } else {
89 mLocationManager.requestLocationUpdates(
90 LocationManager.PASSIVE_PROVIDER,
91 LOCATION_MIN_TIME,
92 LOCATION_MIN_DISTANCE,
93 this);
94 }
95 }
Peng Xua35b5532016-01-20 00:05:45 -080096 }
97
98 private void broadcastDynamicSensorChanged() {
99 Intent i = new Intent(Intent.ACTION_DYNAMIC_SENSOR_CHANGED);
100 i.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); // avoid waking up manifest receivers
101 mContext.sendBroadcastAsUser(i, UserHandle.ALL);
Peng Xu1cfde252017-01-19 17:10:09 -0800102 if (DBG) Slog.d(TAG, "dynamic sensor broadcast sent");
Peng Xua35b5532016-01-20 00:05:45 -0800103 }
104
105 @Override
106 public void onSensorChanged(SensorEvent event) {
107 if (event.sensor == mMetaSensor) {
108 broadcastDynamicSensorChanged();
109 }
110 }
111
112 @Override
Peng Xu1cfde252017-01-19 17:10:09 -0800113 public void onLocationChanged(Location location) {
114 if (DBG) Slog.d(TAG, String.format(
115 "Location is (%f, %f), h %f, acc %f, mocked %b",
116 location.getLatitude(), location.getLongitude(),
117 location.getAltitude(), location.getAccuracy(),
118 location.isFromMockProvider()));
Peng Xua35b5532016-01-20 00:05:45 -0800119
Peng Xu1cfde252017-01-19 17:10:09 -0800120 // lat long == 0 usually means invalid location
121 if (location.getLatitude() == 0 && location.getLongitude() == 0) {
122 return;
123 }
124
125 // update too often, ignore
126 if (SystemClock.elapsedRealtime() - mLocalGeomagneticFieldUpdateTime < 10 * MINUTE_IN_MS) {
127 return;
128 }
129
130 long time = System.currentTimeMillis();
131 // Mocked location should not be used. Except in test, only use mocked location
132 // Wrong system clock also gives bad values so ignore as well.
133 if (useMockedLocation() == location.isFromMockProvider() || time < MILLIS_2010_1_1) {
134 return;
135 }
136
137 GeomagneticField field = new GeomagneticField(
138 (float) location.getLatitude(), (float) location.getLongitude(),
139 (float) location.getAltitude(), time);
140 if (DBG) Slog.d(TAG, String.format(
141 "Nominal mag field, norm %fuT, decline %f deg, incline %f deg",
142 field.getFieldStrength() / 1000, field.getDeclination(), field.getInclination()));
143
144 try {
145 SensorAdditionalInfo info = SensorAdditionalInfo.createLocalGeomagneticField(
146 field.getFieldStrength() / 1000, // convert from nT to uT
147 (float)(field.getDeclination() * Math.PI / 180), // from degree to rad
148 (float)(field.getInclination() * Math.PI / 180)); // from degree to rad
149 if (info != null) {
150 mSensorManager.setOperationParameter(info);
151 mLocalGeomagneticFieldUpdateTime = SystemClock.elapsedRealtime();
152 }
153 } catch (IllegalArgumentException e) {
154 Slog.e(TAG, "Invalid local geomagnetic field, ignore.");
155 }
156 }
157
158 @Override
159 public void onAccuracyChanged(Sensor sensor, int accuracy) {}
160 @Override
161 public void onStatusChanged(String provider, int status, Bundle extras) {}
162 @Override
163 public void onProviderEnabled(String provider) {}
164 @Override
165 public void onProviderDisabled(String provider) {}
166
167 private boolean useMockedLocation() {
168 return "false".equals(System.getProperty(PROPERTY_USE_MOCKED_LOCATION, "false"));
Peng Xua35b5532016-01-20 00:05:45 -0800169 }
170}
171