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 static java.lang.Integer.toHexString; 20 21 import android.Manifest; 22 import android.annotation.Nullable; 23 import android.car.Car; 24 import android.car.CarNotConnectedException; 25 import android.car.hardware.CarSensorConfig; 26 import android.car.hardware.CarSensorEvent; 27 import android.car.hardware.CarSensorManager; 28 import android.content.pm.PackageManager; 29 import android.os.Bundle; 30 import android.os.Handler; 31 import android.support.v4.app.Fragment; 32 import android.text.TextUtils; 33 import android.util.Log; 34 import android.view.LayoutInflater; 35 import android.view.View; 36 import android.view.ViewGroup; 37 import android.widget.TextView; 38 39 import com.google.android.car.kitchensink.KitchenSinkActivity; 40 import com.google.android.car.kitchensink.R; 41 42 import java.text.DateFormat; 43 import java.text.SimpleDateFormat; 44 import java.util.ArrayList; 45 import java.util.Date; 46 import java.util.HashSet; 47 import java.util.List; 48 import java.util.Map; 49 import java.util.Set; 50 import java.util.concurrent.ConcurrentHashMap; 51 52 public class SensorsTestFragment extends Fragment { 53 private static final String TAG = "CAR.SENSOR.KS"; 54 private static final boolean DBG = true; 55 private static final boolean DBG_VERBOSE = true; 56 private static final int KS_PERMISSIONS_REQUEST = 1; 57 58 private final static String[] REQUIRED_PERMISSIONS = new String[]{ 59 Manifest.permission.ACCESS_FINE_LOCATION, 60 Manifest.permission.ACCESS_COARSE_LOCATION, 61 Car.PERMISSION_MILEAGE, 62 Car.PERMISSION_ENERGY, 63 Car.PERMISSION_SPEED, 64 Car.PERMISSION_CAR_DYNAMICS_STATE 65 }; 66 67 private final CarSensorManager.OnSensorChangedListener mOnSensorChangedListener = 68 new CarSensorManager.OnSensorChangedListener() { 69 @Override 70 public void onSensorChanged(CarSensorEvent event) { 71 if (DBG_VERBOSE) { 72 Log.v(TAG, "New car sensor event: " + event); 73 } 74 synchronized (SensorsTestFragment.this) { 75 mEventMap.put(event.sensorType, event); 76 } 77 refreshUi(); 78 } 79 }; 80 private final Handler mHandler = new Handler(); 81 private final Map<Integer, CarSensorEvent> mEventMap = new ConcurrentHashMap<>(); 82 private final DateFormat mDateFormat = SimpleDateFormat.getDateTimeInstance(); 83 84 private KitchenSinkActivity mActivity; 85 private TextView mSensorInfo; 86 private Car mCar; 87 private CarSensorManager mSensorManager; 88 private String mNaString; 89 private int[] supportedSensors = new int[0]; 90 private Set<String> mActivePermissions = new HashSet<String>(); 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.unregisterListener(mOnSensorChangedListener); 120 } 121 } 122 123 private void initSensors() { 124 try { 125 mSensorManager = 126 (CarSensorManager) ((KitchenSinkActivity) getActivity()).getSensorManager(); 127 supportedSensors = mSensorManager.getSupportedSensors(); 128 for (Integer sensor : supportedSensors) { 129 mSensorManager.registerListener(mOnSensorChangedListener, sensor, 130 CarSensorManager.SENSOR_RATE_NORMAL); 131 } 132 } catch (CarNotConnectedException e) { 133 Log.e(TAG, "Car not connected or not supported", e); 134 } catch (Exception e) { 135 Log.e(TAG, "initSensors() exception caught: ", e); 136 } 137 } 138 139 private void initPermissions() { 140 Set<String> missingPermissions = checkExistingPermissions(); 141 if (!missingPermissions.isEmpty()) { 142 requestPermissions(missingPermissions); 143 } else { 144 initSensors(); 145 } 146 } 147 148 private Set<String> checkExistingPermissions() { 149 Set<String> missingPermissions = new HashSet<String>(); 150 for (String permission : REQUIRED_PERMISSIONS) { 151 if (mActivity.checkSelfPermission(permission) 152 == PackageManager.PERMISSION_GRANTED) { 153 mActivePermissions.add(permission); 154 } else { 155 missingPermissions.add(permission); 156 } 157 } 158 return missingPermissions; 159 } 160 161 private void requestPermissions(Set<String> permissions) { 162 Log.d(TAG, "requesting additional permissions=" + permissions); 163 164 requestPermissions(permissions.toArray(new String[permissions.size()]), 165 KS_PERMISSIONS_REQUEST); 166 } 167 168 @Override 169 public void onRequestPermissionsResult(int requestCode, String[] permissions, 170 int[] grantResults) { 171 Log.d(TAG, "onRequestPermissionsResult reqCode=" + requestCode); 172 if (KS_PERMISSIONS_REQUEST == requestCode) { 173 for (int i=0; i<permissions.length; i++) { 174 if (grantResults[i] == PackageManager.PERMISSION_GRANTED) { 175 mActivePermissions.add(permissions[i]); 176 } 177 } 178 initSensors(); 179 } 180 } 181 182 private void refreshUi() { 183 String summaryString; 184 synchronized (this) { 185 List<String> summary = new ArrayList<>(); 186 for (Integer i : supportedSensors) { 187 CarSensorEvent event = mEventMap.get(i); 188 switch (i) { 189 case CarSensorManager.SENSOR_TYPE_CAR_SPEED: 190 summary.add(getContext().getString(R.string.sensor_speed, 191 getTimestamp(event), 192 event == null ? mNaString : event.getCarSpeedData(null).carSpeed)); 193 break; 194 case CarSensorManager.SENSOR_TYPE_RPM: 195 summary.add(getContext().getString(R.string.sensor_rpm, 196 getTimestamp(event), 197 event == null ? mNaString : event.getRpmData(null).rpm)); 198 break; 199 case CarSensorManager.SENSOR_TYPE_ODOMETER: 200 summary.add(getContext().getString(R.string.sensor_odometer, 201 getTimestamp(event), 202 event == null ? mNaString : event.getOdometerData(null).kms)); 203 break; 204 case CarSensorManager.SENSOR_TYPE_FUEL_LEVEL: 205 summary.add(getFuelLevel(event)); 206 break; 207 case CarSensorManager.SENSOR_TYPE_FUEL_DOOR_OPEN: 208 summary.add(getFuelDoorOpen(event)); 209 break; 210 case CarSensorManager.SENSOR_TYPE_PARKING_BRAKE: 211 summary.add(getContext().getString(R.string.sensor_parking_brake, 212 getTimestamp(event), 213 event == null ? mNaString : 214 event.getParkingBrakeData(null).isEngaged)); 215 break; 216 case CarSensorManager.SENSOR_TYPE_GEAR: 217 summary.add(getContext().getString(R.string.sensor_gear, 218 getTimestamp(event), 219 event == null ? mNaString : event.getGearData(null).gear)); 220 break; 221 case CarSensorManager.SENSOR_TYPE_NIGHT: 222 summary.add(getContext().getString(R.string.sensor_night, 223 getTimestamp(event), 224 event == null ? mNaString : event.getNightData(null).isNightMode)); 225 break; 226 case CarSensorManager.SENSOR_TYPE_ENVIRONMENT: 227 String temperature = mNaString; 228 String pressure = mNaString; 229 if (event != null) { 230 CarSensorEvent.EnvironmentData env = event.getEnvironmentData(null); 231 temperature = Float.isNaN(env.temperature) ? temperature : 232 String.valueOf(env.temperature); 233 pressure = Float.isNaN(env.pressure) ? pressure : 234 String.valueOf(env.pressure); 235 } 236 summary.add(getContext().getString(R.string.sensor_environment, 237 getTimestamp(event), temperature, pressure)); 238 break; 239 case CarSensorManager.SENSOR_TYPE_WHEEL_TICK_DISTANCE: 240 if(event != null) { 241 CarSensorEvent.CarWheelTickDistanceData d = 242 event.getCarWheelTickDistanceData(null); 243 summary.add(getContext().getString(R.string.sensor_wheel_ticks, 244 getTimestamp(event), d.sensorResetCount, d.frontLeftWheelDistanceMm, 245 d.frontRightWheelDistanceMm, d.rearLeftWheelDistanceMm, 246 d.rearRightWheelDistanceMm)); 247 } else { 248 summary.add(getContext().getString(R.string.sensor_wheel_ticks, 249 getTimestamp(event), mNaString, mNaString, mNaString, mNaString, 250 mNaString)); 251 } 252 // Get the config data 253 try { 254 CarSensorConfig c = mSensorManager.getSensorConfig( 255 CarSensorManager.SENSOR_TYPE_WHEEL_TICK_DISTANCE); 256 summary.add(getContext().getString(R.string.sensor_wheel_ticks_cfg, 257 c.getInt(CarSensorConfig.WHEEL_TICK_DISTANCE_SUPPORTED_WHEELS), 258 c.getInt(CarSensorConfig.WHEEL_TICK_DISTANCE_FRONT_LEFT_UM_PER_TICK), 259 c.getInt(CarSensorConfig.WHEEL_TICK_DISTANCE_FRONT_RIGHT_UM_PER_TICK), 260 c.getInt(CarSensorConfig.WHEEL_TICK_DISTANCE_REAR_LEFT_UM_PER_TICK), 261 c.getInt(CarSensorConfig.WHEEL_TICK_DISTANCE_REAR_RIGHT_UM_PER_TICK))); 262 } catch (CarNotConnectedException e) { 263 Log.e(TAG, "Car not connected or not supported", e); 264 } 265 break; 266 case CarSensorManager.SENSOR_TYPE_ABS_ACTIVE: 267 summary.add(getContext().getString(R.string.sensor_abs_is_active, 268 getTimestamp(event), event == null ? mNaString : 269 event.getCarAbsActiveData(null).absIsActive)); 270 break; 271 272 case CarSensorManager.SENSOR_TYPE_TRACTION_CONTROL_ACTIVE: 273 summary.add( 274 getContext().getString(R.string.sensor_traction_control_is_active, 275 getTimestamp(event), event == null ? mNaString : 276 event.getCarTractionControlActiveData(null) 277 .tractionControlIsActive)); 278 break; 279 case CarSensorManager.SENSOR_TYPE_EV_BATTERY_LEVEL: 280 summary.add(getEvBatteryLevel(event)); 281 break; 282 case CarSensorManager.SENSOR_TYPE_EV_CHARGE_PORT_OPEN: 283 summary.add(getEvChargePortOpen(event)); 284 break; 285 case CarSensorManager.SENSOR_TYPE_EV_CHARGE_PORT_CONNECTED: 286 summary.add(getEvChargePortConnected(event)); 287 break; 288 case CarSensorManager.SENSOR_TYPE_EV_BATTERY_CHARGE_RATE: 289 summary.add(getEvChargeRate(event)); 290 break; 291 default: 292 // Should never happen. 293 Log.w(TAG, "Unrecognized event type: " + toHexString(i)); 294 } 295 } 296 summaryString = TextUtils.join("\n", summary); 297 } 298 mHandler.post(new Runnable() { 299 @Override 300 public void run() { 301 mSensorInfo.setText(summaryString); 302 } 303 }); 304 } 305 306 private String getTimestamp(CarSensorEvent event) { 307 if (event == null) { 308 return mNaString; 309 } 310 return mDateFormat.format(new Date(event.timestamp / (1000L * 1000L))); 311 } 312 313 private String getFuelLevel(CarSensorEvent event) { 314 String fuelLevel = mNaString; 315 if(event != null) { 316 fuelLevel = String.valueOf(event.getFuelLevelData(null).level); 317 } 318 return getContext().getString(R.string.sensor_fuel_level, getTimestamp(event), fuelLevel); 319 } 320 321 private String getFuelDoorOpen(CarSensorEvent event) { 322 String fuelDoorOpen = mNaString; 323 if(event != null) { 324 fuelDoorOpen = String.valueOf(event.getCarFuelDoorOpenData(null).fuelDoorIsOpen); 325 } 326 return getContext().getString(R.string.sensor_fuel_door_open, getTimestamp(event), 327 fuelDoorOpen); 328 } 329 330 private String getEvBatteryLevel(CarSensorEvent event) { 331 String evBatteryLevel = mNaString; 332 if(event != null) { 333 evBatteryLevel = String.valueOf(event.getCarEvBatteryLevelData(null).evBatteryLevel); 334 } 335 return getContext().getString(R.string.sensor_ev_battery_level, getTimestamp(event), 336 evBatteryLevel); 337 } 338 339 private String getEvChargePortOpen(CarSensorEvent event) { 340 String evChargePortOpen = mNaString; 341 if(event != null) { 342 evChargePortOpen = String.valueOf( 343 event.getCarEvChargePortOpenData(null).evChargePortIsOpen); 344 } 345 return getContext().getString(R.string.sensor_ev_charge_port_is_open, getTimestamp(event), 346 evChargePortOpen); 347 } 348 349 private String getEvChargePortConnected(CarSensorEvent event) { 350 String evChargePortConnected = mNaString; 351 if(event != null) { 352 evChargePortConnected = String.valueOf( 353 event.getCarEvChargePortConnectedData(null).evChargePortIsConnected); 354 } 355 return getContext().getString(R.string.sensor_ev_charge_port_is_connected, 356 getTimestamp(event), evChargePortConnected); 357 } 358 359 private String getEvChargeRate(CarSensorEvent event) { 360 String evChargeRate = mNaString; 361 if(event != null) { 362 evChargeRate = String.valueOf(event.getCarEvBatteryChargeRateData(null).evChargeRate); 363 } 364 return getContext().getString(R.string.sensor_ev_charge_rate, getTimestamp(event), 365 evChargeRate); 366 } 367 } 368