1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #include <stdlib.h> 18 #include <string.h> 19 #include <timer.h> 20 #include <heap.h> 21 #include <plat/rtc.h> 22 #include <plat/syscfg.h> 23 #include <hostIntf.h> 24 #include <nanohubPacket.h> 25 #include <floatRt.h> 26 27 #include <seos.h> 28 29 #include <nanohub_math.h> 30 #include <sensors.h> 31 #include <limits.h> 32 33 #define WINDOW_ORIENTATION_APP_VERSION 2 34 35 #define LOG_TAG "[WO]" 36 37 #define LOGV(fmt, ...) do { \ 38 osLog(LOG_VERBOSE, LOG_TAG " " fmt, ##__VA_ARGS__); \ 39 } while (0); 40 41 #define LOGW(fmt, ...) do { \ 42 osLog(LOG_WARN, LOG_TAG " " fmt, ##__VA_ARGS__); \ 43 } while (0); 44 45 #define LOGI(fmt, ...) do { \ 46 osLog(LOG_INFO, LOG_TAG " " fmt, ##__VA_ARGS__); \ 47 } while (0); 48 49 #define LOGD(fmt, ...) do { \ 50 if (DBG_ENABLE) { \ 51 osLog(LOG_DEBUG, LOG_TAG " " fmt, ##__VA_ARGS__); \ 52 } \ 53 } while (0); 54 55 #define DBG_ENABLE 0 56 57 #define ACCEL_MIN_RATE_HZ SENSOR_HZ(15) // 15 HZ 58 #define ACCEL_MAX_LATENCY_NS 40000000ull // 40 ms in nsec 59 60 // all time units in usec, angles in degrees 61 #define RADIANS_TO_DEGREES (180.0f / M_PI) 62 63 #define NS2US(x) ((x) >> 10) // convert nsec to approx usec 64 65 #define PROPOSAL_MIN_SETTLE_TIME NS2US(40000000ull) // 40 ms 66 #define PROPOSAL_MAX_SETTLE_TIME NS2US(400000000ull) // 400 ms 67 #define PROPOSAL_TILT_ANGLE_KNEE 20 // 20 deg 68 #define PROPOSAL_SETTLE_TIME_SLOPE NS2US(12000000ull) // 12 ms/deg 69 70 #define PROPOSAL_MIN_TIME_SINCE_FLAT_ENDED NS2US(500000000ull) // 500 ms 71 #define PROPOSAL_MIN_TIME_SINCE_SWING_ENDED NS2US(300000000ull) // 300 ms 72 #define PROPOSAL_MIN_TIME_SINCE_ACCELERATION_ENDED NS2US(500000000ull) // 500 ms 73 74 #define FLAT_ANGLE 80 75 #define FLAT_TIME NS2US(1000000000ull) // 1 sec 76 77 #define SWING_AWAY_ANGLE_DELTA 20 78 #define SWING_TIME NS2US(300000000ull) // 300 ms 79 80 #define MAX_FILTER_DELTA_TIME NS2US(1000000000ull) // 1 sec 81 #define FILTER_TIME_CONSTANT NS2US(200000000ull) // 200 ms 82 83 #define NEAR_ZERO_MAGNITUDE 1.0f // m/s^2 84 #define ACCELERATION_TOLERANCE 4.0f 85 #define STANDARD_GRAVITY 9.8f 86 #define MIN_ACCELERATION_MAGNITUDE (STANDARD_GRAVITY - ACCELERATION_TOLERANCE) 87 #define MAX_ACCELERATION_MAGNITUDE (STANDARD_GRAVITY + ACCELERATION_TOLERANCE) 88 89 #define MAX_TILT 80 90 #define TILT_OVERHEAD_ENTER -40 91 #define TILT_OVERHEAD_EXIT -15 92 93 #define ADJACENT_ORIENTATION_ANGLE_GAP 45 94 95 // TILT_HISTORY_SIZE has to be greater than the time constant 96 // max(FLAT_TIME, SWING_TIME) multiplied by the highest accel sample rate after 97 // interpolation (1.0 / MIN_ACCEL_INTERVAL). 98 #define TILT_HISTORY_SIZE 64 99 #define TILT_REFERENCE_PERIOD NS2US(1800000000000ull) // 30 min 100 #define TILT_REFERENCE_BACKOFF NS2US(300000000000ull) // 5 min 101 102 // Allow up to 2.5x of the desired rate (ACCEL_MIN_RATE_HZ) 103 // The concerns are complexity and (not so much) the size of tilt_history. 104 #define MIN_ACCEL_INTERVAL NS2US(26666667ull) // 26.7 ms for 37.5 Hz 105 106 #define EVT_SENSOR_ACC_DATA_RDY sensorGetMyEventType(SENS_TYPE_ACCEL) 107 #define EVT_SENSOR_WIN_ORIENTATION_DATA_RDY sensorGetMyEventType(SENS_TYPE_WIN_ORIENTATION) 108 109 static int8_t Tilt_Tolerance[4][2] = { 110 /* ROTATION_0 */ { -25, 70 }, 111 /* ROTATION_90 */ { -25, 65 }, 112 /* ROTATION_180 */ { -25, 60 }, 113 /* ROTATION_270 */ { -25, 65 } 114 }; 115 116 struct WindowOrientationTask { 117 uint32_t tid; 118 uint32_t handle; 119 uint32_t accelHandle; 120 121 uint64_t last_filtered_time; 122 struct TripleAxisDataPoint last_filtered_sample; 123 124 uint64_t tilt_reference_time; 125 uint64_t accelerating_time; 126 uint64_t predicted_rotation_time; 127 uint64_t flat_time; 128 uint64_t swinging_time; 129 130 uint32_t tilt_history_time[TILT_HISTORY_SIZE]; 131 int tilt_history_index; 132 int8_t tilt_history[TILT_HISTORY_SIZE]; 133 134 int8_t current_rotation; 135 int8_t prev_valid_rotation; 136 int8_t proposed_rotation; 137 int8_t predicted_rotation; 138 139 bool flat; 140 bool swinging; 141 bool accelerating; 142 bool overhead; 143 }; 144 145 static struct WindowOrientationTask mTask; 146 147 static const struct SensorInfo mSi = 148 { 149 .sensorName = "Window Orientation", 150 .sensorType = SENS_TYPE_WIN_ORIENTATION, 151 .numAxis = NUM_AXIS_EMBEDDED, 152 .interrupt = NANOHUB_INT_NONWAKEUP, 153 .minSamples = 20 154 }; 155 156 static bool isTiltAngleAcceptable(int rotation, int8_t tilt_angle) 157 { 158 return ((tilt_angle >= Tilt_Tolerance[rotation][0]) 159 && (tilt_angle <= Tilt_Tolerance[rotation][1])); 160 } 161 162 static bool isOrientationAngleAcceptable(int current_rotation, int rotation, 163 int orientation_angle) 164 { 165 // If there is no current rotation, then there is no gap. 166 // The gap is used only to introduce hysteresis among advertised orientation 167 // changes to avoid flapping. 168 int lower_bound, upper_bound; 169 170 LOGD("current %d, new %d, orientation %d", 171 (int)current_rotation, (int)rotation, (int)orientation_angle); 172 173 if (current_rotation >= 0) { 174 // If the specified rotation is the same or is counter-clockwise 175 // adjacent to the current rotation, then we set a lower bound on the 176 // orientation angle. 177 // For example, if currentRotation is ROTATION_0 and proposed is 178 // ROTATION_90, then we want to check orientationAngle > 45 + GAP / 2. 179 if ((rotation == current_rotation) 180 || (rotation == (current_rotation + 1) % 4)) { 181 lower_bound = rotation * 90 - 45 182 + ADJACENT_ORIENTATION_ANGLE_GAP / 2; 183 if (rotation == 0) { 184 if ((orientation_angle >= 315) 185 && (orientation_angle < lower_bound + 360)) { 186 return false; 187 } 188 } else { 189 if (orientation_angle < lower_bound) { 190 return false; 191 } 192 } 193 } 194 195 // If the specified rotation is the same or is clockwise adjacent, 196 // then we set an upper bound on the orientation angle. 197 // For example, if currentRotation is ROTATION_0 and rotation is 198 // ROTATION_270, then we want to check orientationAngle < 315 - GAP / 2. 199 if ((rotation == current_rotation) 200 || (rotation == (current_rotation + 3) % 4)) { 201 upper_bound = rotation * 90 + 45 202 - ADJACENT_ORIENTATION_ANGLE_GAP / 2; 203 if (rotation == 0) { 204 if ((orientation_angle <= 45) 205 && (orientation_angle > upper_bound)) { 206 return false; 207 } 208 } else { 209 if (orientation_angle > upper_bound) { 210 return false; 211 } 212 } 213 } 214 } 215 return true; 216 } 217 218 static bool isPredictedRotationAcceptable(uint64_t now, int8_t tilt_angle) 219 { 220 // piecewise linear settle_time qualification: 221 // settle_time_needed = 222 // 1) PROPOSAL_MIN_SETTLE_TIME, for |tilt_angle| < PROPOSAL_TILT_ANGLE_KNEE. 223 // 2) linearly increasing with |tilt_angle| at slope PROPOSAL_SETTLE_TIME_SLOPE 224 // until it reaches PROPOSAL_MAX_SETTLE_TIME. 225 int abs_tilt = (tilt_angle >= 0) ? tilt_angle : -tilt_angle; 226 uint64_t settle_time_needed = PROPOSAL_MIN_SETTLE_TIME; 227 if (abs_tilt > PROPOSAL_TILT_ANGLE_KNEE) { 228 settle_time_needed += PROPOSAL_SETTLE_TIME_SLOPE 229 * (abs_tilt - PROPOSAL_TILT_ANGLE_KNEE); 230 } 231 if (settle_time_needed > PROPOSAL_MAX_SETTLE_TIME) { 232 settle_time_needed = PROPOSAL_MAX_SETTLE_TIME; 233 } 234 LOGD("settle_time_needed ~%llu (msec), settle_time ~%llu (msec)", 235 settle_time_needed >> 10, (now - mTask.predicted_rotation_time) >> 10); 236 237 // The predicted rotation must have settled long enough. 238 if (now < mTask.predicted_rotation_time + settle_time_needed) { 239 LOGD("...rejected by settle_time"); 240 return false; 241 } 242 243 // The last flat state (time since picked up) must have been sufficiently 244 // long ago. 245 if (now < mTask.flat_time + PROPOSAL_MIN_TIME_SINCE_FLAT_ENDED) { 246 LOGD("...rejected by flat_time"); 247 return false; 248 } 249 250 // The last swing state (time since last movement to put down) must have 251 // been sufficiently long ago. 252 if (now < mTask.swinging_time + PROPOSAL_MIN_TIME_SINCE_SWING_ENDED) { 253 LOGD("...rejected by swing_time"); 254 return false; 255 } 256 257 // The last acceleration state must have been sufficiently long ago. 258 if (now < mTask.accelerating_time 259 + PROPOSAL_MIN_TIME_SINCE_ACCELERATION_ENDED) { 260 LOGD("...rejected by acceleration_time"); 261 return false; 262 } 263 264 // Looks good! 265 return true; 266 } 267 268 static void clearPredictedRotation() 269 { 270 mTask.predicted_rotation = -1; 271 mTask.predicted_rotation_time = 0; 272 } 273 274 static void clearTiltHistory() 275 { 276 mTask.tilt_history_time[0] = 0; 277 mTask.tilt_history_index = 1; 278 mTask.tilt_reference_time = 0; 279 } 280 281 static void reset() 282 { 283 mTask.last_filtered_time = 0; 284 mTask.proposed_rotation = -1; 285 286 mTask.flat_time = 0; 287 mTask.flat = false; 288 289 mTask.swinging_time = 0; 290 mTask.swinging = false; 291 292 mTask.accelerating_time = 0; 293 mTask.accelerating = false; 294 295 mTask.overhead = false; 296 297 clearPredictedRotation(); 298 clearTiltHistory(); 299 } 300 301 static void updatePredictedRotation(uint64_t now, int rotation) 302 { 303 if (mTask.predicted_rotation != rotation) { 304 mTask.predicted_rotation = rotation; 305 mTask.predicted_rotation_time = now; 306 } 307 } 308 309 static bool isAccelerating(float magnitude) 310 { 311 return ((magnitude < MIN_ACCELERATION_MAGNITUDE) 312 || (magnitude > MAX_ACCELERATION_MAGNITUDE)); 313 } 314 315 static void addTiltHistoryEntry(uint64_t now, int8_t tilt) 316 { 317 uint64_t old_reference_time, delta; 318 size_t i; 319 int index; 320 321 if (mTask.tilt_reference_time == 0) { 322 // set reference_time after reset() 323 324 mTask.tilt_reference_time = now - 1; 325 } else if (mTask.tilt_reference_time + TILT_REFERENCE_PERIOD < now) { 326 // uint32_t tilt_history_time[] is good up to 71 min (2^32 * 1e-6 sec). 327 // proactively shift reference_time every 30 min, 328 // all history entries are within 4.3sec interval (15Hz x 64 samples) 329 330 old_reference_time = mTask.tilt_reference_time; 331 mTask.tilt_reference_time = now - TILT_REFERENCE_BACKOFF; 332 333 delta = mTask.tilt_reference_time - old_reference_time; 334 for (i = 0; i < TILT_HISTORY_SIZE; ++i) { 335 mTask.tilt_history_time[i] = (mTask.tilt_history_time[i] > delta) 336 ? (mTask.tilt_history_time[i] - delta) : 0; 337 } 338 } 339 340 index = mTask.tilt_history_index; 341 mTask.tilt_history[index] = tilt; 342 mTask.tilt_history_time[index] = now - mTask.tilt_reference_time; 343 344 index = ((index + 1) == TILT_HISTORY_SIZE) ? 0 : (index + 1); 345 mTask.tilt_history_index = index; 346 mTask.tilt_history_time[index] = 0; 347 } 348 349 static int nextTiltHistoryIndex(int index) 350 { 351 int next = (index == 0) ? (TILT_HISTORY_SIZE - 1): (index - 1); 352 return ((mTask.tilt_history_time[next] != 0) ? next : -1); 353 } 354 355 static bool isFlat(uint64_t now) 356 { 357 int i = mTask.tilt_history_index; 358 for (; (i = nextTiltHistoryIndex(i)) >= 0;) { 359 if (mTask.tilt_history[i] < FLAT_ANGLE) { 360 break; 361 } 362 if (mTask.tilt_reference_time + mTask.tilt_history_time[i] + FLAT_TIME <= now) { 363 // Tilt has remained greater than FLAT_ANGLE for FLAT_TIME. 364 return true; 365 } 366 } 367 return false; 368 } 369 370 static bool isSwinging(uint64_t now, int8_t tilt) 371 { 372 int i = mTask.tilt_history_index; 373 for (; (i = nextTiltHistoryIndex(i)) >= 0;) { 374 if (mTask.tilt_reference_time + mTask.tilt_history_time[i] + SWING_TIME 375 < now) { 376 break; 377 } 378 if (mTask.tilt_history[i] + SWING_AWAY_ANGLE_DELTA <= tilt) { 379 // Tilted away by SWING_AWAY_ANGLE_DELTA within SWING_TIME. 380 // This is one-sided protection. No latency will be added when 381 // picking up the device and rotating. 382 return true; 383 } 384 } 385 return false; 386 } 387 388 static bool add_samples(struct TripleAxisDataEvent *ev) 389 { 390 int i, tilt_tmp; 391 int orientation_angle, nearest_rotation; 392 float x, y, z, alpha, magnitude; 393 uint64_t now_nsec = ev->referenceTime, now; 394 uint64_t then, time_delta; 395 struct TripleAxisDataPoint *last_sample; 396 size_t sampleCnt = ev->samples[0].firstSample.numSamples; 397 bool skip_sample; 398 bool accelerating, flat, swinging; 399 bool change_detected; 400 int8_t old_proposed_rotation, proposed_rotation; 401 int8_t tilt_angle; 402 403 for (i = 0; i < sampleCnt; i++) { 404 405 x = ev->samples[i].x; 406 y = ev->samples[i].y; 407 z = ev->samples[i].z; 408 409 // Apply a low-pass filter to the acceleration up vector in cartesian space. 410 // Reset the orientation listener state if the samples are too far apart in time. 411 412 now_nsec += i > 0 ? ev->samples[i].deltaTime : 0; 413 now = NS2US(now_nsec); // convert to ~usec 414 415 last_sample = &mTask.last_filtered_sample; 416 then = mTask.last_filtered_time; 417 time_delta = now - then; 418 419 if ((now < then) || (now > then + MAX_FILTER_DELTA_TIME)) { 420 reset(); 421 skip_sample = true; 422 } else { 423 // alpha is the weight on the new sample 424 alpha = floatFromUint64(time_delta) / floatFromUint64(FILTER_TIME_CONSTANT + time_delta); 425 x = alpha * (x - last_sample->x) + last_sample->x; 426 y = alpha * (y - last_sample->y) + last_sample->y; 427 z = alpha * (z - last_sample->z) + last_sample->z; 428 429 skip_sample = false; 430 } 431 432 // poor man's interpolator for reduced complexity: 433 // drop samples when input sampling rate is 2.5x higher than requested 434 if (!skip_sample && (time_delta < MIN_ACCEL_INTERVAL)) { 435 skip_sample = true; 436 } else { 437 mTask.last_filtered_time = now; 438 mTask.last_filtered_sample.x = x; 439 mTask.last_filtered_sample.y = y; 440 mTask.last_filtered_sample.z = z; 441 } 442 443 accelerating = false; 444 flat = false; 445 swinging = false; 446 447 if (!skip_sample) { 448 // Calculate the magnitude of the acceleration vector. 449 magnitude = sqrtf(x * x + y * y + z * z); 450 451 if (magnitude < NEAR_ZERO_MAGNITUDE) { 452 LOGD("Ignoring sensor data, magnitude too close to zero."); 453 clearPredictedRotation(); 454 } else { 455 // Determine whether the device appears to be undergoing 456 // external acceleration. 457 if (isAccelerating(magnitude)) { 458 accelerating = true; 459 mTask.accelerating_time = now; 460 } 461 462 // Calculate the tilt angle. 463 // This is the angle between the up vector and the x-y plane 464 // (the plane of the screen) in a range of [-90, 90] degrees. 465 // -90 degrees: screen horizontal and facing the ground (overhead) 466 // 0 degrees: screen vertical 467 // 90 degrees: screen horizontal and facing the sky (on table) 468 tilt_tmp = (int)(asinf(z / magnitude) * RADIANS_TO_DEGREES); 469 tilt_tmp = (tilt_tmp > 127) ? 127 : tilt_tmp; 470 tilt_tmp = (tilt_tmp < -128) ? -128 : tilt_tmp; 471 tilt_angle = tilt_tmp; 472 addTiltHistoryEntry(now, tilt_angle); 473 474 // Determine whether the device appears to be flat or swinging. 475 if (isFlat(now)) { 476 flat = true; 477 mTask.flat_time = now; 478 } 479 if (isSwinging(now, tilt_angle)) { 480 swinging = true; 481 mTask.swinging_time = now; 482 } 483 484 // If the tilt angle is too close to horizontal then we cannot 485 // determine the orientation angle of the screen. 486 if (tilt_angle <= TILT_OVERHEAD_ENTER) { 487 mTask.overhead = true; 488 } else if (tilt_angle >= TILT_OVERHEAD_EXIT) { 489 mTask.overhead = false; 490 } 491 492 if (mTask.overhead) { 493 LOGD("Ignoring sensor data, device is overhead: %d", (int)tilt_angle); 494 clearPredictedRotation(); 495 } else if (fabsf(tilt_angle) > MAX_TILT) { 496 LOGD("Ignoring sensor data, tilt angle too high: %d", (int)tilt_angle); 497 clearPredictedRotation(); 498 } else { 499 // Calculate the orientation angle. 500 // This is the angle between the x-y projection of the up 501 // vector onto the +y-axis, increasing clockwise in a range 502 // of [0, 360] degrees. 503 orientation_angle = (int)(-atan2f(-x, y) * RADIANS_TO_DEGREES); 504 if (orientation_angle < 0) { 505 // atan2 returns [-180, 180]; normalize to [0, 360] 506 orientation_angle += 360; 507 } 508 509 // Find the nearest rotation. 510 nearest_rotation = (orientation_angle + 45) / 90; 511 if (nearest_rotation == 4) { 512 nearest_rotation = 0; 513 } 514 // Determine the predicted orientation. 515 if (isTiltAngleAcceptable(nearest_rotation, tilt_angle) 516 && isOrientationAngleAcceptable(mTask.current_rotation, 517 nearest_rotation, 518 orientation_angle)) { 519 LOGD("Predicted: tilt %d, orientation %d, predicted %d", 520 (int)tilt_angle, (int)orientation_angle, (int)mTask.predicted_rotation); 521 updatePredictedRotation(now, nearest_rotation); 522 } else { 523 LOGD("Ignoring sensor data, no predicted rotation: " 524 "tilt %d, orientation %d", 525 (int)tilt_angle, (int)orientation_angle); 526 clearPredictedRotation(); 527 } 528 } 529 } 530 531 mTask.flat = flat; 532 mTask.swinging = swinging; 533 mTask.accelerating = accelerating; 534 535 // Determine new proposed rotation. 536 old_proposed_rotation = mTask.proposed_rotation; 537 if ((mTask.predicted_rotation < 0) 538 || isPredictedRotationAcceptable(now, tilt_angle)) { 539 540 mTask.proposed_rotation = mTask.predicted_rotation; 541 } 542 proposed_rotation = mTask.proposed_rotation; 543 544 if ((proposed_rotation != old_proposed_rotation) 545 && (proposed_rotation >= 0)) { 546 mTask.current_rotation = proposed_rotation; 547 548 change_detected = (proposed_rotation != mTask.prev_valid_rotation); 549 mTask.prev_valid_rotation = proposed_rotation; 550 551 if (change_detected) { 552 return true; 553 } 554 } 555 } 556 } 557 558 return false; 559 } 560 561 562 static bool windowOrientationPower(bool on, void *cookie) 563 { 564 if (on == false && mTask.accelHandle != 0) { 565 sensorRelease(mTask.tid, mTask.accelHandle); 566 mTask.accelHandle = 0; 567 osEventUnsubscribe(mTask.tid, EVT_SENSOR_ACC_DATA_RDY); 568 } 569 570 sensorSignalInternalEvt(mTask.handle, SENSOR_INTERNAL_EVT_POWER_STATE_CHG, on, 0); 571 572 return true; 573 } 574 575 static bool windowOrientationSetRate(uint32_t rate, uint64_t latency, void *cookie) 576 { 577 int i; 578 579 if (mTask.accelHandle == 0) { 580 for (i = 0; sensorFind(SENS_TYPE_ACCEL, i, &mTask.accelHandle) != NULL; i++) { 581 if (sensorRequest(mTask.tid, mTask.accelHandle, ACCEL_MIN_RATE_HZ, ACCEL_MAX_LATENCY_NS)) { 582 // clear hysteresis 583 mTask.current_rotation = -1; 584 mTask.prev_valid_rotation = -1; 585 reset(); 586 osEventSubscribe(mTask.tid, EVT_SENSOR_ACC_DATA_RDY); 587 break; 588 } 589 } 590 } 591 592 if (mTask.accelHandle != 0) 593 sensorSignalInternalEvt(mTask.handle, SENSOR_INTERNAL_EVT_RATE_CHG, rate, latency); 594 595 return true; 596 } 597 598 static bool windowOrientationFirmwareUpload(void *cookie) 599 { 600 sensorSignalInternalEvt(mTask.handle, SENSOR_INTERNAL_EVT_FW_STATE_CHG, 601 1, 0); 602 return true; 603 } 604 605 static bool windowOrientationFlush(void *cookie) 606 { 607 return osEnqueueEvt(sensorGetMyEventType(SENS_TYPE_WIN_ORIENTATION), SENSOR_DATA_EVENT_FLUSH, NULL); 608 } 609 610 static void windowOrientationHandleEvent(uint32_t evtType, const void* evtData) 611 { 612 struct TripleAxisDataEvent *ev; 613 union EmbeddedDataPoint sample; 614 bool rotation_changed; 615 616 if (evtData == SENSOR_DATA_EVENT_FLUSH) 617 return; 618 619 switch (evtType) { 620 case EVT_SENSOR_ACC_DATA_RDY: 621 ev = (struct TripleAxisDataEvent *)evtData; 622 rotation_changed = add_samples(ev); 623 624 if (rotation_changed) { 625 LOGV("rotation changed to: ******* %d *******\n", 626 (int)mTask.proposed_rotation); 627 628 // send a single int32 here so no memory alloc/free needed. 629 sample.idata = mTask.proposed_rotation; 630 if (!osEnqueueEvt(EVT_SENSOR_WIN_ORIENTATION_DATA_RDY, sample.vptr, NULL)) { 631 LOGW("osEnqueueEvt failure"); 632 } 633 } 634 break; 635 } 636 } 637 638 static const struct SensorOps mSops = 639 { 640 .sensorPower = windowOrientationPower, 641 .sensorFirmwareUpload = windowOrientationFirmwareUpload, 642 .sensorSetRate = windowOrientationSetRate, 643 .sensorFlush = windowOrientationFlush, 644 }; 645 646 static bool window_orientation_start(uint32_t tid) 647 { 648 mTask.tid = tid; 649 650 mTask.current_rotation = -1; 651 mTask.prev_valid_rotation = -1; 652 reset(); 653 654 mTask.handle = sensorRegister(&mSi, &mSops, NULL, true); 655 656 return true; 657 } 658 659 static void windowOrientationEnd() 660 { 661 } 662 663 INTERNAL_APP_INIT( 664 APP_ID_MAKE(NANOHUB_VENDOR_GOOGLE, 3), 665 WINDOW_ORIENTATION_APP_VERSION, 666 window_orientation_start, 667 windowOrientationEnd, 668 windowOrientationHandleEvent); 669