1a35b5539a95342799a18e95616c5e5751a198e4cPeng Xu/*
2a35b5539a95342799a18e95616c5e5751a198e4cPeng Xu * Copyright (C) 2016 The Android Open Source Project
3a35b5539a95342799a18e95616c5e5751a198e4cPeng Xu *
4a35b5539a95342799a18e95616c5e5751a198e4cPeng Xu * Licensed under the Apache License, Version 2.0 (the "License");
5a35b5539a95342799a18e95616c5e5751a198e4cPeng Xu * you may not use this file except in compliance with the License.
6a35b5539a95342799a18e95616c5e5751a198e4cPeng Xu * You may obtain a copy of the License at
7a35b5539a95342799a18e95616c5e5751a198e4cPeng Xu *
8a35b5539a95342799a18e95616c5e5751a198e4cPeng Xu *      http://www.apache.org/licenses/LICENSE-2.0
9a35b5539a95342799a18e95616c5e5751a198e4cPeng Xu *
10a35b5539a95342799a18e95616c5e5751a198e4cPeng Xu * Unless required by applicable law or agreed to in writing, software
11a35b5539a95342799a18e95616c5e5751a198e4cPeng Xu * distributed under the License is distributed on an "AS IS" BASIS,
12a35b5539a95342799a18e95616c5e5751a198e4cPeng Xu * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13a35b5539a95342799a18e95616c5e5751a198e4cPeng Xu * See the License for the specific language governing permissions and
14a35b5539a95342799a18e95616c5e5751a198e4cPeng Xu * limitations under the License.
15a35b5539a95342799a18e95616c5e5751a198e4cPeng Xu */
16a35b5539a95342799a18e95616c5e5751a198e4cPeng Xu
17a35b5539a95342799a18e95616c5e5751a198e4cPeng Xupackage com.android.server;
18a35b5539a95342799a18e95616c5e5751a198e4cPeng Xu
19a35b5539a95342799a18e95616c5e5751a198e4cPeng Xuimport android.content.BroadcastReceiver;
20a35b5539a95342799a18e95616c5e5751a198e4cPeng Xuimport android.content.Context;
21a35b5539a95342799a18e95616c5e5751a198e4cPeng Xuimport android.content.Intent;
22a35b5539a95342799a18e95616c5e5751a198e4cPeng Xuimport android.content.IntentFilter;
231cfde25e4019bc050c7f030896825fd90d69baf5Peng Xuimport android.hardware.GeomagneticField;
24a35b5539a95342799a18e95616c5e5751a198e4cPeng Xuimport android.hardware.Sensor;
251cfde25e4019bc050c7f030896825fd90d69baf5Peng Xuimport android.hardware.SensorAdditionalInfo;
26a35b5539a95342799a18e95616c5e5751a198e4cPeng Xuimport android.hardware.SensorEvent;
27a35b5539a95342799a18e95616c5e5751a198e4cPeng Xuimport android.hardware.SensorEventListener;
28a35b5539a95342799a18e95616c5e5751a198e4cPeng Xuimport android.hardware.SensorManager;
291cfde25e4019bc050c7f030896825fd90d69baf5Peng Xuimport android.location.Location;
301cfde25e4019bc050c7f030896825fd90d69baf5Peng Xuimport android.location.LocationListener;
311cfde25e4019bc050c7f030896825fd90d69baf5Peng Xuimport android.location.LocationManager;
321cfde25e4019bc050c7f030896825fd90d69baf5Peng Xuimport android.os.Bundle;
33a35b5539a95342799a18e95616c5e5751a198e4cPeng Xuimport android.os.SystemClock;
34a35b5539a95342799a18e95616c5e5751a198e4cPeng Xuimport android.os.SystemProperties;
35a35b5539a95342799a18e95616c5e5751a198e4cPeng Xuimport android.os.UserHandle;
36a35b5539a95342799a18e95616c5e5751a198e4cPeng Xuimport android.provider.Settings;
37a35b5539a95342799a18e95616c5e5751a198e4cPeng Xuimport android.util.Slog;
38a35b5539a95342799a18e95616c5e5751a198e4cPeng Xu
391cfde25e4019bc050c7f030896825fd90d69baf5Peng Xupublic class SensorNotificationService extends SystemService
401cfde25e4019bc050c7f030896825fd90d69baf5Peng Xu        implements SensorEventListener, LocationListener {
411cfde25e4019bc050c7f030896825fd90d69baf5Peng Xu    private static final boolean DBG = false;
42a35b5539a95342799a18e95616c5e5751a198e4cPeng Xu    private static final String TAG = "SensorNotificationService";
43a35b5539a95342799a18e95616c5e5751a198e4cPeng Xu
441cfde25e4019bc050c7f030896825fd90d69baf5Peng Xu    private static final long MINUTE_IN_MS = 60 * 1000;
451cfde25e4019bc050c7f030896825fd90d69baf5Peng Xu    private static final long KM_IN_M = 1000;
461cfde25e4019bc050c7f030896825fd90d69baf5Peng Xu
471cfde25e4019bc050c7f030896825fd90d69baf5Peng Xu    private static final long LOCATION_MIN_TIME = 30 * MINUTE_IN_MS;
481cfde25e4019bc050c7f030896825fd90d69baf5Peng Xu    private static final long LOCATION_MIN_DISTANCE = 100 * KM_IN_M;
491cfde25e4019bc050c7f030896825fd90d69baf5Peng Xu
501cfde25e4019bc050c7f030896825fd90d69baf5Peng Xu    private static final String PROPERTY_USE_MOCKED_LOCATION =
511cfde25e4019bc050c7f030896825fd90d69baf5Peng Xu            "sensor.notification.use_mocked"; // max key length is 32
521cfde25e4019bc050c7f030896825fd90d69baf5Peng Xu
531cfde25e4019bc050c7f030896825fd90d69baf5Peng Xu    private static final long MILLIS_2010_1_1 = 1262358000000l;
541cfde25e4019bc050c7f030896825fd90d69baf5Peng Xu
551cfde25e4019bc050c7f030896825fd90d69baf5Peng Xu    private Context mContext;
56a35b5539a95342799a18e95616c5e5751a198e4cPeng Xu    private SensorManager mSensorManager;
571cfde25e4019bc050c7f030896825fd90d69baf5Peng Xu    private LocationManager mLocationManager;
58a35b5539a95342799a18e95616c5e5751a198e4cPeng Xu    private Sensor mMetaSensor;
59a35b5539a95342799a18e95616c5e5751a198e4cPeng Xu
601cfde25e4019bc050c7f030896825fd90d69baf5Peng Xu    // for rate limiting
611cfde25e4019bc050c7f030896825fd90d69baf5Peng Xu    private long mLocalGeomagneticFieldUpdateTime = -LOCATION_MIN_TIME;
621cfde25e4019bc050c7f030896825fd90d69baf5Peng Xu
63a35b5539a95342799a18e95616c5e5751a198e4cPeng Xu    public SensorNotificationService(Context context) {
64a35b5539a95342799a18e95616c5e5751a198e4cPeng Xu        super(context);
65a35b5539a95342799a18e95616c5e5751a198e4cPeng Xu        mContext = context;
66a35b5539a95342799a18e95616c5e5751a198e4cPeng Xu    }
67a35b5539a95342799a18e95616c5e5751a198e4cPeng Xu
68a35b5539a95342799a18e95616c5e5751a198e4cPeng Xu    public void onStart() {
69a35b5539a95342799a18e95616c5e5751a198e4cPeng Xu        LocalServices.addService(SensorNotificationService.class, this);
70a35b5539a95342799a18e95616c5e5751a198e4cPeng Xu    }
71a35b5539a95342799a18e95616c5e5751a198e4cPeng Xu
72a35b5539a95342799a18e95616c5e5751a198e4cPeng Xu    public void onBootPhase(int phase) {
73a35b5539a95342799a18e95616c5e5751a198e4cPeng Xu        if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
74a35b5539a95342799a18e95616c5e5751a198e4cPeng Xu            mSensorManager = (SensorManager) mContext.getSystemService(Context.SENSOR_SERVICE);
75a35b5539a95342799a18e95616c5e5751a198e4cPeng Xu            mMetaSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_DYNAMIC_SENSOR_META);
76a35b5539a95342799a18e95616c5e5751a198e4cPeng Xu            if (mMetaSensor == null) {
77a35b5539a95342799a18e95616c5e5751a198e4cPeng Xu                if (DBG) Slog.d(TAG, "Cannot obtain dynamic meta sensor, not supported.");
78a35b5539a95342799a18e95616c5e5751a198e4cPeng Xu            } else {
79a35b5539a95342799a18e95616c5e5751a198e4cPeng Xu                mSensorManager.registerListener(this, mMetaSensor,
80a35b5539a95342799a18e95616c5e5751a198e4cPeng Xu                        SensorManager.SENSOR_DELAY_FASTEST);
81a35b5539a95342799a18e95616c5e5751a198e4cPeng Xu            }
82a35b5539a95342799a18e95616c5e5751a198e4cPeng Xu        }
831cfde25e4019bc050c7f030896825fd90d69baf5Peng Xu
841cfde25e4019bc050c7f030896825fd90d69baf5Peng Xu        if (phase == PHASE_BOOT_COMPLETED) {
851cfde25e4019bc050c7f030896825fd90d69baf5Peng Xu            // LocationManagerService is initialized after PHASE_THIRD_PARTY_APPS_CAN_START
861cfde25e4019bc050c7f030896825fd90d69baf5Peng Xu            mLocationManager =
871cfde25e4019bc050c7f030896825fd90d69baf5Peng Xu                    (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE);
881cfde25e4019bc050c7f030896825fd90d69baf5Peng Xu            if (mLocationManager == null) {
891cfde25e4019bc050c7f030896825fd90d69baf5Peng Xu                if (DBG) Slog.d(TAG, "Cannot obtain location service.");
901cfde25e4019bc050c7f030896825fd90d69baf5Peng Xu            } else {
911cfde25e4019bc050c7f030896825fd90d69baf5Peng Xu                mLocationManager.requestLocationUpdates(
921cfde25e4019bc050c7f030896825fd90d69baf5Peng Xu                        LocationManager.PASSIVE_PROVIDER,
931cfde25e4019bc050c7f030896825fd90d69baf5Peng Xu                        LOCATION_MIN_TIME,
941cfde25e4019bc050c7f030896825fd90d69baf5Peng Xu                        LOCATION_MIN_DISTANCE,
951cfde25e4019bc050c7f030896825fd90d69baf5Peng Xu                        this);
961cfde25e4019bc050c7f030896825fd90d69baf5Peng Xu            }
971cfde25e4019bc050c7f030896825fd90d69baf5Peng Xu        }
98a35b5539a95342799a18e95616c5e5751a198e4cPeng Xu    }
99a35b5539a95342799a18e95616c5e5751a198e4cPeng Xu
100a35b5539a95342799a18e95616c5e5751a198e4cPeng Xu    private void broadcastDynamicSensorChanged() {
101a35b5539a95342799a18e95616c5e5751a198e4cPeng Xu        Intent i = new Intent(Intent.ACTION_DYNAMIC_SENSOR_CHANGED);
102a35b5539a95342799a18e95616c5e5751a198e4cPeng Xu        i.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); // avoid waking up manifest receivers
103a35b5539a95342799a18e95616c5e5751a198e4cPeng Xu        mContext.sendBroadcastAsUser(i, UserHandle.ALL);
1041cfde25e4019bc050c7f030896825fd90d69baf5Peng Xu        if (DBG) Slog.d(TAG, "dynamic sensor broadcast sent");
105a35b5539a95342799a18e95616c5e5751a198e4cPeng Xu    }
106a35b5539a95342799a18e95616c5e5751a198e4cPeng Xu
107a35b5539a95342799a18e95616c5e5751a198e4cPeng Xu    @Override
108a35b5539a95342799a18e95616c5e5751a198e4cPeng Xu    public void onSensorChanged(SensorEvent event) {
109a35b5539a95342799a18e95616c5e5751a198e4cPeng Xu        if (event.sensor == mMetaSensor) {
110a35b5539a95342799a18e95616c5e5751a198e4cPeng Xu            broadcastDynamicSensorChanged();
111a35b5539a95342799a18e95616c5e5751a198e4cPeng Xu        }
112a35b5539a95342799a18e95616c5e5751a198e4cPeng Xu    }
113a35b5539a95342799a18e95616c5e5751a198e4cPeng Xu
114a35b5539a95342799a18e95616c5e5751a198e4cPeng Xu    @Override
1151cfde25e4019bc050c7f030896825fd90d69baf5Peng Xu    public void onLocationChanged(Location location) {
1161cfde25e4019bc050c7f030896825fd90d69baf5Peng Xu        if (DBG) Slog.d(TAG, String.format(
1171cfde25e4019bc050c7f030896825fd90d69baf5Peng Xu                "Location is (%f, %f), h %f, acc %f, mocked %b",
1181cfde25e4019bc050c7f030896825fd90d69baf5Peng Xu                location.getLatitude(), location.getLongitude(),
1191cfde25e4019bc050c7f030896825fd90d69baf5Peng Xu                location.getAltitude(), location.getAccuracy(),
1201cfde25e4019bc050c7f030896825fd90d69baf5Peng Xu                location.isFromMockProvider()));
1211cfde25e4019bc050c7f030896825fd90d69baf5Peng Xu
1221cfde25e4019bc050c7f030896825fd90d69baf5Peng Xu        // lat long == 0 usually means invalid location
1231cfde25e4019bc050c7f030896825fd90d69baf5Peng Xu        if (location.getLatitude() == 0 && location.getLongitude() == 0) {
1241cfde25e4019bc050c7f030896825fd90d69baf5Peng Xu            return;
1251cfde25e4019bc050c7f030896825fd90d69baf5Peng Xu        }
1261cfde25e4019bc050c7f030896825fd90d69baf5Peng Xu
1271cfde25e4019bc050c7f030896825fd90d69baf5Peng Xu        // update too often, ignore
1281cfde25e4019bc050c7f030896825fd90d69baf5Peng Xu        if (SystemClock.elapsedRealtime() - mLocalGeomagneticFieldUpdateTime < 10 * MINUTE_IN_MS) {
1291cfde25e4019bc050c7f030896825fd90d69baf5Peng Xu            return;
1301cfde25e4019bc050c7f030896825fd90d69baf5Peng Xu        }
1311cfde25e4019bc050c7f030896825fd90d69baf5Peng Xu
1321cfde25e4019bc050c7f030896825fd90d69baf5Peng Xu        long time = System.currentTimeMillis();
1331cfde25e4019bc050c7f030896825fd90d69baf5Peng Xu        // Mocked location should not be used. Except in test, only use mocked location
1341cfde25e4019bc050c7f030896825fd90d69baf5Peng Xu        // Wrong system clock also gives bad values so ignore as well.
1351cfde25e4019bc050c7f030896825fd90d69baf5Peng Xu        if (useMockedLocation() == location.isFromMockProvider() || time < MILLIS_2010_1_1) {
1361cfde25e4019bc050c7f030896825fd90d69baf5Peng Xu            return;
1371cfde25e4019bc050c7f030896825fd90d69baf5Peng Xu        }
1381cfde25e4019bc050c7f030896825fd90d69baf5Peng Xu
1391cfde25e4019bc050c7f030896825fd90d69baf5Peng Xu        GeomagneticField field = new GeomagneticField(
1401cfde25e4019bc050c7f030896825fd90d69baf5Peng Xu                (float) location.getLatitude(), (float) location.getLongitude(),
1411cfde25e4019bc050c7f030896825fd90d69baf5Peng Xu                (float) location.getAltitude(), time);
1421cfde25e4019bc050c7f030896825fd90d69baf5Peng Xu        if (DBG) Slog.d(TAG, String.format(
1431cfde25e4019bc050c7f030896825fd90d69baf5Peng Xu                "Nominal mag field, norm %fuT, decline %f deg, incline %f deg",
1441cfde25e4019bc050c7f030896825fd90d69baf5Peng Xu                field.getFieldStrength() / 1000, field.getDeclination(), field.getInclination()));
1451cfde25e4019bc050c7f030896825fd90d69baf5Peng Xu
1461cfde25e4019bc050c7f030896825fd90d69baf5Peng Xu        try {
1471cfde25e4019bc050c7f030896825fd90d69baf5Peng Xu            SensorAdditionalInfo info = SensorAdditionalInfo.createLocalGeomagneticField(
1481cfde25e4019bc050c7f030896825fd90d69baf5Peng Xu                        field.getFieldStrength() / 1000, // convert from nT to uT
1491cfde25e4019bc050c7f030896825fd90d69baf5Peng Xu                        (float)(field.getDeclination() * Math.PI / 180), // from degree to rad
1501cfde25e4019bc050c7f030896825fd90d69baf5Peng Xu                        (float)(field.getInclination() * Math.PI / 180)); // from degree to rad
1511cfde25e4019bc050c7f030896825fd90d69baf5Peng Xu            if (info != null) {
1521cfde25e4019bc050c7f030896825fd90d69baf5Peng Xu                mSensorManager.setOperationParameter(info);
1531cfde25e4019bc050c7f030896825fd90d69baf5Peng Xu                mLocalGeomagneticFieldUpdateTime = SystemClock.elapsedRealtime();
1541cfde25e4019bc050c7f030896825fd90d69baf5Peng Xu            }
1551cfde25e4019bc050c7f030896825fd90d69baf5Peng Xu        } catch (IllegalArgumentException e) {
1561cfde25e4019bc050c7f030896825fd90d69baf5Peng Xu            Slog.e(TAG, "Invalid local geomagnetic field, ignore.");
1571cfde25e4019bc050c7f030896825fd90d69baf5Peng Xu        }
1581cfde25e4019bc050c7f030896825fd90d69baf5Peng Xu    }
1591cfde25e4019bc050c7f030896825fd90d69baf5Peng Xu
1601cfde25e4019bc050c7f030896825fd90d69baf5Peng Xu    @Override
1611cfde25e4019bc050c7f030896825fd90d69baf5Peng Xu    public void onAccuracyChanged(Sensor sensor, int accuracy) {}
1621cfde25e4019bc050c7f030896825fd90d69baf5Peng Xu    @Override
1631cfde25e4019bc050c7f030896825fd90d69baf5Peng Xu    public void onStatusChanged(String provider, int status, Bundle extras) {}
1641cfde25e4019bc050c7f030896825fd90d69baf5Peng Xu    @Override
1651cfde25e4019bc050c7f030896825fd90d69baf5Peng Xu    public void onProviderEnabled(String provider) {}
1661cfde25e4019bc050c7f030896825fd90d69baf5Peng Xu    @Override
1671cfde25e4019bc050c7f030896825fd90d69baf5Peng Xu    public void onProviderDisabled(String provider) {}
168a35b5539a95342799a18e95616c5e5751a198e4cPeng Xu
1691cfde25e4019bc050c7f030896825fd90d69baf5Peng Xu    private boolean useMockedLocation() {
1701cfde25e4019bc050c7f030896825fd90d69baf5Peng Xu        return "false".equals(System.getProperty(PROPERTY_USE_MOCKED_LOCATION, "false"));
171a35b5539a95342799a18e95616c5e5751a198e4cPeng Xu    }
172a35b5539a95342799a18e95616c5e5751a198e4cPeng Xu}
173a35b5539a95342799a18e95616c5e5751a198e4cPeng Xu
174