• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.car.VehiclePropertyIds;
23 import android.car.VehiclePropertyType;
24 import android.car.hardware.CarPropertyConfig;
25 import android.car.hardware.CarPropertyValue;
26 import android.car.hardware.property.CarPropertyManager;
27 import android.content.pm.PackageManager;
28 import android.os.Bundle;
29 import android.os.Handler;
30 import android.text.TextUtils;
31 import android.text.method.ScrollingMovementMethod;
32 import android.util.ArraySet;
33 import android.util.Log;
34 import android.util.SparseIntArray;
35 import android.view.LayoutInflater;
36 import android.view.View;
37 import android.view.ViewGroup;
38 import android.widget.TextView;
39 
40 import androidx.fragment.app.Fragment;
41 
42 import com.google.android.car.kitchensink.KitchenSinkActivity;
43 import com.google.android.car.kitchensink.R;
44 
45 import java.util.ArrayList;
46 import java.util.Arrays;
47 import java.util.HashSet;
48 import java.util.List;
49 import java.util.Map;
50 import java.util.Set;
51 import java.util.concurrent.ConcurrentHashMap;
52 
53 public class SensorsTestFragment extends Fragment {
54     private static final String TAG = "CAR.SENSOR.KS";
55     private static final boolean DBG = true;
56     private static final boolean DBG_VERBOSE = true;
57     private static final int KS_PERMISSIONS_REQUEST = 1;
58 
59     private final static String[] REQUIRED_PERMISSIONS = new String[]{
60         Manifest.permission.ACCESS_FINE_LOCATION,
61         Manifest.permission.ACCESS_COARSE_LOCATION,
62         Car.PERMISSION_MILEAGE,
63         Car.PERMISSION_ENERGY,
64         Car.PERMISSION_SPEED,
65         Car.PERMISSION_CAR_DYNAMICS_STATE
66     };
67     private static final ArraySet<Integer> SENSORS_SET = new ArraySet<>(Arrays.asList(
68             VehiclePropertyIds.PERF_VEHICLE_SPEED,
69             VehiclePropertyIds.ENGINE_RPM,
70             VehiclePropertyIds.PERF_ODOMETER,
71             VehiclePropertyIds.FUEL_LEVEL,
72             VehiclePropertyIds.FUEL_DOOR_OPEN,
73             VehiclePropertyIds.IGNITION_STATE,
74             VehiclePropertyIds.PARKING_BRAKE_ON,
75             VehiclePropertyIds.GEAR_SELECTION,
76             VehiclePropertyIds.NIGHT_MODE,
77             VehiclePropertyIds.ENV_OUTSIDE_TEMPERATURE,
78             VehiclePropertyIds.WHEEL_TICK,
79             VehiclePropertyIds.ABS_ACTIVE,
80             VehiclePropertyIds.TRACTION_CONTROL_ACTIVE,
81             VehiclePropertyIds.EV_BATTERY_LEVEL,
82             VehiclePropertyIds.EV_CHARGE_PORT_OPEN,
83             VehiclePropertyIds.EV_CHARGE_PORT_CONNECTED,
84             VehiclePropertyIds.EV_BATTERY_INSTANTANEOUS_CHARGE_RATE,
85             VehiclePropertyIds.ENGINE_OIL_LEVEL
86     ));
87     private static final SparseIntArray PROPERTY_TO_RESOURCE =
88             new SparseIntArray() {{
89                 put(VehiclePropertyIds.PERF_VEHICLE_SPEED, R.string.sensor_speed);
90                 put(VehiclePropertyIds.ENGINE_RPM, R.string.sensor_rpm);
91                 put(VehiclePropertyIds.PERF_ODOMETER, R.string.sensor_odometer);
92                 put(VehiclePropertyIds.FUEL_LEVEL, R.string.sensor_fuel_level);
93                 put(VehiclePropertyIds.FUEL_DOOR_OPEN, R.string.sensor_fuel_door_open);
94                 put(VehiclePropertyIds.IGNITION_STATE, R.string.sensor_ignition_status);
95                 put(VehiclePropertyIds.PARKING_BRAKE_ON, R.string.sensor_parking_brake);
96                 put(VehiclePropertyIds.GEAR_SELECTION, R.string.sensor_gear);
97                 put(VehiclePropertyIds.NIGHT_MODE, R.string.sensor_night);
98                 put(VehiclePropertyIds.ENV_OUTSIDE_TEMPERATURE, R.string.sensor_environment);
99                 put(VehiclePropertyIds.WHEEL_TICK, R.string.sensor_wheel_ticks);
100                 put(VehiclePropertyIds.ABS_ACTIVE, R.string.sensor_abs_is_active);
101                 put(VehiclePropertyIds.TRACTION_CONTROL_ACTIVE,
102                         R.string.sensor_traction_control_is_active);
103                 put(VehiclePropertyIds.EV_BATTERY_LEVEL, R.string.sensor_ev_battery_level);
104                 put(VehiclePropertyIds.EV_CHARGE_PORT_OPEN, R.string.sensor_ev_charge_port_is_open);
105                 put(VehiclePropertyIds.EV_CHARGE_PORT_CONNECTED,
106                         R.string.sensor_ev_charge_port_is_connected);
107                 put(VehiclePropertyIds.EV_BATTERY_INSTANTANEOUS_CHARGE_RATE,
108                         R.string.sensor_ev_charge_rate);
109                 put(VehiclePropertyIds.ENGINE_OIL_LEVEL, R.string.sensor_engine_oil_level);
110             }};
111 
112     private final CarPropertyManager.CarPropertyEventCallback mCarPropertyEventCallback =
113             new CarPropertyManager.CarPropertyEventCallback() {
114                 @Override
115                 public void onChangeEvent(CarPropertyValue value) {
116                     if (DBG_VERBOSE) {
117                         Log.v(TAG, "New car property value: " + value);
118                     }
119                     mValueMap.put(value.getPropertyId(), value);
120                     refreshCarSensorInfoText();
121                 }
122                 @Override
123                 public void onErrorEvent(int propId, int zone) {
124                     Log.e(TAG, "propId: " + propId + " zone: " + zone);
125                 }
126             };
127 
128     private final Handler mHandler = new Handler();
129     private final Map<Integer, CarPropertyValue> mValueMap = new ConcurrentHashMap<>();
130     private KitchenSinkActivity mActivity;
131     private CarPropertyManager mCarPropertyManager;
132     private LocationListeners mLocationListener;
133     private String mNaString;
134     private List<CarPropertyConfig> mCarPropertyConfigs;
135 
136     private TextView mCarSensorInfo;
137     private TextView mLocationInfo;
138     private TextView mAccelInfo;
139     private TextView mGyroInfo;
140     private TextView mMagInfo;
141     private TextView mAccelUncalInfo;
142     private TextView mGyroUncalInfo;
143     private TextView mAccelLimitedAxesInfo;
144     private TextView mGyroLimitedAxesInfo;
145     private TextView mAccelLimitedAxesUncalInfo;
146     private TextView mGyroLimitedAxesUncalInfo;
147 
148     @Nullable
149     @Override
onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState)150     public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
151                              @Nullable Bundle savedInstanceState) {
152         if (DBG) {
153             Log.i(TAG, "onCreateView");
154         }
155 
156         View view = inflater.inflate(R.layout.sensors, container, false);
157         mActivity = (KitchenSinkActivity) getHost();
158         mCarSensorInfo = (TextView) view.findViewById(R.id.car_sensor_info);
159         mCarSensorInfo.setMovementMethod(new ScrollingMovementMethod());
160         mLocationInfo = (TextView) view.findViewById(R.id.location_info);
161         mLocationInfo.setMovementMethod(new ScrollingMovementMethod());
162         mAccelInfo = (TextView) view.findViewById(R.id.accel_info);
163         mGyroInfo = (TextView) view.findViewById(R.id.gyro_info);
164         mMagInfo = (TextView) view.findViewById(R.id.mag_info);
165         mAccelUncalInfo = (TextView) view.findViewById(R.id.accel_uncal_info);
166         mGyroUncalInfo = (TextView) view.findViewById(R.id.gyro_uncal_info);
167         mAccelLimitedAxesInfo = (TextView) view.findViewById(R.id.accel_limited_axes_info);
168         mGyroLimitedAxesInfo = (TextView) view.findViewById(R.id.gyro_limited_axes_info);
169         mAccelLimitedAxesUncalInfo =
170                 (TextView) view.findViewById(R.id.accel_limited_axes_uncal_info);
171         mGyroLimitedAxesUncalInfo = (TextView) view.findViewById(R.id.gyro_limited_axes_uncal_info);
172 
173         mNaString = getContext().getString(R.string.sensor_na);
174         return view;
175     }
176 
177     @Override
onStart()178     public void onStart() {
179         super.onStart();
180         initPermissions();
181     }
182 
183     @Override
onResume()184     public void onResume() {
185         super.onResume();
186         Set<String> missingPermissions = checkExistingPermissions();
187         if (!missingPermissions.isEmpty()) {
188             Log.e(TAG, "Permissions not granted. Cannot initialize sensors. " + missingPermissions);
189             return;
190         }
191 
192         ((KitchenSinkActivity) getActivity()).requestRefreshManager(
193                 this::initSensors, new Handler(getContext().getMainLooper()));
194     }
195 
196     @Override
onPause()197     public void onPause() {
198         super.onPause();
199         if (mCarPropertyManager != null) {
200             mCarPropertyManager.unregisterCallback(mCarPropertyEventCallback);
201         }
202         if (mLocationListener != null) {
203             mLocationListener.stopListening();
204         }
205     }
206 
initSensors()207     private void initSensors() {
208         try {
209             initCarSensor();
210             initLocationSensor();
211         } catch (Exception e) {
212             Log.e(TAG, "initSensors() exception caught ", e);
213         }
214 
215     }
216 
initCarSensor()217     private void initCarSensor() {
218         if (mCarPropertyManager == null) {
219             mCarPropertyManager = ((KitchenSinkActivity) getActivity()).getPropertyManager();
220         }
221         mCarPropertyConfigs = mCarPropertyManager.getPropertyList(SENSORS_SET);
222 
223         for (CarPropertyConfig property : mCarPropertyConfigs) {
224             float rate = CarPropertyManager.SENSOR_RATE_NORMAL;
225             if (property.getChangeMode()
226                     == CarPropertyConfig.VEHICLE_PROPERTY_CHANGE_MODE_ONCHANGE) {
227                 rate = CarPropertyManager.SENSOR_RATE_ONCHANGE;
228             }
229             mCarPropertyManager.registerCallback(mCarPropertyEventCallback,
230                     property.getPropertyId(), rate);
231         }
232     }
233 
initLocationSensor()234     private void initLocationSensor() {
235         if (mLocationListener == null) {
236             mLocationListener = new LocationListeners(getContext(),
237                     new LocationInfoTextUpdateListener());
238         }
239         mLocationListener.startListening();
240     }
241 
initPermissions()242     private void initPermissions() {
243         Set<String> missingPermissions = checkExistingPermissions();
244         if (!missingPermissions.isEmpty()) {
245             requestPermissions(missingPermissions);
246         }
247     }
248 
checkExistingPermissions()249     private Set<String> checkExistingPermissions() {
250         Set<String> missingPermissions = new HashSet<String>();
251         for (String permission : REQUIRED_PERMISSIONS) {
252             if (mActivity.checkSelfPermission(permission) != PackageManager.PERMISSION_GRANTED) {
253                 missingPermissions.add(permission);
254             }
255         }
256         return missingPermissions;
257     }
258 
requestPermissions(Set<String> permissions)259     private void requestPermissions(Set<String> permissions) {
260         Log.d(TAG, "requesting additional permissions=" + permissions);
261         requestPermissions(permissions.toArray(new String[permissions.size()]),
262                 KS_PERMISSIONS_REQUEST);
263     }
264 
265     @Override
onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults)266     public void onRequestPermissionsResult(int requestCode, String[] permissions,
267             int[] grantResults) {
268         Log.d(TAG, "onRequestPermissionsResult reqCode=" + requestCode);
269     }
270 
refreshCarSensorInfoText()271     private void refreshCarSensorInfoText() {
272         String summaryString;
273         List<String> summary = formSummary();
274         summaryString = TextUtils.join("\n", summary);
275         mHandler.post(() -> mCarSensorInfo.setText(summaryString));
276     }
277 
formSummary()278     private List<String> formSummary() {
279         List<String> summary = new ArrayList<>();
280         for (CarPropertyConfig propertyConfig : mCarPropertyConfigs) {
281             int propertyId = propertyConfig.getPropertyId();
282             CarPropertyValue propertyValue = mValueMap.get(propertyId);
283             if (propertyValue != null
284                     && propertyValue.getStatus() != CarPropertyValue.STATUS_AVAILABLE) {
285                 propertyValue = null;
286             }
287             int resourceId = PROPERTY_TO_RESOURCE.get(propertyId);
288             // for wheel_tick, add the configuration.
289             if (propertyId == VehiclePropertyIds.WHEEL_TICK) {
290                 if (propertyValue != null) {
291                     Long[] wheelTickData = (Long[]) propertyValue.getValue();
292                     summary.add(getContext().getString(R.string.sensor_wheel_ticks,
293                             getTimestamp(propertyValue),
294                             wheelTickData[0],
295                             wheelTickData[1],
296                             wheelTickData[2],
297                             wheelTickData[3],
298                             wheelTickData[4]));
299                 } else {
300                     summary.add(getContext().getString(R.string.sensor_wheel_ticks,
301                             getTimestamp(propertyValue),
302                             mNaString, mNaString, mNaString, mNaString, mNaString));
303                 }
304                 List<Integer> configArray = propertyConfig.getConfigArray();
305                 summary.add(getContext().getString(R.string.sensor_wheel_ticks_cfg,
306                         configArray.get(0),
307                         configArray.get(1),
308                         configArray.get(2),
309                         configArray.get(3),
310                         configArray.get(4)));
311             } else {
312                 summary.add(getContext().getString(
313                         resourceId, getTimestamp(propertyValue),
314                         getStringOfPropertyValue(propertyValue)));
315             }
316         }
317         return summary;
318     }
319 
getTimestamp(CarPropertyValue value)320     private String getTimestamp(CarPropertyValue value) {
321         if (value == null) {
322             return mNaString;
323         }
324         return Double.toString(value.getTimestamp() / (1000L * 1000L * 1000L)) + " sec";
325     }
326 
getTimestampNow()327     private String getTimestampNow() {
328         return Double.toString(System.nanoTime() / (1000L * 1000L * 1000L)) + " sec";
329     }
330 
getStringOfPropertyValue(CarPropertyValue value)331     private String getStringOfPropertyValue(CarPropertyValue value) {
332         String defaultString = mNaString;
333         if (value != null) {
334             if (isArrayType(value.getPropertyId())) {
335                 defaultString = Arrays.toString((Object[]) value.getValue());
336             } else {
337                 defaultString = value.getValue().toString();
338             }
339         }
340         return defaultString;
341     }
342 
isArrayType(int propertyId)343     private boolean isArrayType(int propertyId) {
344         int mask = propertyId & VehiclePropertyType.MASK;
345         return mask == VehiclePropertyType.FLOAT_VEC
346                 || mask == VehiclePropertyType.INT32_VEC
347                 || mask == VehiclePropertyType.INT64_VEC;
348     }
349 
350     public class LocationInfoTextUpdateListener {
setLocationField(String value)351         public void setLocationField(String value) {
352             setTimestampedTextField(mLocationInfo, value);
353         }
354 
setAccelField(String value)355         public void setAccelField(String value) {
356             setTimestampedTextField(mAccelInfo, value);
357         }
358 
setGyroField(String value)359         public void setGyroField(String value) {
360             setTimestampedTextField(mGyroInfo, value);
361         }
362 
setMagField(String value)363         public void setMagField(String value) {
364             setTimestampedTextField(mMagInfo, value);
365         }
366 
setAccelUncalField(String value)367         public void setAccelUncalField(String value) {
368             setTimestampedTextField(mAccelUncalInfo, value);
369         }
370 
setGyroUncalField(String value)371         public void setGyroUncalField(String value) {
372             setTimestampedTextField(mGyroUncalInfo, value);
373         }
374 
setAccelLimitedAxesField(String value)375         public void setAccelLimitedAxesField(String value) {
376             setTimestampedTextField(mAccelLimitedAxesInfo, value);
377         }
378 
setGyroLimitedAxesField(String value)379         public void setGyroLimitedAxesField(String value) {
380             setTimestampedTextField(mGyroLimitedAxesInfo, value);
381         }
382 
setAccelLimitedAxesUncalField(String value)383         public void setAccelLimitedAxesUncalField(String value) {
384             setTimestampedTextField(mAccelLimitedAxesUncalInfo, value);
385         }
386 
setGyroLimitedAxesUncalField(String value)387         public void setGyroLimitedAxesUncalField(String value) {
388             setTimestampedTextField(mGyroLimitedAxesUncalInfo, value);
389         }
390 
setTimestampedTextField(TextView text, String value)391         private void setTimestampedTextField(TextView text, String value) {
392             synchronized (SensorsTestFragment.this) {
393                 text.setText(getTimestampNow() + ": " + value);
394                 Log.d(TAG, "setText: " + value);
395             }
396         }
397     }
398 }
399