Home | History | Annotate | Download | only in server
      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 
     17 package com.android.server;
     18 
     19 import android.content.BroadcastReceiver;
     20 import android.content.Context;
     21 import android.content.Intent;
     22 import android.content.IntentFilter;
     23 import android.hardware.GeomagneticField;
     24 import android.hardware.Sensor;
     25 import android.hardware.SensorAdditionalInfo;
     26 import android.hardware.SensorEvent;
     27 import android.hardware.SensorEventListener;
     28 import android.hardware.SensorManager;
     29 import android.location.Location;
     30 import android.location.LocationListener;
     31 import android.location.LocationManager;
     32 import android.os.Bundle;
     33 import android.os.SystemClock;
     34 import android.os.SystemProperties;
     35 import android.os.UserHandle;
     36 import android.provider.Settings;
     37 import android.util.Slog;
     38 
     39 public 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