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 */ 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 */ 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 */ 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 */ 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 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 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 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 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 */ 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 */ 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 */ 283 private void startSensors() { 284 for (MonitoredSensor sensor : mSensors) { 285 sensor.startListening(); 286 } 287 } 288 289 /** 290 * Stop listening to all monitored sensors. 291 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 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 662 public void onAccuracyChanged(Sensor sensor, int accuracy) { 663 } 664 } 665 } // MonitoredSensor 666 667 /*************************************************************************** 668 * Logging wrappers 669 **************************************************************************/ 670 671 private void Loge(String log) { 672 mService.addError(log); 673 Log.e(TAG, log); 674 } 675 } 676