Roll NDK to pick std::deque patch.
[android_tools.git] / sdk / tools / apps / SdkController / src / com / android / tools / sdkcontroller / handlers / SensorChannel.java
blobffc2fd03a8f0437937ed185c4864605398f8b120
1 /*
2 * Copyright (C) 2012 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.tools.sdkcontroller.handlers;
19 import java.nio.ByteBuffer;
20 import java.util.ArrayList;
21 import java.util.List;
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;
32 import com.android.tools.sdkcontroller.lib.Channel;
33 import com.android.tools.sdkcontroller.lib.ProtocolConstants;
34 import com.android.tools.sdkcontroller.service.ControllerService;
36 /**
37 * Implements sensors emulation.
39 public class SensorChannel extends Channel {
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.
53 private long mUpdateTargetMs = 1000/20; // 20 fps in milliseconds
54 /** Accumulates average update frequency. */
55 private long mGlobalAvgUpdateMs = 0;
57 /** Array containing monitored sensors. */
58 private final List<MonitoredSensor> mSensors = new ArrayList<MonitoredSensor>();
59 /** Sensor manager. */
60 private SensorManager mSenMan;
63 * Messages exchanged with the UI.
66 /**
67 * Sensor "enabled by emulator" state has changed. Parameter {@code obj} is
68 * the {@link MonitoredSensor}.
70 public static final int SENSOR_STATE_CHANGED = 1;
71 /**
72 * Sensor display value has changed. Parameter {@code obj} is the
73 * {@link MonitoredSensor}.
75 public static final int SENSOR_DISPLAY_MODIFIED = 2;
77 /**
78 * Constructs SensorChannel instance.
80 * @param service Service context.
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);
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()));
109 * Returns the list of sensors found on the device.
110 * The list is computed once by {@link #SensorChannel(ControllerService)}.
112 * @return A non-null possibly-empty list of sensors.
114 public List<MonitoredSensor> getSensors() {
115 return mSensors;
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.
124 * @param updateTargetMs 0 to disable throttling, otherwise a > 0 millisecond minimum
125 * between sensor updates.
127 public void setUpdateTargetMs(long updateTargetMs) {
128 mUpdateTargetMs = updateTargetMs;
132 * Returns the actual average time in milliseconds between same-sensor updates.
134 * @return The actual average time in milliseconds between same-sensor updates or 0.
136 public long getActualUpdateMs() {
137 return mGlobalAvgUpdateMs;
141 * Channel abstract implementation.
145 * This method is invoked when this channel is fully connected with its
146 * counterpart in the emulator.
148 @Override
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();
156 * This method is invoked when this channel loses connection with its
157 * counterpart in the emulator.
159 @Override
160 public void onEmulatorDisconnected() {
161 // Stop sensor event callbacks.
162 stopSensors();
166 * A query has been received from the emulator.
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.
173 @Override
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);
193 // Terminating entry contains single -1 integer.
194 resp = ExpandIf(resp, 4);
195 resp.putInt(-1);
196 sendQueryResponse(query_id, resp);
197 return;
199 default:
200 Loge("Unknown query " + query_type);
201 return;
206 * A message has been received from the emulator.
208 * @param msg_type Message type.
209 * @param msg_data Packet received from the emulator.
211 @Override
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;
239 * Handles 'enable' message.
241 * @param name Emulator-friendly name of a sensor to enable, or "all" to
242 * enable all sensors.
244 private void onEnableSensor(String name) {
245 if (name.contentEquals("all")) {
246 // Enable all sensors.
247 for (MonitoredSensor sensor : mSensors) {
248 sensor.enableSensor();
250 } else {
251 // Lookup sensor by emulator-friendly name.
252 final MonitoredSensor sensor = getSensorByEFN(name);
253 if (sensor != null) {
254 sensor.enableSensor();
260 * Handles 'disable' message.
262 * @param name Emulator-friendly name of a sensor to disable, or "all" to
263 * disable all sensors.
265 private void onDisableSensor(String name) {
266 if (name.contentEquals("all")) {
267 // Disable all sensors.
268 for (MonitoredSensor sensor : mSensors) {
269 sensor.disableSensor();
271 } else {
272 // Lookup sensor by emulator-friendly name.
273 MonitoredSensor sensor = getSensorByEFN(name);
274 if (sensor != null) {
275 sensor.disableSensor();
281 * Start listening to all monitored sensors.
283 private void startSensors() {
284 for (MonitoredSensor sensor : mSensors) {
285 sensor.startListening();
290 * Stop listening to all monitored sensors.
292 private void stopSensors() {
293 for (MonitoredSensor sensor : mSensors) {
294 sensor.stopListening();
298 /***************************************************************************
299 * Internals
300 **************************************************************************/
303 * Checks if a sensor for the given type is already monitored.
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.
309 private boolean isSensorTypeAlreadyMonitored(int type) {
310 for (MonitoredSensor sensor : mSensors) {
311 if (sensor.getType() == type) {
312 return true;
315 return false;
319 * Looks up a monitored sensor by its emulator-friendly name.
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.
325 private MonitoredSensor getSensorByEFN(String name) {
326 for (MonitoredSensor sensor : mSensors) {
327 if (sensor.mEmulatorFriendlyName.contentEquals(name)) {
328 return sensor;
331 return null;
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.
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];
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.
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();
367 * Constructs MonitoredSensor instance, and register the listeners.
369 * @param sensor Sensor to monitor.
371 MonitoredSensor(Sensor sensor) {
372 mSensor = sensor;
373 mEnabledByUser = true;
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;
447 * Get name for this sensor to display.
449 * @return Name for this sensor to display.
451 public String getUiName() {
452 return mUiName;
456 * Gets current sensor value to display.
458 * @return Current sensor value to display.
460 public String getValue() {
461 if (mValue == null) {
462 float[] values = mValues;
463 mValue = String.format(mTextFmt, values[0], values[1], values[2]);
465 return mValue == null ? "??" : mValue;
469 * Checks if monitoring of this this sensor has been enabled by
470 * emulator.
472 * @return true if monitoring of this this sensor has been enabled by
473 * emulator, or false if emulator didn't enable this sensor.
475 public boolean isEnabledByEmulator() {
476 return mEnabledByEmulator;
480 * Checks if monitoring of this this sensor has been enabled by user.
482 * @return true if monitoring of this this sensor has been enabled by
483 * user, or false if user didn't enable this sensor.
485 public boolean isEnabledByUser() {
486 return mEnabledByUser;
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.
494 public void onCheckedChanged(boolean isChecked) {
495 mEnabledByUser = isChecked;
496 if (isChecked) {
497 startListening();
498 } else {
499 stopListening();
504 * Gets sensor type.
506 * @return Sensor type as one of the Sensor.TYPE_XXX constants.
508 private int getType() {
509 return mSensor.getType();
513 * Gets sensor's emulator-friendly name.
515 * @return Sensor's emulator-friendly name.
517 private String getEmulatorFriendlyName() {
518 return mEmulatorFriendlyName;
522 * Starts monitoring the sensor.
523 * NOTE: This method is called from outside of the UI thread.
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);
533 * Stops monitoring the sensor.
534 * NOTE: This method is called from outside of the UI thread.
536 private void stopListening() {
537 if (DEBUG) Log.d(TAG, "--- Sensor " + getEmulatorFriendlyName() + " is stopped.");
538 mSenMan.unregisterListener(mListener);
542 * Enables sensor events.
543 * NOTE: This method is called from outside of the UI thread.
545 private void enableSensor() {
546 if (DEBUG) Log.d(TAG, ">>> Sensor " + getEmulatorFriendlyName() + " is enabled.");
547 mEnabledByEmulator = true;
548 mValue = null;
550 Message msg = Message.obtain();
551 msg.what = SENSOR_STATE_CHANGED;
552 msg.obj = MonitoredSensor.this;
553 notifyUiHandlers(msg);
557 * Disables sensor events.
558 * NOTE: This method is called from outside of the UI thread.
560 private void disableSensor() {
561 if (DEBUG) Log.w(TAG, "<<< Sensor " + getEmulatorFriendlyName() + " is disabled.");
562 mEnabledByEmulator = false;
563 mValue = "Disabled by emulator";
565 Message msg = Message.obtain();
566 msg.what = SENSOR_STATE_CHANGED;
567 msg.obj = MonitoredSensor.this;
568 notifyUiHandlers(msg);
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);
580 * Handles "sensor changed" event.
581 * This is an implementation of the SensorEventListener interface.
583 @Override
584 public void onSensorChanged(SensorEvent event) {
585 long now = SystemClock.elapsedRealtime();
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;
596 // Format and post message for the emulator.
597 float[] values = event.values;
598 final int len = values.length;
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]);
610 postMessage(ProtocolConstants.SENSORS_SENSOR_EVENT, mChangeMsg);
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;
620 mLastUpdateTS = now;
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;
631 mLastDisplayTS = now;
633 mValues[0] = values[0];
634 if (len > 1) {
635 mValues[1] = values[1];
636 if (len > 2) {
637 mValues[2] = values[2];
640 mValue = null;
642 Message msg = Message.obtain();
643 msg.what = SENSOR_DISPLAY_MODIFIED;
644 msg.obj = MonitoredSensor.this;
645 notifyUiHandlers(msg);
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()));
658 * Handles "sensor accuracy changed" event.
659 * This is an implementation of the SensorEventListener interface.
661 @Override
662 public void onAccuracyChanged(Sensor sensor, int accuracy) {
665 } // MonitoredSensor
667 /***************************************************************************
668 * Logging wrappers
669 **************************************************************************/
671 private void Loge(String log) {
672 mService.addError(log);
673 Log.e(TAG, log);