• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2012 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5  * use this file except in compliance with the License. You may obtain a copy of
6  * 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, WITHOUT
12  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13  * License for the specific language governing permissions and limitations under
14  * the License.
15  */
16 
17 package com.android.tools.sdkcontroller.handlers;
18 
19 import java.nio.ByteBuffer;
20 import java.util.ArrayList;
21 import java.util.List;
22 
23 import android.content.Context;
24 import android.hardware.Sensor;
25 import android.hardware.SensorEvent;
26 import android.hardware.SensorEventListener;
27 import android.hardware.SensorManager;
28 import android.os.Message;
29 import android.os.SystemClock;
30 import android.util.Log;
31 
32 import com.android.tools.sdkcontroller.lib.Channel;
33 import com.android.tools.sdkcontroller.lib.ProtocolConstants;
34 import com.android.tools.sdkcontroller.service.ControllerService;
35 
36 /**
37  * Implements sensors emulation.
38  */
39 public class SensorChannel extends Channel {
40 
41     @SuppressWarnings("hiding")
42     private static String TAG = SensorChannel.class.getSimpleName();
43     @SuppressWarnings("hiding")
44     private static boolean DEBUG = false;
45     /**
46      * The target update time per sensor. Ignored if 0 or negative.
47      * Sensor updates that arrive faster than this delay are ignored.
48      * Ideally the emulator can be updated at up to 50 fps, however
49      * for average power devices something like 20 fps is more
50      * reasonable.
51      * Default value should match res/values/strings.xml > sensors_default_sample_rate.
52      */
53     private long mUpdateTargetMs = 1000/20; // 20 fps in milliseconds
54     /** Accumulates average update frequency. */
55     private long mGlobalAvgUpdateMs = 0;
56 
57     /** Array containing monitored sensors. */
58     private final List<MonitoredSensor> mSensors = new ArrayList<MonitoredSensor>();
59     /** Sensor manager. */
60     private SensorManager mSenMan;
61 
62     /*
63      * Messages exchanged with the UI.
64      */
65 
66     /**
67      * Sensor "enabled by emulator" state has changed. Parameter {@code obj} is
68      * the {@link MonitoredSensor}.
69      */
70     public static final int SENSOR_STATE_CHANGED = 1;
71     /**
72      * Sensor display value has changed. Parameter {@code obj} is the
73      * {@link MonitoredSensor}.
74      */
75     public static final int SENSOR_DISPLAY_MODIFIED = 2;
76 
77     /**
78      * Constructs SensorChannel instance.
79      *
80      * @param service Service context.
81      */
SensorChannel(ControllerService service)82     public SensorChannel(ControllerService service) {
83         super(service, Channel.SENSOR_CHANNEL);
84         mSenMan = (SensorManager) service.getSystemService(Context.SENSOR_SERVICE);
85         // Iterate through the available sensors, adding them to the array.
86         List<Sensor> sensors = mSenMan.getSensorList(Sensor.TYPE_ALL);
87         int cur_index = 0;
88         for (int n = 0; n < sensors.size(); n++) {
89             Sensor avail_sensor = sensors.get(n);
90 
91             // There can be multiple sensors of the same type. We need only one.
92             if (!isSensorTypeAlreadyMonitored(avail_sensor.getType())) {
93                 // The first sensor we've got for the given type is not
94                 // necessarily the right one. So, use the default sensor
95                 // for the given type.
96                 Sensor def_sens = mSenMan.getDefaultSensor(avail_sensor.getType());
97                 MonitoredSensor to_add = new MonitoredSensor(def_sens);
98                 cur_index++;
99                 mSensors.add(to_add);
100                 if (DEBUG)
101                     Log.d(TAG, String.format(
102                             "Monitoring sensor #%02d: Name = '%s', Type = 0x%x",
103                             cur_index, def_sens.getName(), def_sens.getType()));
104             }
105         }
106     }
107 
108     /**
109      * Returns the list of sensors found on the device.
110      * The list is computed once by {@link #SensorChannel(ControllerService)}.
111      *
112      * @return A non-null possibly-empty list of sensors.
113      */
getSensors()114     public List<MonitoredSensor> getSensors() {
115         return mSensors;
116     }
117 
118     /**
119      * Set the target update delay throttling per-sensor, in milliseconds.
120      * <p/>
121      * For example setting it to 1000/50 means that updates for a <em>given</em> sensor
122      * faster than 50 fps is discarded.
123      *
124      * @param updateTargetMs 0 to disable throttling, otherwise a > 0 millisecond minimum
125      *   between sensor updates.
126      */
setUpdateTargetMs(long updateTargetMs)127     public void setUpdateTargetMs(long updateTargetMs) {
128         mUpdateTargetMs = updateTargetMs;
129     }
130 
131     /**
132      * Returns the actual average time in milliseconds between same-sensor updates.
133      *
134      * @return The actual average time in milliseconds between same-sensor updates or 0.
135      */
getActualUpdateMs()136     public long getActualUpdateMs() {
137         return mGlobalAvgUpdateMs;
138     }
139 
140     /*
141      * Channel abstract implementation.
142      */
143 
144     /**
145      * This method is invoked when this channel is fully connected with its
146      * counterpart in the emulator.
147      */
148     @Override
onEmulatorConnected()149     public void onEmulatorConnected() {
150         // Emulation is now possible. Note though that it will start only after
151         // emulator tells us so with SENSORS_START command.
152         enable();
153     }
154 
155     /**
156      * This method is invoked when this channel loses connection with its
157      * counterpart in the emulator.
158      */
159     @Override
onEmulatorDisconnected()160     public void onEmulatorDisconnected() {
161         // Stop sensor event callbacks.
162         stopSensors();
163     }
164 
165     /**
166      * A query has been received from the emulator.
167      *
168      * @param query_id Identifies the query. This ID should be used when
169      *            replying to the query.
170      * @param query_type Query type.
171      * @param query_data Query data.
172      */
173     @Override
onEmulatorQuery(int query_id, int query_type, ByteBuffer query_data)174     public void onEmulatorQuery(int query_id, int query_type, ByteBuffer query_data) {
175         switch (query_type) {
176             case ProtocolConstants.SENSORS_QUERY_LIST:
177                 // Preallocate large response buffer.
178                 ByteBuffer resp = ByteBuffer.allocate(1024);
179                 resp.order(getEndian());
180                 // Iterate through the list of monitored sensors, dumping them
181                 // into the response buffer.
182                 for (MonitoredSensor sensor : mSensors) {
183                     // Entry for each sensor must contain:
184                     // - an integer for its ID
185                     // - a zero-terminated emulator-friendly name.
186                     final byte[] name = sensor.getEmulatorFriendlyName().getBytes();
187                     final int required_size = 4 + name.length + 1;
188                     resp = ExpandIf(resp, required_size);
189                     resp.putInt(sensor.getType());
190                     resp.put(name);
191                     resp.put((byte) 0);
192                 }
193                 // Terminating entry contains single -1 integer.
194                 resp = ExpandIf(resp, 4);
195                 resp.putInt(-1);
196                 sendQueryResponse(query_id, resp);
197                 return;
198 
199             default:
200                 Loge("Unknown query " + query_type);
201                 return;
202         }
203     }
204 
205     /**
206      * A message has been received from the emulator.
207      *
208      * @param msg_type Message type.
209      * @param msg_data Packet received from the emulator.
210      */
211     @Override
onEmulatorMessage(int msg_type, ByteBuffer msg_data)212     public void onEmulatorMessage(int msg_type, ByteBuffer msg_data) {
213         switch (msg_type) {
214             case ProtocolConstants.SENSORS_START:
215                 Log.v(TAG, "Starting sensors emulation.");
216                 startSensors();
217                 break;
218             case ProtocolConstants.SENSORS_STOP:
219                 Log.v(TAG, "Stopping sensors emulation.");
220                 stopSensors();
221                 break;
222             case ProtocolConstants.SENSORS_ENABLE:
223                 String enable_name = new String(msg_data.array());
224                 Log.v(TAG, "Enabling sensor: " + enable_name);
225                 onEnableSensor(enable_name);
226                 break;
227             case ProtocolConstants.SENSORS_DISABLE:
228                 String disable_name = new String(msg_data.array());
229                 Log.v(TAG, "Disabling sensor: " + disable_name);
230                 onDisableSensor(disable_name);
231                 break;
232             default:
233                 Loge("Unknown message type " + msg_type);
234                 break;
235         }
236     }
237 
238     /**
239      * Handles 'enable' message.
240      *
241      * @param name Emulator-friendly name of a sensor to enable, or "all" to
242      *            enable all sensors.
243      */
onEnableSensor(String name)244     private void onEnableSensor(String name) {
245         if (name.contentEquals("all")) {
246             // Enable all sensors.
247             for (MonitoredSensor sensor : mSensors) {
248                 sensor.enableSensor();
249             }
250         } else {
251             // Lookup sensor by emulator-friendly name.
252             final MonitoredSensor sensor = getSensorByEFN(name);
253             if (sensor != null) {
254                 sensor.enableSensor();
255             }
256         }
257     }
258 
259     /**
260      * Handles 'disable' message.
261      *
262      * @param name Emulator-friendly name of a sensor to disable, or "all" to
263      *            disable all sensors.
264      */
onDisableSensor(String name)265     private void onDisableSensor(String name) {
266         if (name.contentEquals("all")) {
267             // Disable all sensors.
268             for (MonitoredSensor sensor : mSensors) {
269                 sensor.disableSensor();
270             }
271         } else {
272             // Lookup sensor by emulator-friendly name.
273             MonitoredSensor sensor = getSensorByEFN(name);
274             if (sensor != null) {
275                 sensor.disableSensor();
276             }
277         }
278     }
279 
280     /**
281      * Start listening to all monitored sensors.
282      */
startSensors()283     private void startSensors() {
284         for (MonitoredSensor sensor : mSensors) {
285             sensor.startListening();
286         }
287     }
288 
289     /**
290      * Stop listening to all monitored sensors.
291      */
stopSensors()292     private void stopSensors() {
293         for (MonitoredSensor sensor : mSensors) {
294             sensor.stopListening();
295         }
296     }
297 
298     /***************************************************************************
299      * Internals
300      **************************************************************************/
301 
302     /**
303      * Checks if a sensor for the given type is already monitored.
304      *
305      * @param type Sensor type (one of the Sensor.TYPE_XXX constants)
306      * @return true if a sensor for the given type is already monitored, or
307      *         false if the sensor is not monitored.
308      */
isSensorTypeAlreadyMonitored(int type)309     private boolean isSensorTypeAlreadyMonitored(int type) {
310         for (MonitoredSensor sensor : mSensors) {
311             if (sensor.getType() == type) {
312                 return true;
313             }
314         }
315         return false;
316     }
317 
318     /**
319      * Looks up a monitored sensor by its emulator-friendly name.
320      *
321      * @param name Emulator-friendly name to look up the monitored sensor for.
322      * @return Monitored sensor for the fiven name, or null if sensor was not
323      *         found.
324      */
getSensorByEFN(String name)325     private MonitoredSensor getSensorByEFN(String name) {
326         for (MonitoredSensor sensor : mSensors) {
327             if (sensor.mEmulatorFriendlyName.contentEquals(name)) {
328                 return sensor;
329             }
330         }
331         return null;
332     }
333 
334     /**
335      * Encapsulates a sensor that is being monitored. To monitor sensor changes
336      * each monitored sensor registers with sensor manager as a sensor listener.
337      * To control sensor monitoring from the UI, each monitored sensor has two
338      * UI controls associated with it: - A check box (named after sensor) that
339      * can be used to enable, or disable listening to the sensor changes. - A
340      * text view where current sensor value is displayed.
341      */
342     public class MonitoredSensor {
343         /** Sensor to monitor. */
344         private final Sensor mSensor;
345         /** The sensor name to display in the UI. */
346         private String mUiName = "";
347         /** Text view displaying the value of the sensor. */
348         private String mValue = null;
349         /** Emulator-friendly name for the sensor. */
350         private String mEmulatorFriendlyName;
351         /** Formats string to show in the TextView. */
352         private String mTextFmt;
353         /** Sensor values. */
354         private float[] mValues = new float[3];
355         /**
356          * Enabled state. This state is controlled by the emulator, that
357          * maintains its own list of sensors. So, if a sensor is missing, or is
358          * disabled in the emulator, it should be disabled in this application.
359          */
360         private boolean mEnabledByEmulator = false;
361         /** User-controlled enabled state. */
362         private boolean mEnabledByUser = true;
363         /** Sensor event listener for this sensor. */
364         private final OurSensorEventListener mListener = new OurSensorEventListener();
365 
366         /**
367          * Constructs MonitoredSensor instance, and register the listeners.
368          *
369          * @param sensor Sensor to monitor.
370          */
MonitoredSensor(Sensor sensor)371         MonitoredSensor(Sensor sensor) {
372             mSensor = sensor;
373             mEnabledByUser = true;
374 
375             // Set appropriate sensor name depending on the type. Unfortunately,
376             // we can't really use sensor.getName() here, since the value it
377             // returns (although resembles the purpose) is a bit vaguer than it
378             // should be. Also choose an appropriate format for the strings that
379             // display sensor's value.
380             switch (sensor.getType()) {
381                 case Sensor.TYPE_ACCELEROMETER:
382                     mUiName = "Accelerometer";
383                     mTextFmt = "%+.2f %+.2f %+.2f";
384                     mEmulatorFriendlyName = "acceleration";
385                     break;
386                 case 9: // Sensor.TYPE_GRAVITY is missing in API 7
387                     mUiName = "Gravity";
388                     mTextFmt = "%+.2f %+.2f %+.2f";
389                     mEmulatorFriendlyName = "gravity";
390                     break;
391                 case Sensor.TYPE_GYROSCOPE:
392                     mUiName = "Gyroscope";
393                     mTextFmt = "%+.2f %+.2f %+.2f";
394                     mEmulatorFriendlyName = "gyroscope";
395                     break;
396                 case Sensor.TYPE_LIGHT:
397                     mUiName = "Light";
398                     mTextFmt = "%.0f";
399                     mEmulatorFriendlyName = "light";
400                     break;
401                 case 10: // Sensor.TYPE_LINEAR_ACCELERATION is missing in API 7
402                     mUiName = "Linear acceleration";
403                     mTextFmt = "%+.2f %+.2f %+.2f";
404                     mEmulatorFriendlyName = "linear-acceleration";
405                     break;
406                 case Sensor.TYPE_MAGNETIC_FIELD:
407                     mUiName = "Magnetic field";
408                     mTextFmt = "%+.2f %+.2f %+.2f";
409                     mEmulatorFriendlyName = "magnetic-field";
410                     break;
411                 case Sensor.TYPE_ORIENTATION:
412                     mUiName = "Orientation";
413                     mTextFmt = "%+03.0f %+03.0f %+03.0f";
414                     mEmulatorFriendlyName = "orientation";
415                     break;
416                 case Sensor.TYPE_PRESSURE:
417                     mUiName = "Pressure";
418                     mTextFmt = "%.0f";
419                     mEmulatorFriendlyName = "pressure";
420                     break;
421                 case Sensor.TYPE_PROXIMITY:
422                     mUiName = "Proximity";
423                     mTextFmt = "%.0f";
424                     mEmulatorFriendlyName = "proximity";
425                     break;
426                 case 11: // Sensor.TYPE_ROTATION_VECTOR is missing in API 7
427                     mUiName = "Rotation";
428                     mTextFmt = "%+.2f %+.2f %+.2f";
429                     mEmulatorFriendlyName = "rotation";
430                     break;
431                 case Sensor.TYPE_TEMPERATURE:
432                     mUiName = "Temperature";
433                     mTextFmt = "%.0f";
434                     mEmulatorFriendlyName = "temperature";
435                     break;
436                 default:
437                     mUiName = "<Unknown>";
438                     mTextFmt = "N/A";
439                     mEmulatorFriendlyName = "unknown";
440                     if (DEBUG) Loge("Unknown sensor type " + mSensor.getType() +
441                             " for sensor " + mSensor.getName());
442                     break;
443             }
444         }
445 
446         /**
447          * Get name for this sensor to display.
448          *
449          * @return Name for this sensor to display.
450          */
getUiName()451         public String getUiName() {
452             return mUiName;
453         }
454 
455         /**
456          * Gets current sensor value to display.
457          *
458          * @return Current sensor value to display.
459          */
getValue()460         public String getValue() {
461             if (mValue == null) {
462                 float[] values = mValues;
463                 mValue = String.format(mTextFmt, values[0], values[1], values[2]);
464             }
465             return mValue == null ? "??" : mValue;
466         }
467 
468         /**
469          * Checks if monitoring of this this sensor has been enabled by
470          * emulator.
471          *
472          * @return true if monitoring of this this sensor has been enabled by
473          *         emulator, or false if emulator didn't enable this sensor.
474          */
isEnabledByEmulator()475         public boolean isEnabledByEmulator() {
476             return mEnabledByEmulator;
477         }
478 
479         /**
480          * Checks if monitoring of this this sensor has been enabled by user.
481          *
482          * @return true if monitoring of this this sensor has been enabled by
483          *         user, or false if user didn't enable this sensor.
484          */
isEnabledByUser()485         public boolean isEnabledByUser() {
486             return mEnabledByUser;
487         }
488 
489         /**
490          * Handles checked state change for the associated CheckBox. If check
491          * box is checked we will register sensor change listener. If it is
492          * unchecked, we will unregister sensor change listener.
493          */
onCheckedChanged(boolean isChecked)494         public void onCheckedChanged(boolean isChecked) {
495             mEnabledByUser = isChecked;
496             if (isChecked) {
497                 startListening();
498             } else {
499                 stopListening();
500             }
501         }
502 
503         /**
504          * Gets sensor type.
505          *
506          * @return Sensor type as one of the Sensor.TYPE_XXX constants.
507          */
getType()508         private int getType() {
509             return mSensor.getType();
510         }
511 
512         /**
513          * Gets sensor's emulator-friendly name.
514          *
515          * @return Sensor's emulator-friendly name.
516          */
getEmulatorFriendlyName()517         private String getEmulatorFriendlyName() {
518             return mEmulatorFriendlyName;
519         }
520 
521         /**
522          * Starts monitoring the sensor.
523          * NOTE: This method is called from outside of the UI thread.
524          */
startListening()525         private void startListening() {
526             if (mEnabledByEmulator && mEnabledByUser) {
527                 if (DEBUG) Log.d(TAG, "+++ Sensor " + getEmulatorFriendlyName() + " is started.");
528                 mSenMan.registerListener(mListener, mSensor, SensorManager.SENSOR_DELAY_FASTEST);
529             }
530         }
531 
532         /**
533          * Stops monitoring the sensor.
534          * NOTE: This method is called from outside of the UI thread.
535          */
stopListening()536         private void stopListening() {
537             if (DEBUG) Log.d(TAG, "--- Sensor " + getEmulatorFriendlyName() + " is stopped.");
538             mSenMan.unregisterListener(mListener);
539         }
540 
541         /**
542          * Enables sensor events.
543          * NOTE: This method is called from outside of the UI thread.
544          */
enableSensor()545         private void enableSensor() {
546             if (DEBUG) Log.d(TAG, ">>> Sensor " + getEmulatorFriendlyName() + " is enabled.");
547             mEnabledByEmulator = true;
548             mValue = null;
549 
550             Message msg = Message.obtain();
551             msg.what = SENSOR_STATE_CHANGED;
552             msg.obj = MonitoredSensor.this;
553             notifyUiHandlers(msg);
554         }
555 
556         /**
557          * Disables sensor events.
558          * NOTE: This method is called from outside of the UI thread.
559          */
disableSensor()560         private void disableSensor() {
561             if (DEBUG) Log.w(TAG, "<<< Sensor " + getEmulatorFriendlyName() + " is disabled.");
562             mEnabledByEmulator = false;
563             mValue = "Disabled by emulator";
564 
565             Message msg = Message.obtain();
566             msg.what = SENSOR_STATE_CHANGED;
567             msg.obj = MonitoredSensor.this;
568             notifyUiHandlers(msg);
569         }
570 
571         private class OurSensorEventListener implements SensorEventListener {
572             /** Last update's time-stamp in local thread millisecond time. */
573             private long mLastUpdateTS = 0;
574             /** Last display update time-stamp. */
575             private long mLastDisplayTS = 0;
576             /** Preallocated buffer for change notification message. */
577             private final ByteBuffer mChangeMsg = ByteBuffer.allocate(64);
578 
579             /**
580              * Handles "sensor changed" event.
581              * This is an implementation of the SensorEventListener interface.
582              */
583             @Override
onSensorChanged(SensorEvent event)584             public void onSensorChanged(SensorEvent event) {
585                 long now = SystemClock.elapsedRealtime();
586 
587                 long deltaMs = 0;
588                 if (mLastUpdateTS != 0) {
589                     deltaMs = now - mLastUpdateTS;
590                     if (mUpdateTargetMs > 0 && deltaMs < mUpdateTargetMs) {
591                         // New sample is arriving too fast. Discard it.
592                         return;
593                     }
594                 }
595 
596                 // Format and post message for the emulator.
597                 float[] values = event.values;
598                 final int len = values.length;
599 
600                 mChangeMsg.order(getEndian());
601                 mChangeMsg.position(0);
602                 mChangeMsg.putInt(getType());
603                 mChangeMsg.putFloat(values[0]);
604                 if (len > 1) {
605                     mChangeMsg.putFloat(values[1]);
606                     if (len > 2) {
607                         mChangeMsg.putFloat(values[2]);
608                     }
609                 }
610                 postMessage(ProtocolConstants.SENSORS_SENSOR_EVENT, mChangeMsg);
611 
612                 // Computes average update time for this sensor and average globally.
613                 if (mLastUpdateTS != 0) {
614                     if (mGlobalAvgUpdateMs != 0) {
615                         mGlobalAvgUpdateMs = (mGlobalAvgUpdateMs + deltaMs) / 2;
616                     } else {
617                         mGlobalAvgUpdateMs = deltaMs;
618                     }
619                 }
620                 mLastUpdateTS = now;
621 
622                 // Update the UI for the sensor, with a static throttling of 10 fps max.
623                 if (hasUiHandler()) {
624                     if (mLastDisplayTS != 0) {
625                         long uiDeltaMs = now - mLastDisplayTS;
626                         if (uiDeltaMs < 1000 / 4 /* 4fps in ms */) {
627                             // Skip this UI update
628                             return;
629                         }
630                     }
631                     mLastDisplayTS = now;
632 
633                     mValues[0] = values[0];
634                     if (len > 1) {
635                         mValues[1] = values[1];
636                         if (len > 2) {
637                             mValues[2] = values[2];
638                         }
639                     }
640                     mValue = null;
641 
642                     Message msg = Message.obtain();
643                     msg.what = SENSOR_DISPLAY_MODIFIED;
644                     msg.obj = MonitoredSensor.this;
645                     notifyUiHandlers(msg);
646                 }
647 
648                 if (DEBUG) {
649                     long now2 = SystemClock.elapsedRealtime();
650                     long processingTimeMs = now2 - now;
651                     Log.d(TAG, String.format("glob %d - local %d > target %d - processing %d -- %s",
652                             mGlobalAvgUpdateMs, deltaMs, mUpdateTargetMs, processingTimeMs,
653                             mSensor.getName()));
654                 }
655             }
656 
657             /**
658              * Handles "sensor accuracy changed" event.
659              * This is an implementation of the SensorEventListener interface.
660              */
661             @Override
onAccuracyChanged(Sensor sensor, int accuracy)662             public void onAccuracyChanged(Sensor sensor, int accuracy) {
663             }
664         }
665     } // MonitoredSensor
666 
667     /***************************************************************************
668      * Logging wrappers
669      **************************************************************************/
670 
Loge(String log)671     private void Loge(String log) {
672         mService.addError(log);
673         Log.e(TAG, log);
674     }
675 }
676