diff options
Diffstat (limited to 'apps/SdkController/src/com/android/tools/sdkcontroller/handlers/SensorChannel.java')
-rwxr-xr-x | apps/SdkController/src/com/android/tools/sdkcontroller/handlers/SensorChannel.java | 1350 |
1 files changed, 675 insertions, 675 deletions
diff --git a/apps/SdkController/src/com/android/tools/sdkcontroller/handlers/SensorChannel.java b/apps/SdkController/src/com/android/tools/sdkcontroller/handlers/SensorChannel.java index d1ab836..ffc2fd0 100755 --- a/apps/SdkController/src/com/android/tools/sdkcontroller/handlers/SensorChannel.java +++ b/apps/SdkController/src/com/android/tools/sdkcontroller/handlers/SensorChannel.java @@ -1,675 +1,675 @@ -/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-
-package com.android.tools.sdkcontroller.handlers;
-
-import java.nio.ByteBuffer;
-import java.util.ArrayList;
-import java.util.List;
-
-import android.content.Context;
-import android.hardware.Sensor;
-import android.hardware.SensorEvent;
-import android.hardware.SensorEventListener;
-import android.hardware.SensorManager;
-import android.os.Message;
-import android.os.SystemClock;
-import android.util.Log;
-
-import com.android.tools.sdkcontroller.lib.Channel;
-import com.android.tools.sdkcontroller.lib.ProtocolConstants;
-import com.android.tools.sdkcontroller.service.ControllerService;
-
-/**
- * Implements sensors emulation.
- */
-public class SensorChannel extends Channel {
-
- @SuppressWarnings("hiding")
- private static String TAG = SensorChannel.class.getSimpleName();
- @SuppressWarnings("hiding")
- private static boolean DEBUG = false;
- /**
- * The target update time per sensor. Ignored if 0 or negative.
- * Sensor updates that arrive faster than this delay are ignored.
- * Ideally the emulator can be updated at up to 50 fps, however
- * for average power devices something like 20 fps is more
- * reasonable.
- * Default value should match res/values/strings.xml > sensors_default_sample_rate.
- */
- private long mUpdateTargetMs = 1000/20; // 20 fps in milliseconds
- /** Accumulates average update frequency. */
- private long mGlobalAvgUpdateMs = 0;
-
- /** Array containing monitored sensors. */
- private final List<MonitoredSensor> mSensors = new ArrayList<MonitoredSensor>();
- /** Sensor manager. */
- private SensorManager mSenMan;
-
- /*
- * Messages exchanged with the UI.
- */
-
- /**
- * Sensor "enabled by emulator" state has changed. Parameter {@code obj} is
- * the {@link MonitoredSensor}.
- */
- public static final int SENSOR_STATE_CHANGED = 1;
- /**
- * Sensor display value has changed. Parameter {@code obj} is the
- * {@link MonitoredSensor}.
- */
- public static final int SENSOR_DISPLAY_MODIFIED = 2;
-
- /**
- * Constructs SensorChannel instance.
- *
- * @param service Service context.
- */
- public SensorChannel(ControllerService service) {
- super(service, Channel.SENSOR_CHANNEL);
- mSenMan = (SensorManager) service.getSystemService(Context.SENSOR_SERVICE);
- // Iterate through the available sensors, adding them to the array.
- List<Sensor> sensors = mSenMan.getSensorList(Sensor.TYPE_ALL);
- int cur_index = 0;
- for (int n = 0; n < sensors.size(); n++) {
- Sensor avail_sensor = sensors.get(n);
-
- // There can be multiple sensors of the same type. We need only one.
- if (!isSensorTypeAlreadyMonitored(avail_sensor.getType())) {
- // The first sensor we've got for the given type is not
- // necessarily the right one. So, use the default sensor
- // for the given type.
- Sensor def_sens = mSenMan.getDefaultSensor(avail_sensor.getType());
- MonitoredSensor to_add = new MonitoredSensor(def_sens);
- cur_index++;
- mSensors.add(to_add);
- if (DEBUG)
- Log.d(TAG, String.format(
- "Monitoring sensor #%02d: Name = '%s', Type = 0x%x",
- cur_index, def_sens.getName(), def_sens.getType()));
- }
- }
- }
-
- /**
- * Returns the list of sensors found on the device.
- * The list is computed once by {@link #SensorChannel(ControllerService)}.
- *
- * @return A non-null possibly-empty list of sensors.
- */
- public List<MonitoredSensor> getSensors() {
- return mSensors;
- }
-
- /**
- * Set the target update delay throttling per-sensor, in milliseconds.
- * <p/>
- * For example setting it to 1000/50 means that updates for a <em>given</em> sensor
- * faster than 50 fps is discarded.
- *
- * @param updateTargetMs 0 to disable throttling, otherwise a > 0 millisecond minimum
- * between sensor updates.
- */
- public void setUpdateTargetMs(long updateTargetMs) {
- mUpdateTargetMs = updateTargetMs;
- }
-
- /**
- * Returns the actual average time in milliseconds between same-sensor updates.
- *
- * @return The actual average time in milliseconds between same-sensor updates or 0.
- */
- public long getActualUpdateMs() {
- return mGlobalAvgUpdateMs;
- }
-
- /*
- * Channel abstract implementation.
- */
-
- /**
- * This method is invoked when this channel is fully connected with its
- * counterpart in the emulator.
- */
- @Override
- public void onEmulatorConnected() {
- // Emulation is now possible. Note though that it will start only after
- // emulator tells us so with SENSORS_START command.
- enable();
- }
-
- /**
- * This method is invoked when this channel loses connection with its
- * counterpart in the emulator.
- */
- @Override
- public void onEmulatorDisconnected() {
- // Stop sensor event callbacks.
- stopSensors();
- }
-
- /**
- * A query has been received from the emulator.
- *
- * @param query_id Identifies the query. This ID should be used when
- * replying to the query.
- * @param query_type Query type.
- * @param query_data Query data.
- */
- @Override
- public void onEmulatorQuery(int query_id, int query_type, ByteBuffer query_data) {
- switch (query_type) {
- case ProtocolConstants.SENSORS_QUERY_LIST:
- // Preallocate large response buffer.
- ByteBuffer resp = ByteBuffer.allocate(1024);
- resp.order(getEndian());
- // Iterate through the list of monitored sensors, dumping them
- // into the response buffer.
- for (MonitoredSensor sensor : mSensors) {
- // Entry for each sensor must contain:
- // - an integer for its ID
- // - a zero-terminated emulator-friendly name.
- final byte[] name = sensor.getEmulatorFriendlyName().getBytes();
- final int required_size = 4 + name.length + 1;
- resp = ExpandIf(resp, required_size);
- resp.putInt(sensor.getType());
- resp.put(name);
- resp.put((byte) 0);
- }
- // Terminating entry contains single -1 integer.
- resp = ExpandIf(resp, 4);
- resp.putInt(-1);
- sendQueryResponse(query_id, resp);
- return;
-
- default:
- Loge("Unknown query " + query_type);
- return;
- }
- }
-
- /**
- * A message has been received from the emulator.
- *
- * @param msg_type Message type.
- * @param msg_data Packet received from the emulator.
- */
- @Override
- public void onEmulatorMessage(int msg_type, ByteBuffer msg_data) {
- switch (msg_type) {
- case ProtocolConstants.SENSORS_START:
- Log.v(TAG, "Starting sensors emulation.");
- startSensors();
- break;
- case ProtocolConstants.SENSORS_STOP:
- Log.v(TAG, "Stopping sensors emulation.");
- stopSensors();
- break;
- case ProtocolConstants.SENSORS_ENABLE:
- String enable_name = new String(msg_data.array());
- Log.v(TAG, "Enabling sensor: " + enable_name);
- onEnableSensor(enable_name);
- break;
- case ProtocolConstants.SENSORS_DISABLE:
- String disable_name = new String(msg_data.array());
- Log.v(TAG, "Disabling sensor: " + disable_name);
- onDisableSensor(disable_name);
- break;
- default:
- Loge("Unknown message type " + msg_type);
- break;
- }
- }
-
- /**
- * Handles 'enable' message.
- *
- * @param name Emulator-friendly name of a sensor to enable, or "all" to
- * enable all sensors.
- */
- private void onEnableSensor(String name) {
- if (name.contentEquals("all")) {
- // Enable all sensors.
- for (MonitoredSensor sensor : mSensors) {
- sensor.enableSensor();
- }
- } else {
- // Lookup sensor by emulator-friendly name.
- final MonitoredSensor sensor = getSensorByEFN(name);
- if (sensor != null) {
- sensor.enableSensor();
- }
- }
- }
-
- /**
- * Handles 'disable' message.
- *
- * @param name Emulator-friendly name of a sensor to disable, or "all" to
- * disable all sensors.
- */
- private void onDisableSensor(String name) {
- if (name.contentEquals("all")) {
- // Disable all sensors.
- for (MonitoredSensor sensor : mSensors) {
- sensor.disableSensor();
- }
- } else {
- // Lookup sensor by emulator-friendly name.
- MonitoredSensor sensor = getSensorByEFN(name);
- if (sensor != null) {
- sensor.disableSensor();
- }
- }
- }
-
- /**
- * Start listening to all monitored sensors.
- */
- private void startSensors() {
- for (MonitoredSensor sensor : mSensors) {
- sensor.startListening();
- }
- }
-
- /**
- * Stop listening to all monitored sensors.
- */
- private void stopSensors() {
- for (MonitoredSensor sensor : mSensors) {
- sensor.stopListening();
- }
- }
-
- /***************************************************************************
- * Internals
- **************************************************************************/
-
- /**
- * Checks if a sensor for the given type is already monitored.
- *
- * @param type Sensor type (one of the Sensor.TYPE_XXX constants)
- * @return true if a sensor for the given type is already monitored, or
- * false if the sensor is not monitored.
- */
- private boolean isSensorTypeAlreadyMonitored(int type) {
- for (MonitoredSensor sensor : mSensors) {
- if (sensor.getType() == type) {
- return true;
- }
- }
- return false;
- }
-
- /**
- * Looks up a monitored sensor by its emulator-friendly name.
- *
- * @param name Emulator-friendly name to look up the monitored sensor for.
- * @return Monitored sensor for the fiven name, or null if sensor was not
- * found.
- */
- private MonitoredSensor getSensorByEFN(String name) {
- for (MonitoredSensor sensor : mSensors) {
- if (sensor.mEmulatorFriendlyName.contentEquals(name)) {
- return sensor;
- }
- }
- return null;
- }
-
- /**
- * Encapsulates a sensor that is being monitored. To monitor sensor changes
- * each monitored sensor registers with sensor manager as a sensor listener.
- * To control sensor monitoring from the UI, each monitored sensor has two
- * UI controls associated with it: - A check box (named after sensor) that
- * can be used to enable, or disable listening to the sensor changes. - A
- * text view where current sensor value is displayed.
- */
- public class MonitoredSensor {
- /** Sensor to monitor. */
- private final Sensor mSensor;
- /** The sensor name to display in the UI. */
- private String mUiName = "";
- /** Text view displaying the value of the sensor. */
- private String mValue = null;
- /** Emulator-friendly name for the sensor. */
- private String mEmulatorFriendlyName;
- /** Formats string to show in the TextView. */
- private String mTextFmt;
- /** Sensor values. */
- private float[] mValues = new float[3];
- /**
- * Enabled state. This state is controlled by the emulator, that
- * maintains its own list of sensors. So, if a sensor is missing, or is
- * disabled in the emulator, it should be disabled in this application.
- */
- private boolean mEnabledByEmulator = false;
- /** User-controlled enabled state. */
- private boolean mEnabledByUser = true;
- /** Sensor event listener for this sensor. */
- private final OurSensorEventListener mListener = new OurSensorEventListener();
-
- /**
- * Constructs MonitoredSensor instance, and register the listeners.
- *
- * @param sensor Sensor to monitor.
- */
- MonitoredSensor(Sensor sensor) {
- mSensor = sensor;
- mEnabledByUser = true;
-
- // Set appropriate sensor name depending on the type. Unfortunately,
- // we can't really use sensor.getName() here, since the value it
- // returns (although resembles the purpose) is a bit vaguer than it
- // should be. Also choose an appropriate format for the strings that
- // display sensor's value.
- switch (sensor.getType()) {
- case Sensor.TYPE_ACCELEROMETER:
- mUiName = "Accelerometer";
- mTextFmt = "%+.2f %+.2f %+.2f";
- mEmulatorFriendlyName = "acceleration";
- break;
- case 9: // Sensor.TYPE_GRAVITY is missing in API 7
- mUiName = "Gravity";
- mTextFmt = "%+.2f %+.2f %+.2f";
- mEmulatorFriendlyName = "gravity";
- break;
- case Sensor.TYPE_GYROSCOPE:
- mUiName = "Gyroscope";
- mTextFmt = "%+.2f %+.2f %+.2f";
- mEmulatorFriendlyName = "gyroscope";
- break;
- case Sensor.TYPE_LIGHT:
- mUiName = "Light";
- mTextFmt = "%.0f";
- mEmulatorFriendlyName = "light";
- break;
- case 10: // Sensor.TYPE_LINEAR_ACCELERATION is missing in API 7
- mUiName = "Linear acceleration";
- mTextFmt = "%+.2f %+.2f %+.2f";
- mEmulatorFriendlyName = "linear-acceleration";
- break;
- case Sensor.TYPE_MAGNETIC_FIELD:
- mUiName = "Magnetic field";
- mTextFmt = "%+.2f %+.2f %+.2f";
- mEmulatorFriendlyName = "magnetic-field";
- break;
- case Sensor.TYPE_ORIENTATION:
- mUiName = "Orientation";
- mTextFmt = "%+03.0f %+03.0f %+03.0f";
- mEmulatorFriendlyName = "orientation";
- break;
- case Sensor.TYPE_PRESSURE:
- mUiName = "Pressure";
- mTextFmt = "%.0f";
- mEmulatorFriendlyName = "pressure";
- break;
- case Sensor.TYPE_PROXIMITY:
- mUiName = "Proximity";
- mTextFmt = "%.0f";
- mEmulatorFriendlyName = "proximity";
- break;
- case 11: // Sensor.TYPE_ROTATION_VECTOR is missing in API 7
- mUiName = "Rotation";
- mTextFmt = "%+.2f %+.2f %+.2f";
- mEmulatorFriendlyName = "rotation";
- break;
- case Sensor.TYPE_TEMPERATURE:
- mUiName = "Temperature";
- mTextFmt = "%.0f";
- mEmulatorFriendlyName = "temperature";
- break;
- default:
- mUiName = "<Unknown>";
- mTextFmt = "N/A";
- mEmulatorFriendlyName = "unknown";
- if (DEBUG) Loge("Unknown sensor type " + mSensor.getType() +
- " for sensor " + mSensor.getName());
- break;
- }
- }
-
- /**
- * Get name for this sensor to display.
- *
- * @return Name for this sensor to display.
- */
- public String getUiName() {
- return mUiName;
- }
-
- /**
- * Gets current sensor value to display.
- *
- * @return Current sensor value to display.
- */
- public String getValue() {
- if (mValue == null) {
- float[] values = mValues;
- mValue = String.format(mTextFmt, values[0], values[1], values[2]);
- }
- return mValue == null ? "??" : mValue;
- }
-
- /**
- * Checks if monitoring of this this sensor has been enabled by
- * emulator.
- *
- * @return true if monitoring of this this sensor has been enabled by
- * emulator, or false if emulator didn't enable this sensor.
- */
- public boolean isEnabledByEmulator() {
- return mEnabledByEmulator;
- }
-
- /**
- * Checks if monitoring of this this sensor has been enabled by user.
- *
- * @return true if monitoring of this this sensor has been enabled by
- * user, or false if user didn't enable this sensor.
- */
- public boolean isEnabledByUser() {
- return mEnabledByUser;
- }
-
- /**
- * Handles checked state change for the associated CheckBox. If check
- * box is checked we will register sensor change listener. If it is
- * unchecked, we will unregister sensor change listener.
- */
- public void onCheckedChanged(boolean isChecked) {
- mEnabledByUser = isChecked;
- if (isChecked) {
- startListening();
- } else {
- stopListening();
- }
- }
-
- /**
- * Gets sensor type.
- *
- * @return Sensor type as one of the Sensor.TYPE_XXX constants.
- */
- private int getType() {
- return mSensor.getType();
- }
-
- /**
- * Gets sensor's emulator-friendly name.
- *
- * @return Sensor's emulator-friendly name.
- */
- private String getEmulatorFriendlyName() {
- return mEmulatorFriendlyName;
- }
-
- /**
- * Starts monitoring the sensor.
- * NOTE: This method is called from outside of the UI thread.
- */
- private void startListening() {
- if (mEnabledByEmulator && mEnabledByUser) {
- if (DEBUG) Log.d(TAG, "+++ Sensor " + getEmulatorFriendlyName() + " is started.");
- mSenMan.registerListener(mListener, mSensor, SensorManager.SENSOR_DELAY_FASTEST);
- }
- }
-
- /**
- * Stops monitoring the sensor.
- * NOTE: This method is called from outside of the UI thread.
- */
- private void stopListening() {
- if (DEBUG) Log.d(TAG, "--- Sensor " + getEmulatorFriendlyName() + " is stopped.");
- mSenMan.unregisterListener(mListener);
- }
-
- /**
- * Enables sensor events.
- * NOTE: This method is called from outside of the UI thread.
- */
- private void enableSensor() {
- if (DEBUG) Log.d(TAG, ">>> Sensor " + getEmulatorFriendlyName() + " is enabled.");
- mEnabledByEmulator = true;
- mValue = null;
-
- Message msg = Message.obtain();
- msg.what = SENSOR_STATE_CHANGED;
- msg.obj = MonitoredSensor.this;
- notifyUiHandlers(msg);
- }
-
- /**
- * Disables sensor events.
- * NOTE: This method is called from outside of the UI thread.
- */
- private void disableSensor() {
- if (DEBUG) Log.w(TAG, "<<< Sensor " + getEmulatorFriendlyName() + " is disabled.");
- mEnabledByEmulator = false;
- mValue = "Disabled by emulator";
-
- Message msg = Message.obtain();
- msg.what = SENSOR_STATE_CHANGED;
- msg.obj = MonitoredSensor.this;
- notifyUiHandlers(msg);
- }
-
- private class OurSensorEventListener implements SensorEventListener {
- /** Last update's time-stamp in local thread millisecond time. */
- private long mLastUpdateTS = 0;
- /** Last display update time-stamp. */
- private long mLastDisplayTS = 0;
- /** Preallocated buffer for change notification message. */
- private final ByteBuffer mChangeMsg = ByteBuffer.allocate(64);
-
- /**
- * Handles "sensor changed" event.
- * This is an implementation of the SensorEventListener interface.
- */
- @Override
- public void onSensorChanged(SensorEvent event) {
- long now = SystemClock.elapsedRealtime();
-
- long deltaMs = 0;
- if (mLastUpdateTS != 0) {
- deltaMs = now - mLastUpdateTS;
- if (mUpdateTargetMs > 0 && deltaMs < mUpdateTargetMs) {
- // New sample is arriving too fast. Discard it.
- return;
- }
- }
-
- // Format and post message for the emulator.
- float[] values = event.values;
- final int len = values.length;
-
- mChangeMsg.order(getEndian());
- mChangeMsg.position(0);
- mChangeMsg.putInt(getType());
- mChangeMsg.putFloat(values[0]);
- if (len > 1) {
- mChangeMsg.putFloat(values[1]);
- if (len > 2) {
- mChangeMsg.putFloat(values[2]);
- }
- }
- postMessage(ProtocolConstants.SENSORS_SENSOR_EVENT, mChangeMsg);
-
- // Computes average update time for this sensor and average globally.
- if (mLastUpdateTS != 0) {
- if (mGlobalAvgUpdateMs != 0) {
- mGlobalAvgUpdateMs = (mGlobalAvgUpdateMs + deltaMs) / 2;
- } else {
- mGlobalAvgUpdateMs = deltaMs;
- }
- }
- mLastUpdateTS = now;
-
- // Update the UI for the sensor, with a static throttling of 10 fps max.
- if (hasUiHandler()) {
- if (mLastDisplayTS != 0) {
- long uiDeltaMs = now - mLastDisplayTS;
- if (uiDeltaMs < 1000 / 4 /* 4fps in ms */) {
- // Skip this UI update
- return;
- }
- }
- mLastDisplayTS = now;
-
- mValues[0] = values[0];
- if (len > 1) {
- mValues[1] = values[1];
- if (len > 2) {
- mValues[2] = values[2];
- }
- }
- mValue = null;
-
- Message msg = Message.obtain();
- msg.what = SENSOR_DISPLAY_MODIFIED;
- msg.obj = MonitoredSensor.this;
- notifyUiHandlers(msg);
- }
-
- if (DEBUG) {
- long now2 = SystemClock.elapsedRealtime();
- long processingTimeMs = now2 - now;
- Log.d(TAG, String.format("glob %d - local %d > target %d - processing %d -- %s",
- mGlobalAvgUpdateMs, deltaMs, mUpdateTargetMs, processingTimeMs,
- mSensor.getName()));
- }
- }
-
- /**
- * Handles "sensor accuracy changed" event.
- * This is an implementation of the SensorEventListener interface.
- */
- @Override
- public void onAccuracyChanged(Sensor sensor, int accuracy) {
- }
- }
- } // MonitoredSensor
-
- /***************************************************************************
- * Logging wrappers
- **************************************************************************/
-
- private void Loge(String log) {
- mService.addError(log);
- Log.e(TAG, log);
- }
-}
+/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.android.tools.sdkcontroller.handlers; + +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.List; + +import android.content.Context; +import android.hardware.Sensor; +import android.hardware.SensorEvent; +import android.hardware.SensorEventListener; +import android.hardware.SensorManager; +import android.os.Message; +import android.os.SystemClock; +import android.util.Log; + +import com.android.tools.sdkcontroller.lib.Channel; +import com.android.tools.sdkcontroller.lib.ProtocolConstants; +import com.android.tools.sdkcontroller.service.ControllerService; + +/** + * Implements sensors emulation. + */ +public class SensorChannel extends Channel { + + @SuppressWarnings("hiding") + private static String TAG = SensorChannel.class.getSimpleName(); + @SuppressWarnings("hiding") + private static boolean DEBUG = false; + /** + * The target update time per sensor. Ignored if 0 or negative. + * Sensor updates that arrive faster than this delay are ignored. + * Ideally the emulator can be updated at up to 50 fps, however + * for average power devices something like 20 fps is more + * reasonable. + * Default value should match res/values/strings.xml > sensors_default_sample_rate. + */ + private long mUpdateTargetMs = 1000/20; // 20 fps in milliseconds + /** Accumulates average update frequency. */ + private long mGlobalAvgUpdateMs = 0; + + /** Array containing monitored sensors. */ + private final List<MonitoredSensor> mSensors = new ArrayList<MonitoredSensor>(); + /** Sensor manager. */ + private SensorManager mSenMan; + + /* + * Messages exchanged with the UI. + */ + + /** + * Sensor "enabled by emulator" state has changed. Parameter {@code obj} is + * the {@link MonitoredSensor}. + */ + public static final int SENSOR_STATE_CHANGED = 1; + /** + * Sensor display value has changed. Parameter {@code obj} is the + * {@link MonitoredSensor}. + */ + public static final int SENSOR_DISPLAY_MODIFIED = 2; + + /** + * Constructs SensorChannel instance. + * + * @param service Service context. + */ + public SensorChannel(ControllerService service) { + super(service, Channel.SENSOR_CHANNEL); + mSenMan = (SensorManager) service.getSystemService(Context.SENSOR_SERVICE); + // Iterate through the available sensors, adding them to the array. + List<Sensor> sensors = mSenMan.getSensorList(Sensor.TYPE_ALL); + int cur_index = 0; + for (int n = 0; n < sensors.size(); n++) { + Sensor avail_sensor = sensors.get(n); + + // There can be multiple sensors of the same type. We need only one. + if (!isSensorTypeAlreadyMonitored(avail_sensor.getType())) { + // The first sensor we've got for the given type is not + // necessarily the right one. So, use the default sensor + // for the given type. + Sensor def_sens = mSenMan.getDefaultSensor(avail_sensor.getType()); + MonitoredSensor to_add = new MonitoredSensor(def_sens); + cur_index++; + mSensors.add(to_add); + if (DEBUG) + Log.d(TAG, String.format( + "Monitoring sensor #%02d: Name = '%s', Type = 0x%x", + cur_index, def_sens.getName(), def_sens.getType())); + } + } + } + + /** + * Returns the list of sensors found on the device. + * The list is computed once by {@link #SensorChannel(ControllerService)}. + * + * @return A non-null possibly-empty list of sensors. + */ + public List<MonitoredSensor> getSensors() { + return mSensors; + } + + /** + * Set the target update delay throttling per-sensor, in milliseconds. + * <p/> + * For example setting it to 1000/50 means that updates for a <em>given</em> sensor + * faster than 50 fps is discarded. + * + * @param updateTargetMs 0 to disable throttling, otherwise a > 0 millisecond minimum + * between sensor updates. + */ + public void setUpdateTargetMs(long updateTargetMs) { + mUpdateTargetMs = updateTargetMs; + } + + /** + * Returns the actual average time in milliseconds between same-sensor updates. + * + * @return The actual average time in milliseconds between same-sensor updates or 0. + */ + public long getActualUpdateMs() { + return mGlobalAvgUpdateMs; + } + + /* + * Channel abstract implementation. + */ + + /** + * This method is invoked when this channel is fully connected with its + * counterpart in the emulator. + */ + @Override + public void onEmulatorConnected() { + // Emulation is now possible. Note though that it will start only after + // emulator tells us so with SENSORS_START command. + enable(); + } + + /** + * This method is invoked when this channel loses connection with its + * counterpart in the emulator. + */ + @Override + public void onEmulatorDisconnected() { + // Stop sensor event callbacks. + stopSensors(); + } + + /** + * A query has been received from the emulator. + * + * @param query_id Identifies the query. This ID should be used when + * replying to the query. + * @param query_type Query type. + * @param query_data Query data. + */ + @Override + public void onEmulatorQuery(int query_id, int query_type, ByteBuffer query_data) { + switch (query_type) { + case ProtocolConstants.SENSORS_QUERY_LIST: + // Preallocate large response buffer. + ByteBuffer resp = ByteBuffer.allocate(1024); + resp.order(getEndian()); + // Iterate through the list of monitored sensors, dumping them + // into the response buffer. + for (MonitoredSensor sensor : mSensors) { + // Entry for each sensor must contain: + // - an integer for its ID + // - a zero-terminated emulator-friendly name. + final byte[] name = sensor.getEmulatorFriendlyName().getBytes(); + final int required_size = 4 + name.length + 1; + resp = ExpandIf(resp, required_size); + resp.putInt(sensor.getType()); + resp.put(name); + resp.put((byte) 0); + } + // Terminating entry contains single -1 integer. + resp = ExpandIf(resp, 4); + resp.putInt(-1); + sendQueryResponse(query_id, resp); + return; + + default: + Loge("Unknown query " + query_type); + return; + } + } + + /** + * A message has been received from the emulator. + * + * @param msg_type Message type. + * @param msg_data Packet received from the emulator. + */ + @Override + public void onEmulatorMessage(int msg_type, ByteBuffer msg_data) { + switch (msg_type) { + case ProtocolConstants.SENSORS_START: + Log.v(TAG, "Starting sensors emulation."); + startSensors(); + break; + case ProtocolConstants.SENSORS_STOP: + Log.v(TAG, "Stopping sensors emulation."); + stopSensors(); + break; + case ProtocolConstants.SENSORS_ENABLE: + String enable_name = new String(msg_data.array()); + Log.v(TAG, "Enabling sensor: " + enable_name); + onEnableSensor(enable_name); + break; + case ProtocolConstants.SENSORS_DISABLE: + String disable_name = new String(msg_data.array()); + Log.v(TAG, "Disabling sensor: " + disable_name); + onDisableSensor(disable_name); + break; + default: + Loge("Unknown message type " + msg_type); + break; + } + } + + /** + * Handles 'enable' message. + * + * @param name Emulator-friendly name of a sensor to enable, or "all" to + * enable all sensors. + */ + private void onEnableSensor(String name) { + if (name.contentEquals("all")) { + // Enable all sensors. + for (MonitoredSensor sensor : mSensors) { + sensor.enableSensor(); + } + } else { + // Lookup sensor by emulator-friendly name. + final MonitoredSensor sensor = getSensorByEFN(name); + if (sensor != null) { + sensor.enableSensor(); + } + } + } + + /** + * Handles 'disable' message. + * + * @param name Emulator-friendly name of a sensor to disable, or "all" to + * disable all sensors. + */ + private void onDisableSensor(String name) { + if (name.contentEquals("all")) { + // Disable all sensors. + for (MonitoredSensor sensor : mSensors) { + sensor.disableSensor(); + } + } else { + // Lookup sensor by emulator-friendly name. + MonitoredSensor sensor = getSensorByEFN(name); + if (sensor != null) { + sensor.disableSensor(); + } + } + } + + /** + * Start listening to all monitored sensors. + */ + private void startSensors() { + for (MonitoredSensor sensor : mSensors) { + sensor.startListening(); + } + } + + /** + * Stop listening to all monitored sensors. + */ + private void stopSensors() { + for (MonitoredSensor sensor : mSensors) { + sensor.stopListening(); + } + } + + /*************************************************************************** + * Internals + **************************************************************************/ + + /** + * Checks if a sensor for the given type is already monitored. + * + * @param type Sensor type (one of the Sensor.TYPE_XXX constants) + * @return true if a sensor for the given type is already monitored, or + * false if the sensor is not monitored. + */ + private boolean isSensorTypeAlreadyMonitored(int type) { + for (MonitoredSensor sensor : mSensors) { + if (sensor.getType() == type) { + return true; + } + } + return false; + } + + /** + * Looks up a monitored sensor by its emulator-friendly name. + * + * @param name Emulator-friendly name to look up the monitored sensor for. + * @return Monitored sensor for the fiven name, or null if sensor was not + * found. + */ + private MonitoredSensor getSensorByEFN(String name) { + for (MonitoredSensor sensor : mSensors) { + if (sensor.mEmulatorFriendlyName.contentEquals(name)) { + return sensor; + } + } + return null; + } + + /** + * Encapsulates a sensor that is being monitored. To monitor sensor changes + * each monitored sensor registers with sensor manager as a sensor listener. + * To control sensor monitoring from the UI, each monitored sensor has two + * UI controls associated with it: - A check box (named after sensor) that + * can be used to enable, or disable listening to the sensor changes. - A + * text view where current sensor value is displayed. + */ + public class MonitoredSensor { + /** Sensor to monitor. */ + private final Sensor mSensor; + /** The sensor name to display in the UI. */ + private String mUiName = ""; + /** Text view displaying the value of the sensor. */ + private String mValue = null; + /** Emulator-friendly name for the sensor. */ + private String mEmulatorFriendlyName; + /** Formats string to show in the TextView. */ + private String mTextFmt; + /** Sensor values. */ + private float[] mValues = new float[3]; + /** + * Enabled state. This state is controlled by the emulator, that + * maintains its own list of sensors. So, if a sensor is missing, or is + * disabled in the emulator, it should be disabled in this application. + */ + private boolean mEnabledByEmulator = false; + /** User-controlled enabled state. */ + private boolean mEnabledByUser = true; + /** Sensor event listener for this sensor. */ + private final OurSensorEventListener mListener = new OurSensorEventListener(); + + /** + * Constructs MonitoredSensor instance, and register the listeners. + * + * @param sensor Sensor to monitor. + */ + MonitoredSensor(Sensor sensor) { + mSensor = sensor; + mEnabledByUser = true; + + // Set appropriate sensor name depending on the type. Unfortunately, + // we can't really use sensor.getName() here, since the value it + // returns (although resembles the purpose) is a bit vaguer than it + // should be. Also choose an appropriate format for the strings that + // display sensor's value. + switch (sensor.getType()) { + case Sensor.TYPE_ACCELEROMETER: + mUiName = "Accelerometer"; + mTextFmt = "%+.2f %+.2f %+.2f"; + mEmulatorFriendlyName = "acceleration"; + break; + case 9: // Sensor.TYPE_GRAVITY is missing in API 7 + mUiName = "Gravity"; + mTextFmt = "%+.2f %+.2f %+.2f"; + mEmulatorFriendlyName = "gravity"; + break; + case Sensor.TYPE_GYROSCOPE: + mUiName = "Gyroscope"; + mTextFmt = "%+.2f %+.2f %+.2f"; + mEmulatorFriendlyName = "gyroscope"; + break; + case Sensor.TYPE_LIGHT: + mUiName = "Light"; + mTextFmt = "%.0f"; + mEmulatorFriendlyName = "light"; + break; + case 10: // Sensor.TYPE_LINEAR_ACCELERATION is missing in API 7 + mUiName = "Linear acceleration"; + mTextFmt = "%+.2f %+.2f %+.2f"; + mEmulatorFriendlyName = "linear-acceleration"; + break; + case Sensor.TYPE_MAGNETIC_FIELD: + mUiName = "Magnetic field"; + mTextFmt = "%+.2f %+.2f %+.2f"; + mEmulatorFriendlyName = "magnetic-field"; + break; + case Sensor.TYPE_ORIENTATION: + mUiName = "Orientation"; + mTextFmt = "%+03.0f %+03.0f %+03.0f"; + mEmulatorFriendlyName = "orientation"; + break; + case Sensor.TYPE_PRESSURE: + mUiName = "Pressure"; + mTextFmt = "%.0f"; + mEmulatorFriendlyName = "pressure"; + break; + case Sensor.TYPE_PROXIMITY: + mUiName = "Proximity"; + mTextFmt = "%.0f"; + mEmulatorFriendlyName = "proximity"; + break; + case 11: // Sensor.TYPE_ROTATION_VECTOR is missing in API 7 + mUiName = "Rotation"; + mTextFmt = "%+.2f %+.2f %+.2f"; + mEmulatorFriendlyName = "rotation"; + break; + case Sensor.TYPE_TEMPERATURE: + mUiName = "Temperature"; + mTextFmt = "%.0f"; + mEmulatorFriendlyName = "temperature"; + break; + default: + mUiName = "<Unknown>"; + mTextFmt = "N/A"; + mEmulatorFriendlyName = "unknown"; + if (DEBUG) Loge("Unknown sensor type " + mSensor.getType() + + " for sensor " + mSensor.getName()); + break; + } + } + + /** + * Get name for this sensor to display. + * + * @return Name for this sensor to display. + */ + public String getUiName() { + return mUiName; + } + + /** + * Gets current sensor value to display. + * + * @return Current sensor value to display. + */ + public String getValue() { + if (mValue == null) { + float[] values = mValues; + mValue = String.format(mTextFmt, values[0], values[1], values[2]); + } + return mValue == null ? "??" : mValue; + } + + /** + * Checks if monitoring of this this sensor has been enabled by + * emulator. + * + * @return true if monitoring of this this sensor has been enabled by + * emulator, or false if emulator didn't enable this sensor. + */ + public boolean isEnabledByEmulator() { + return mEnabledByEmulator; + } + + /** + * Checks if monitoring of this this sensor has been enabled by user. + * + * @return true if monitoring of this this sensor has been enabled by + * user, or false if user didn't enable this sensor. + */ + public boolean isEnabledByUser() { + return mEnabledByUser; + } + + /** + * Handles checked state change for the associated CheckBox. If check + * box is checked we will register sensor change listener. If it is + * unchecked, we will unregister sensor change listener. + */ + public void onCheckedChanged(boolean isChecked) { + mEnabledByUser = isChecked; + if (isChecked) { + startListening(); + } else { + stopListening(); + } + } + + /** + * Gets sensor type. + * + * @return Sensor type as one of the Sensor.TYPE_XXX constants. + */ + private int getType() { + return mSensor.getType(); + } + + /** + * Gets sensor's emulator-friendly name. + * + * @return Sensor's emulator-friendly name. + */ + private String getEmulatorFriendlyName() { + return mEmulatorFriendlyName; + } + + /** + * Starts monitoring the sensor. + * NOTE: This method is called from outside of the UI thread. + */ + private void startListening() { + if (mEnabledByEmulator && mEnabledByUser) { + if (DEBUG) Log.d(TAG, "+++ Sensor " + getEmulatorFriendlyName() + " is started."); + mSenMan.registerListener(mListener, mSensor, SensorManager.SENSOR_DELAY_FASTEST); + } + } + + /** + * Stops monitoring the sensor. + * NOTE: This method is called from outside of the UI thread. + */ + private void stopListening() { + if (DEBUG) Log.d(TAG, "--- Sensor " + getEmulatorFriendlyName() + " is stopped."); + mSenMan.unregisterListener(mListener); + } + + /** + * Enables sensor events. + * NOTE: This method is called from outside of the UI thread. + */ + private void enableSensor() { + if (DEBUG) Log.d(TAG, ">>> Sensor " + getEmulatorFriendlyName() + " is enabled."); + mEnabledByEmulator = true; + mValue = null; + + Message msg = Message.obtain(); + msg.what = SENSOR_STATE_CHANGED; + msg.obj = MonitoredSensor.this; + notifyUiHandlers(msg); + } + + /** + * Disables sensor events. + * NOTE: This method is called from outside of the UI thread. + */ + private void disableSensor() { + if (DEBUG) Log.w(TAG, "<<< Sensor " + getEmulatorFriendlyName() + " is disabled."); + mEnabledByEmulator = false; + mValue = "Disabled by emulator"; + + Message msg = Message.obtain(); + msg.what = SENSOR_STATE_CHANGED; + msg.obj = MonitoredSensor.this; + notifyUiHandlers(msg); + } + + private class OurSensorEventListener implements SensorEventListener { + /** Last update's time-stamp in local thread millisecond time. */ + private long mLastUpdateTS = 0; + /** Last display update time-stamp. */ + private long mLastDisplayTS = 0; + /** Preallocated buffer for change notification message. */ + private final ByteBuffer mChangeMsg = ByteBuffer.allocate(64); + + /** + * Handles "sensor changed" event. + * This is an implementation of the SensorEventListener interface. + */ + @Override + public void onSensorChanged(SensorEvent event) { + long now = SystemClock.elapsedRealtime(); + + long deltaMs = 0; + if (mLastUpdateTS != 0) { + deltaMs = now - mLastUpdateTS; + if (mUpdateTargetMs > 0 && deltaMs < mUpdateTargetMs) { + // New sample is arriving too fast. Discard it. + return; + } + } + + // Format and post message for the emulator. + float[] values = event.values; + final int len = values.length; + + mChangeMsg.order(getEndian()); + mChangeMsg.position(0); + mChangeMsg.putInt(getType()); + mChangeMsg.putFloat(values[0]); + if (len > 1) { + mChangeMsg.putFloat(values[1]); + if (len > 2) { + mChangeMsg.putFloat(values[2]); + } + } + postMessage(ProtocolConstants.SENSORS_SENSOR_EVENT, mChangeMsg); + + // Computes average update time for this sensor and average globally. + if (mLastUpdateTS != 0) { + if (mGlobalAvgUpdateMs != 0) { + mGlobalAvgUpdateMs = (mGlobalAvgUpdateMs + deltaMs) / 2; + } else { + mGlobalAvgUpdateMs = deltaMs; + } + } + mLastUpdateTS = now; + + // Update the UI for the sensor, with a static throttling of 10 fps max. + if (hasUiHandler()) { + if (mLastDisplayTS != 0) { + long uiDeltaMs = now - mLastDisplayTS; + if (uiDeltaMs < 1000 / 4 /* 4fps in ms */) { + // Skip this UI update + return; + } + } + mLastDisplayTS = now; + + mValues[0] = values[0]; + if (len > 1) { + mValues[1] = values[1]; + if (len > 2) { + mValues[2] = values[2]; + } + } + mValue = null; + + Message msg = Message.obtain(); + msg.what = SENSOR_DISPLAY_MODIFIED; + msg.obj = MonitoredSensor.this; + notifyUiHandlers(msg); + } + + if (DEBUG) { + long now2 = SystemClock.elapsedRealtime(); + long processingTimeMs = now2 - now; + Log.d(TAG, String.format("glob %d - local %d > target %d - processing %d -- %s", + mGlobalAvgUpdateMs, deltaMs, mUpdateTargetMs, processingTimeMs, + mSensor.getName())); + } + } + + /** + * Handles "sensor accuracy changed" event. + * This is an implementation of the SensorEventListener interface. + */ + @Override + public void onAccuracyChanged(Sensor sensor, int accuracy) { + } + } + } // MonitoredSensor + + /*************************************************************************** + * Logging wrappers + **************************************************************************/ + + private void Loge(String log) { + mService.addError(log); + Log.e(TAG, log); + } +} |