Home | History | Annotate | Download | only in sensor
      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.google.android.car.kitchensink.sensor;
     18 
     19 import android.Manifest;
     20 import android.annotation.Nullable;
     21 import android.car.Car;
     22 import android.content.pm.PackageManager;
     23 import android.location.Location;
     24 import android.os.Bundle;
     25 import android.os.Handler;
     26 import android.support.car.CarNotConnectedException;
     27 import android.support.car.hardware.CarSensorConfig;
     28 import android.support.car.hardware.CarSensorEvent;
     29 import android.support.car.hardware.CarSensorManager;
     30 import android.support.v4.app.Fragment;
     31 import android.text.TextUtils;
     32 import android.util.Log;
     33 import android.view.LayoutInflater;
     34 import android.view.View;
     35 import android.view.ViewGroup;
     36 import android.widget.TextView;
     37 
     38 import com.google.android.car.kitchensink.KitchenSinkActivity;
     39 import com.google.android.car.kitchensink.R;
     40 
     41 import java.text.DateFormat;
     42 import java.text.SimpleDateFormat;
     43 import java.util.ArrayList;
     44 import java.util.Date;
     45 import java.util.HashSet;
     46 import java.util.List;
     47 import java.util.Map;
     48 import java.util.Set;
     49 import java.util.concurrent.ConcurrentHashMap;
     50 
     51 public class SensorsTestFragment extends Fragment {
     52     private static final String TAG = "CAR.SENSOR.KS";
     53     private static final boolean DBG = true;
     54     private static final boolean DBG_VERBOSE = false;
     55     private static final int KS_PERMISSIONS_REQUEST = 1;
     56 
     57     private final static String[] REQUIRED_PERMISSIONS = new String[]{
     58         Manifest.permission.ACCESS_FINE_LOCATION,
     59         Manifest.permission.ACCESS_COARSE_LOCATION,
     60         Car.PERMISSION_MILEAGE,
     61         Car.PERMISSION_FUEL,
     62         Car.PERMISSION_SPEED,
     63         Car.PERMISSION_VEHICLE_DYNAMICS_STATE
     64     };
     65 
     66     private final CarSensorManager.OnSensorChangedListener mOnSensorChangedListener =
     67             new CarSensorManager.OnSensorChangedListener() {
     68                 @Override
     69                 public void onSensorChanged(CarSensorManager manager, CarSensorEvent event) {
     70                     if (DBG_VERBOSE) {
     71                         Log.v(TAG, "New car sensor event: " + event);
     72                     }
     73                     synchronized (SensorsTestFragment.this) {
     74                         mEventMap.put(event.sensorType, event);
     75                     }
     76                     refreshUi();
     77                 }
     78             };
     79     private final Handler mHandler = new Handler();
     80     private final Map<Integer, CarSensorEvent> mEventMap = new ConcurrentHashMap<>();
     81     private final DateFormat mDateFormat = SimpleDateFormat.getDateTimeInstance();
     82 
     83     private KitchenSinkActivity mActivity;
     84     private TextView mSensorInfo;
     85     private Car mCar;
     86     private CarSensorManager mSensorManager;
     87     private String mNaString;
     88     private int[] supportedSensors = new int[0];
     89     private Set<String> mActivePermissions = new HashSet<String>();
     90 
     91 
     92     @Nullable
     93     @Override
     94     public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
     95                              @Nullable Bundle savedInstanceState) {
     96         if (DBG) {
     97             Log.i(TAG, "onCreateView");
     98         }
     99 
    100         View view = inflater.inflate(R.layout.sensors, container, false);
    101         mActivity = (KitchenSinkActivity) getHost();
    102 
    103         mSensorInfo = (TextView) view.findViewById(R.id.sensor_info);
    104         mNaString = getContext().getString(R.string.sensor_na);
    105 
    106         return view;
    107     }
    108 
    109     @Override
    110     public void onResume() {
    111         super.onResume();
    112         initPermissions();
    113     }
    114 
    115     @Override
    116     public void onPause() {
    117         super.onPause();
    118         if (mSensorManager != null) {
    119             mSensorManager.removeListener(mOnSensorChangedListener);
    120         }
    121     }
    122 
    123     private void initSensors() {
    124         try {
    125             mSensorManager = (CarSensorManager)
    126                     mActivity.getCar().getCarManager(Car.SENSOR_SERVICE);
    127             supportedSensors = mSensorManager.getSupportedSensors();
    128             for (Integer sensor : supportedSensors) {
    129                 if ((sensor == CarSensorManager.SENSOR_TYPE_LOCATION
    130                      || sensor == CarSensorManager.SENSOR_TYPE_GPS_SATELLITE)
    131                     && !mActivePermissions.contains(Manifest.permission.ACCESS_FINE_LOCATION)) {
    132                     continue;
    133                 }
    134                 mSensorManager.addListener(mOnSensorChangedListener, sensor,
    135                         CarSensorManager.SENSOR_RATE_NORMAL);
    136             }
    137         } catch (CarNotConnectedException e) {
    138             Log.e(TAG, "Car not connected or not supported", e);
    139         }
    140     }
    141 
    142     private void initPermissions() {
    143         Set<String> missingPermissions = checkExistingPermissions();
    144         if (!missingPermissions.isEmpty()) {
    145             requestPermissions(missingPermissions);
    146         } else {
    147             initSensors();
    148         }
    149     }
    150 
    151     private Set<String> checkExistingPermissions() {
    152         Set<String> missingPermissions = new HashSet<String>();
    153         for (String permission : REQUIRED_PERMISSIONS) {
    154             if (mActivity.checkSelfPermission(permission)
    155                 == PackageManager.PERMISSION_GRANTED) {
    156                 mActivePermissions.add(permission);
    157             } else {
    158                 missingPermissions.add(permission);
    159             }
    160         }
    161         return missingPermissions;
    162     }
    163 
    164     private void requestPermissions(Set<String> permissions) {
    165         Log.d(TAG, "requesting additional permissions=" + permissions);
    166 
    167         requestPermissions(permissions.toArray(new String[permissions.size()]),
    168                 KS_PERMISSIONS_REQUEST);
    169     }
    170 
    171     @Override
    172     public void onRequestPermissionsResult(int requestCode, String[] permissions,
    173             int[] grantResults) {
    174         Log.d(TAG, "onRequestPermissionsResult reqCode=" + requestCode);
    175         if (KS_PERMISSIONS_REQUEST == requestCode) {
    176             for (int i=0; i<permissions.length; i++) {
    177                 if (grantResults[i] == PackageManager.PERMISSION_GRANTED) {
    178                     mActivePermissions.add(permissions[i]);
    179                 }
    180             }
    181             initSensors();
    182         }
    183     }
    184 
    185     private void refreshUi() {
    186         String summaryString;
    187         synchronized (this) {
    188             List<String> summary = new ArrayList<>();
    189             for (Integer i : supportedSensors) {
    190                 CarSensorEvent event = mEventMap.get(i);
    191                 switch (i) {
    192                     case CarSensorManager.SENSOR_TYPE_COMPASS:
    193                         summary.add(getCompassString(event));
    194                         break;
    195                     case CarSensorManager.SENSOR_TYPE_CAR_SPEED:
    196                         summary.add(getContext().getString(R.string.sensor_speed,
    197                                 getTimestamp(event),
    198                                 event == null ? mNaString : event.getCarSpeedData().carSpeed));
    199                         break;
    200                     case CarSensorManager.SENSOR_TYPE_RPM:
    201                         summary.add(getContext().getString(R.string.sensor_rpm,
    202                                 getTimestamp(event),
    203                                 event == null ? mNaString : event.getRpmData().rpm));
    204                         break;
    205                     case CarSensorManager.SENSOR_TYPE_ODOMETER:
    206                         summary.add(getContext().getString(R.string.sensor_odometer,
    207                                 getTimestamp(event),
    208                                 event == null ? mNaString : event.getOdometerData().kms));
    209                         break;
    210                     case CarSensorManager.SENSOR_TYPE_FUEL_LEVEL:
    211                         String level = mNaString;
    212                         String range = mNaString;
    213                         String lowFuelWarning = mNaString;
    214                         if (event != null) {
    215                             CarSensorEvent.FuelLevelData fuelData = event.getFuelLevelData();
    216                             level = fuelData.level == -1 ? level : String.valueOf(fuelData.level);
    217                             range = fuelData.range == -1 ? range : String.valueOf(fuelData.range);
    218                             lowFuelWarning = String.valueOf(fuelData.lowFuelWarning);
    219                         }
    220                         summary.add(getContext().getString(R.string.sensor_fuel_level,
    221                                 getTimestamp(event), level, range, lowFuelWarning));
    222                         break;
    223                     case CarSensorManager.SENSOR_TYPE_PARKING_BRAKE:
    224                         summary.add(getContext().getString(R.string.sensor_parking_brake,
    225                                 getTimestamp(event),
    226                                 event == null ? mNaString :
    227                                 event.getParkingBrakeData().isEngaged));
    228                         break;
    229                     case CarSensorManager.SENSOR_TYPE_GEAR:
    230                         summary.add(getContext().getString(R.string.sensor_gear,
    231                                 getTimestamp(event),
    232                                 event == null ? mNaString : event.getGearData().gear));
    233                         break;
    234                     case CarSensorManager.SENSOR_TYPE_NIGHT:
    235                         summary.add(getContext().getString(R.string.sensor_night,
    236                                 getTimestamp(event),
    237                                 event == null ? mNaString : event.getNightData().isNightMode));
    238                         break;
    239                     case CarSensorManager.SENSOR_TYPE_LOCATION:
    240                         summary.add(getLocationString(event));
    241                         break;
    242                     case CarSensorManager.SENSOR_TYPE_DRIVING_STATUS:
    243                         String drivingStatus = mNaString;
    244                         String binDrivingStatus = mNaString;
    245                         if (event != null) {
    246                             CarSensorEvent.DrivingStatusData drivingStatusData =
    247                                     event.getDrivingStatusData();
    248                             drivingStatus = String.valueOf(drivingStatusData.status);
    249                             binDrivingStatus = Integer.toBinaryString(drivingStatusData.status);
    250                         }
    251                         summary.add(getContext().getString(R.string.sensor_driving_status,
    252                                 getTimestamp(event), drivingStatus, binDrivingStatus));
    253                         break;
    254                     case CarSensorManager.SENSOR_TYPE_ENVIRONMENT:
    255                         String temperature = mNaString;
    256                         String pressure = mNaString;
    257                         if (event != null) {
    258                             CarSensorEvent.EnvironmentData env = event.getEnvironmentData();
    259                             temperature = Float.isNaN(env.temperature) ? temperature :
    260                                     String.valueOf(env.temperature);
    261                             pressure = Float.isNaN(env.pressure) ? pressure :
    262                                     String.valueOf(env.pressure);
    263                         }
    264                         summary.add(getContext().getString(R.string.sensor_environment,
    265                                 getTimestamp(event), temperature, pressure));
    266                         break;
    267                     case CarSensorManager.SENSOR_TYPE_ACCELEROMETER:
    268                         summary.add(getAccelerometerString(event));
    269                         break;
    270                     case CarSensorManager.SENSOR_TYPE_GPS_SATELLITE:
    271                         summary.add(getGpsSatelliteString(event));
    272                         break;
    273                     case CarSensorManager.SENSOR_TYPE_GYROSCOPE:
    274                         summary.add(getGyroscopeString(event));
    275                         break;
    276                     case CarSensorManager.SENSOR_TYPE_WHEEL_TICK_DISTANCE:
    277                         if(event != null) {
    278                             CarSensorEvent.CarWheelTickDistanceData d =
    279                                 event.getCarWheelTickDistanceData();
    280                             summary.add(getContext().getString(R.string.sensor_wheel_ticks,
    281                                 getTimestamp(event), d.sensorResetCount, d.frontLeftWheelDistanceMm,
    282                                 d.frontRightWheelDistanceMm, d.rearLeftWheelDistanceMm,
    283                                 d.rearRightWheelDistanceMm));
    284                         } else {
    285                             summary.add(getContext().getString(R.string.sensor_wheel_ticks,
    286                                 getTimestamp(event), mNaString, mNaString, mNaString, mNaString,
    287                                 mNaString));
    288                         }
    289                         // Get the config data
    290                         try {
    291                             CarSensorConfig c = mSensorManager.getSensorConfig(
    292                                 CarSensorManager.SENSOR_TYPE_WHEEL_TICK_DISTANCE);
    293                             summary.add(getContext().getString(R.string.sensor_wheel_ticks_cfg,
    294                                 c.getInt(CarSensorConfig.WHEEL_TICK_DISTANCE_SUPPORTED_WHEELS),
    295                                 c.getInt(CarSensorConfig.WHEEL_TICK_DISTANCE_FRONT_LEFT_UM_PER_TICK),
    296                                 c.getInt(CarSensorConfig.WHEEL_TICK_DISTANCE_FRONT_RIGHT_UM_PER_TICK),
    297                                 c.getInt(CarSensorConfig.WHEEL_TICK_DISTANCE_REAR_LEFT_UM_PER_TICK),
    298                                 c.getInt(CarSensorConfig.WHEEL_TICK_DISTANCE_REAR_RIGHT_UM_PER_TICK)));
    299                         } catch (CarNotConnectedException e) {
    300                             Log.e(TAG, "Car not connected or not supported", e);
    301                         }
    302                         break;
    303                     case CarSensorManager.SENSOR_TYPE_ABS_ACTIVE:
    304                         summary.add(getContext().getString(R.string.sensor_abs_is_active,
    305                             getTimestamp(event), event == null ? mNaString :
    306                             event.getCarAbsActiveData().absIsActive));
    307                         break;
    308 
    309                     case CarSensorManager.SENSOR_TYPE_TRACTION_CONTROL_ACTIVE:
    310                         summary.add(
    311                             getContext().getString(R.string.sensor_traction_control_is_active,
    312                             getTimestamp(event), event == null ? mNaString :
    313                             event.getCarTractionControlActiveData().tractionControlIsActive));
    314                         break;
    315                     default:
    316                         // Should never happen.
    317                         Log.w(TAG, "Unrecognized event type: " + i);
    318                 }
    319             }
    320             summaryString = TextUtils.join("\n", summary);
    321         }
    322         mHandler.post(new Runnable() {
    323             @Override
    324             public void run() {
    325                 mSensorInfo.setText(summaryString);
    326             }
    327         });
    328     }
    329 
    330     private String getTimestamp(CarSensorEvent event) {
    331         if (event == null) {
    332             return mNaString;
    333         }
    334         return mDateFormat.format(new Date(event.timestamp / 1000L));
    335     }
    336 
    337     private String getCompassString(CarSensorEvent event) {
    338         String bear = mNaString;
    339         String pitch = mNaString;
    340         String roll = mNaString;
    341         if (event != null) {
    342             CarSensorEvent.CompassData compass = event.getCompassData();
    343             bear = Float.isNaN(compass.bearing) ? bear : String.valueOf(compass.bearing);
    344             pitch = Float.isNaN(compass.pitch) ? pitch : String.valueOf(compass.pitch);
    345             roll = Float.isNaN(compass.roll) ? roll : String.valueOf(compass.roll);
    346         }
    347         return getContext().getString(R.string.sensor_compass,
    348                 getTimestamp(event), bear, pitch, roll);
    349     }
    350 
    351     private String getGyroscopeString(CarSensorEvent event) {
    352         String x = mNaString;
    353         String y = mNaString;
    354         String z = mNaString;
    355         if (event != null) {
    356             CarSensorEvent.GyroscopeData gyro = event.getGyroscopeData();
    357             x = Float.isNaN(gyro.x) ? x : String.valueOf(gyro.x);
    358             y = Float.isNaN(gyro.y) ? y : String.valueOf(gyro.y);
    359             z = Float.isNaN(gyro.z) ? z : String.valueOf(gyro.z);
    360         }
    361         return getContext().getString(R.string.sensor_gyroscope,
    362                 getTimestamp(event), x, y, z);
    363     }
    364 
    365     private String getAccelerometerString(CarSensorEvent event) {
    366         String x = mNaString;
    367         String y = mNaString;
    368         String z = mNaString;
    369         if (event != null) {
    370             CarSensorEvent.AccelerometerData gyro = event.getAccelerometerData();
    371             x = Float.isNaN(gyro.x) ? x : String.valueOf(gyro.x);
    372             y = Float.isNaN(gyro.y) ? y : String.valueOf(gyro.y);
    373             z = Float.isNaN(gyro.z) ? z : String.valueOf(gyro.z);
    374         }
    375         return getContext().getString(R.string.sensor_accelerometer,
    376                 getTimestamp(event), x, y, z);
    377     }
    378 
    379     private String getLocationString(CarSensorEvent event) {
    380         String lat = mNaString;
    381         String lon = mNaString;
    382         String accuracy = mNaString;
    383         String alt = mNaString;
    384         String speed = mNaString;
    385         String bearing = mNaString;
    386         if (event != null) {
    387             Location location = event.getLocation(null);
    388             lat = String.valueOf(location.getLatitude());
    389             lon = String.valueOf(location.getLongitude());
    390             accuracy = location.hasAccuracy() ? String.valueOf(location.getAccuracy()) : accuracy;
    391             alt = location.hasAltitude() ? String.valueOf(location.getAltitude()) : alt;
    392             speed = location.hasSpeed() ? String.valueOf(location.getSpeed()) : speed;
    393             bearing = location.hasBearing() ? String.valueOf(location.getBearing()) : bearing;
    394         }
    395         return getContext().getString(R.string.sensor_location,
    396                 getTimestamp(event), lat, lon, accuracy, alt, speed, bearing);
    397     }
    398 
    399     private String getGpsSatelliteString(CarSensorEvent event) {
    400         String inUse = mNaString;
    401         String inView = mNaString;
    402         String perSattelite = "";
    403         if (event != null) {
    404             CarSensorEvent.GpsSatelliteData gpsData = event.getGpsSatelliteData(true);
    405             inUse = gpsData.numberInUse != -1 ? String.valueOf(gpsData.numberInUse) : inUse;
    406             inView = gpsData.numberInView != -1 ? String.valueOf(gpsData.numberInView) : inView;
    407             List<String> perSatteliteList = new ArrayList<>();
    408             int num = gpsData.usedInFix.length;
    409             for (int i=0; i<num; i++) {
    410                 perSatteliteList.add(getContext().getString(R.string.sensor_single_gps_satellite,
    411                         i+1, gpsData.usedInFix[i], gpsData.prn[i], gpsData.snr[i],
    412                         gpsData.azimuth[i], gpsData.elevation[i]));
    413             }
    414             perSattelite = TextUtils.join(", ", perSatteliteList);
    415         }
    416         return getContext().getString(R.string.sensor_gps,
    417                 getTimestamp(event), inView, inUse, perSattelite);
    418     }
    419 }
    420