No Description

ui.cpp 22KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711
  1. /*
  2. * Copyright (C) 2011 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. #include "ui.h"
  17. #include <errno.h>
  18. #include <fcntl.h>
  19. #include <linux/input.h>
  20. #include <pthread.h>
  21. #include <stdarg.h>
  22. #include <stdio.h>
  23. #include <stdlib.h>
  24. #include <string.h>
  25. #include <sys/stat.h>
  26. #include <sys/time.h>
  27. #include <sys/types.h>
  28. #include <time.h>
  29. #include <unistd.h>
  30. #include <functional>
  31. #include <string>
  32. #include <android-base/file.h>
  33. #include <android-base/logging.h>
  34. #include <android-base/parseint.h>
  35. #include <android-base/properties.h>
  36. #include <android-base/strings.h>
  37. #include <cutils/android_reboot.h>
  38. #include <cutils/properties.h>
  39. #include <minui/minui.h>
  40. #include <volume_manager/VolumeManager.h>
  41. #include "common.h"
  42. #include "roots.h"
  43. #include "device.h"
  44. static constexpr int UI_WAIT_KEY_TIMEOUT_SEC = 120;
  45. static constexpr const char* BRIGHTNESS_FILE = BACKLIGHT_PATH "/brightness";
  46. static constexpr const char* MAX_BRIGHTNESS_FILE = BACKLIGHT_PATH "/max_brightness";
  47. static constexpr const char* BRIGHTNESS_FILE_SDM =
  48. "/sys/class/backlight/panel0-backlight/brightness";
  49. static constexpr const char* MAX_BRIGHTNESS_FILE_SDM =
  50. "/sys/class/backlight/panel0-backlight/max_brightness";
  51. RecoveryUI::RecoveryUI()
  52. : brightness_normal_(50),
  53. brightness_dimmed_(25),
  54. brightness_file_(BRIGHTNESS_FILE),
  55. max_brightness_file_(MAX_BRIGHTNESS_FILE),
  56. touch_screen_allowed_(true),
  57. kTouchLowThreshold(RECOVERY_UI_TOUCH_LOW_THRESHOLD),
  58. kTouchHighThreshold(RECOVERY_UI_TOUCH_HIGH_THRESHOLD),
  59. event_queue_len(0),
  60. key_last_down(-1),
  61. key_long_press(false),
  62. key_down_count(0),
  63. enable_reboot(true),
  64. consecutive_power_keys(0),
  65. last_key(-1),
  66. has_power_key(false),
  67. has_up_key(false),
  68. has_down_key(false),
  69. has_touch_screen(false),
  70. touch_slot_(0),
  71. touch_finger_down_(false),
  72. touch_saw_x_(false),
  73. touch_saw_y_(false),
  74. touch_reported_(false),
  75. is_bootreason_recovery_ui_(false),
  76. volumes_changed_(false),
  77. screensaver_state_(ScreensaverState::DISABLED) {
  78. char propval[PROPERTY_VALUE_MAX];
  79. property_get("ro.build.version.release", propval, "(unknown)");
  80. android_version_ = std::string("Android ") + propval;
  81. property_get("ro.lineage.version", propval, "(unknown)");
  82. lineage_version_ = std::string("LineageOS ") + propval;
  83. pthread_mutex_init(&event_queue_mutex, nullptr);
  84. pthread_cond_init(&event_queue_cond, nullptr);
  85. memset(key_pressed, 0, sizeof(key_pressed));
  86. }
  87. void RecoveryUI::OnTouchDeviceDetected(int fd) {
  88. char name[256];
  89. char path[PATH_MAX];
  90. char buf[4096];
  91. memset(name, 0, sizeof(name));
  92. if (ioctl(fd, EVIOCGNAME(sizeof(name)), name) < 0) {
  93. return;
  94. }
  95. sprintf(path, "/sys/board_properties/virtualkeys.%s", name);
  96. int vkfd = open(path, O_RDONLY);
  97. if (vkfd < 0) {
  98. LOG(INFO) << "vkeys: could not open " << path;
  99. return;
  100. }
  101. ssize_t len = read(vkfd, buf, sizeof(buf));
  102. close(vkfd);
  103. if (len <= 0) {
  104. LOG(ERROR) << "vkeys: could not read " << path;
  105. return;
  106. }
  107. buf[len] = '\0';
  108. char* p = buf;
  109. char* endp;
  110. for (size_t n = 0; p < buf+len && *p == '0'; ++n) {
  111. int val[6];
  112. int f;
  113. for (f = 0; *p && f < 6; ++f) {
  114. val[f] = strtol(p, &endp, 0);
  115. if (p == endp)
  116. break;
  117. p = endp+1;
  118. }
  119. if (f != 6 || val[0] != 0x01)
  120. break;
  121. vkey_t vk;
  122. vk.keycode = val[1];
  123. vk.min_ = Point(val[2] - val[4]/2, val[3] - val[5]/2);
  124. vk.max_ = Point(val[2] + val[4]/2, val[3] + val[5]/2);
  125. virtual_keys_.push_back(vk);
  126. }
  127. }
  128. void RecoveryUI::OnKeyDetected(int key_code) {
  129. if (key_code == KEY_POWER) {
  130. has_power_key = true;
  131. } else if (key_code == KEY_DOWN || key_code == KEY_VOLUMEDOWN) {
  132. has_down_key = true;
  133. } else if (key_code == KEY_UP || key_code == KEY_VOLUMEUP) {
  134. has_up_key = true;
  135. } else if (key_code == ABS_MT_POSITION_X || key_code == ABS_MT_POSITION_Y) {
  136. has_touch_screen = true;
  137. }
  138. }
  139. // Reads input events, handles special hot keys, and adds to the key queue.
  140. static void* InputThreadLoop(void*) {
  141. while (true) {
  142. if (!ev_wait(-1)) {
  143. ev_dispatch();
  144. }
  145. }
  146. return nullptr;
  147. }
  148. bool RecoveryUI::InitScreensaver() {
  149. // Disabled.
  150. if (brightness_normal_ == 0 || brightness_dimmed_ > brightness_normal_) {
  151. return false;
  152. }
  153. if (access(brightness_file_.c_str(), R_OK | W_OK)) {
  154. brightness_file_ = BRIGHTNESS_FILE_SDM;
  155. }
  156. if (access(max_brightness_file_.c_str(), R_OK)) {
  157. max_brightness_file_ = MAX_BRIGHTNESS_FILE_SDM;
  158. }
  159. // Set the initial brightness level based on the max brightness. Note that reading the initial
  160. // value from BRIGHTNESS_FILE doesn't give the actual brightness value (bullhead, sailfish), so
  161. // we don't have a good way to query the default value.
  162. std::string content;
  163. if (!android::base::ReadFileToString(max_brightness_file_, &content)) {
  164. PLOG(WARNING) << "Failed to read max brightness";
  165. return false;
  166. }
  167. unsigned int max_value;
  168. if (!android::base::ParseUint(android::base::Trim(content), &max_value)) {
  169. LOG(WARNING) << "Failed to parse max brightness: " << content;
  170. return false;
  171. }
  172. brightness_normal_value_ = max_value * brightness_normal_ / 100.0;
  173. brightness_dimmed_value_ = max_value * brightness_dimmed_ / 100.0;
  174. if (!android::base::WriteStringToFile(std::to_string(brightness_normal_value_),
  175. brightness_file_)) {
  176. PLOG(WARNING) << "Failed to set brightness";
  177. return false;
  178. }
  179. LOG(INFO) << "Brightness: " << brightness_normal_value_ << " (" << brightness_normal_ << "%)";
  180. screensaver_state_ = ScreensaverState::NORMAL;
  181. return true;
  182. }
  183. bool RecoveryUI::Init(const std::string& /* locale */) {
  184. ev_init(std::bind(&RecoveryUI::OnInputEvent, this, std::placeholders::_1, std::placeholders::_2),
  185. touch_screen_allowed_);
  186. ev_iterate_available_keys(std::bind(&RecoveryUI::OnKeyDetected, this, std::placeholders::_1));
  187. if (touch_screen_allowed_) {
  188. ev_iterate_touch_inputs(std::bind(&RecoveryUI::OnTouchDeviceDetected, this, std::placeholders::_1),
  189. std::bind(&RecoveryUI::OnKeyDetected, this, std::placeholders::_1));
  190. // Parse /proc/cmdline to determine if it's booting into recovery with a bootreason of
  191. // "recovery_ui". This specific reason is set by some (wear) bootloaders, to allow an easier way
  192. // to turn on text mode. It will only be set if the recovery boot is triggered from fastboot, or
  193. // with 'adb reboot recovery'. Note that this applies to all build variants. Otherwise the text
  194. // mode will be turned on automatically on debuggable builds, even without a swipe.
  195. std::string cmdline;
  196. if (android::base::ReadFileToString("/proc/cmdline", &cmdline)) {
  197. is_bootreason_recovery_ui_ = cmdline.find("bootreason=recovery_ui") != std::string::npos;
  198. } else {
  199. // Non-fatal, and won't affect Init() result.
  200. PLOG(WARNING) << "Failed to read /proc/cmdline";
  201. }
  202. }
  203. if (!InitScreensaver()) {
  204. LOG(INFO) << "Screensaver disabled";
  205. }
  206. pthread_create(&input_thread_, nullptr, InputThreadLoop, nullptr);
  207. return true;
  208. }
  209. void RecoveryUI::Stop() {
  210. if (!android::base::WriteStringToFile("0", BRIGHTNESS_FILE)) {
  211. PLOG(WARNING) << "Failed to write brightness file";
  212. }
  213. }
  214. void RecoveryUI::OnTouchPress() {
  215. touch_start_ = touch_track_ = touch_pos_;
  216. }
  217. void RecoveryUI::OnTouchTrack() {
  218. if (touch_pos_.y() <= gr_fb_height()) {
  219. if (MenuShowing() && MenuScrollable()) {
  220. while (abs(touch_pos_.y() - touch_track_.y()) >= MenuItemHeight()) {
  221. int dy = touch_pos_.y() - touch_track_.y();
  222. int key = (dy < 0) ? KEY_VOLUMEUP : KEY_VOLUMEDOWN;
  223. ProcessKey(key, 1); // press key
  224. ProcessKey(key, 0); // and release it
  225. int sgn = (dy > 0) - (dy < 0);
  226. touch_track_.y(touch_track_.y() + sgn * MenuItemHeight());
  227. }
  228. }
  229. }
  230. }
  231. void RecoveryUI::OnTouchRelease() {
  232. // Allow turning on text mode with any swipe, if bootloader has set a bootreason of recovery_ui.
  233. if (is_bootreason_recovery_ui_ && !IsTextVisible()) {
  234. ShowText(true);
  235. return;
  236. }
  237. // Check vkeys. Only report if touch both starts and ends in the vkey.
  238. if (touch_start_.y() > gr_fb_height() && touch_pos_.y() > gr_fb_height()) {
  239. for (const auto& vk : virtual_keys_) {
  240. if (vk.inside(touch_start_) && vk.inside(touch_pos_)) {
  241. ProcessKey(vk.keycode, 1); // press key
  242. ProcessKey(vk.keycode, 0); // and release it
  243. }
  244. }
  245. return;
  246. }
  247. // If we tracked a vertical swipe, ignore the release
  248. if (touch_track_ != touch_start_) {
  249. return;
  250. }
  251. // Check for horizontal swipe
  252. Point delta = touch_pos_ - touch_start_;
  253. if (abs(delta.y()) < kTouchLowThreshold && abs(delta.x()) > kTouchHighThreshold) {
  254. int key = (delta.x() < 0) ? KEY_BACK : KEY_POWER;
  255. ProcessKey(key, 1); // press key
  256. ProcessKey(key, 0); // and release it
  257. return;
  258. }
  259. // Simple touch
  260. EnqueueTouch(touch_pos_);
  261. }
  262. int RecoveryUI::OnInputEvent(int fd, uint32_t epevents) {
  263. struct input_event ev;
  264. if (ev_get_input(fd, epevents, &ev) == -1) {
  265. return -1;
  266. }
  267. // Touch inputs handling.
  268. //
  269. // Per the doc Multi-touch Protocol at below, there are two protocols.
  270. // https://www.kernel.org/doc/Documentation/input/multi-touch-protocol.txt
  271. //
  272. // The main difference between the stateless type A protocol and the stateful type B slot protocol
  273. // lies in the usage of identifiable contacts to reduce the amount of data sent to userspace. The
  274. // slot protocol (i.e. type B) sends ABS_MT_TRACKING_ID with a unique id on initial contact, and
  275. // sends ABS_MT_TRACKING_ID -1 upon lifting the contact. Protocol A doesn't send
  276. // ABS_MT_TRACKING_ID -1 on lifting, but the driver may additionally report BTN_TOUCH event.
  277. //
  278. // For protocol A, we rely on BTN_TOUCH to recognize lifting, while for protocol B we look for
  279. // ABS_MT_TRACKING_ID being -1.
  280. //
  281. // Touch input events will only be available if touch_screen_allowed_ is set.
  282. if (ev.type == EV_SYN) {
  283. if (touch_screen_allowed_ && ev.code == SYN_REPORT) {
  284. // There might be multiple SYN_REPORT events. Only report press/release once.
  285. if (!touch_reported_ && touch_finger_down_) {
  286. if (touch_saw_x_ && touch_saw_y_) {
  287. OnTouchPress();
  288. touch_reported_ = true;
  289. touch_saw_x_ = touch_saw_y_ = false;
  290. }
  291. } else if (touch_reported_ && !touch_finger_down_) {
  292. OnTouchRelease();
  293. touch_reported_ = false;
  294. touch_saw_x_ = touch_saw_y_ = false;
  295. }
  296. }
  297. return 0;
  298. }
  299. if (ev.type == EV_REL) {
  300. if (ev.code == REL_Y) {
  301. // accumulate the up or down motion reported by
  302. // the trackball. When it exceeds a threshold
  303. // (positive or negative), fake an up/down
  304. // key event.
  305. rel_sum += ev.value;
  306. if (rel_sum > 3) {
  307. ProcessKey(KEY_DOWN, 1); // press down key
  308. ProcessKey(KEY_DOWN, 0); // and release it
  309. rel_sum = 0;
  310. } else if (rel_sum < -3) {
  311. ProcessKey(KEY_UP, 1); // press up key
  312. ProcessKey(KEY_UP, 0); // and release it
  313. rel_sum = 0;
  314. }
  315. }
  316. } else {
  317. rel_sum = 0;
  318. }
  319. if (touch_screen_allowed_ && ev.type == EV_ABS) {
  320. if (ev.code == ABS_MT_SLOT) {
  321. touch_slot_ = ev.value;
  322. }
  323. // Ignore other fingers.
  324. if (touch_slot_ > 0) return 0;
  325. switch (ev.code) {
  326. case ABS_MT_POSITION_X:
  327. touch_finger_down_ = true;
  328. touch_saw_x_ = true;
  329. touch_pos_.x(ev.value);
  330. if (touch_reported_ && touch_saw_y_) {
  331. OnTouchTrack();
  332. touch_saw_x_ = touch_saw_y_ = false;
  333. }
  334. break;
  335. case ABS_MT_POSITION_Y:
  336. touch_finger_down_ = true;
  337. touch_saw_y_ = true;
  338. touch_pos_.y(ev.value);
  339. if (touch_reported_ && touch_saw_x_) {
  340. OnTouchTrack();
  341. touch_saw_x_ = touch_saw_y_ = false;
  342. }
  343. break;
  344. case ABS_MT_TRACKING_ID:
  345. // Protocol B: -1 marks lifting the contact.
  346. if (ev.value < 0) touch_finger_down_ = false;
  347. break;
  348. }
  349. return 0;
  350. }
  351. if (ev.type == EV_KEY && ev.code <= KEY_MAX) {
  352. if (touch_screen_allowed_) {
  353. if (ev.code == BTN_TOUCH) {
  354. // A BTN_TOUCH with value 1 indicates the start of contact (protocol A), with 0 means
  355. // lifting the contact.
  356. touch_finger_down_ = (ev.value == 1);
  357. }
  358. // Intentionally ignore BTN_TOUCH and BTN_TOOL_FINGER, which would otherwise trigger
  359. // additional scrolling (because in ScreenRecoveryUI::ShowFile(), we consider keys other than
  360. // KEY_POWER and KEY_UP as KEY_DOWN).
  361. if (ev.code == BTN_TOUCH || ev.code == BTN_TOOL_FINGER) {
  362. return 0;
  363. }
  364. }
  365. ProcessKey(ev.code, ev.value);
  366. }
  367. return 0;
  368. }
  369. // Process a key-up or -down event. A key is "registered" when it is
  370. // pressed and then released, with no other keypresses or releases in
  371. // between. Registered keys are passed to CheckKey() to see if it
  372. // should trigger a visibility toggle, an immediate reboot, or be
  373. // queued to be processed next time the foreground thread wants a key
  374. // (eg, for the menu).
  375. //
  376. // We also keep track of which keys are currently down so that
  377. // CheckKey can call IsKeyPressed to see what other keys are held when
  378. // a key is registered.
  379. //
  380. // updown == 1 for key down events; 0 for key up events
  381. void RecoveryUI::ProcessKey(int key_code, int updown) {
  382. bool register_key = false;
  383. bool long_press = false;
  384. bool reboot_enabled;
  385. pthread_mutex_lock(&event_queue_mutex);
  386. key_pressed[key_code] = updown;
  387. if (updown) {
  388. ++key_down_count;
  389. key_last_down = key_code;
  390. key_long_press = false;
  391. key_timer_t* info = new key_timer_t;
  392. info->ui = this;
  393. info->key_code = key_code;
  394. info->count = key_down_count;
  395. pthread_t thread;
  396. pthread_create(&thread, nullptr, &RecoveryUI::time_key_helper, info);
  397. pthread_detach(thread);
  398. } else {
  399. if (key_last_down == key_code) {
  400. long_press = key_long_press;
  401. register_key = true;
  402. }
  403. key_last_down = -1;
  404. }
  405. reboot_enabled = enable_reboot;
  406. pthread_mutex_unlock(&event_queue_mutex);
  407. if (register_key) {
  408. switch (CheckKey(key_code, long_press)) {
  409. case RecoveryUI::IGNORE:
  410. break;
  411. case RecoveryUI::TOGGLE:
  412. ShowText(!IsTextVisible());
  413. break;
  414. case RecoveryUI::REBOOT:
  415. if (reboot_enabled) {
  416. android::volmgr::VolumeManager::Instance()->unmountAll();
  417. reboot("reboot,");
  418. while (true) {
  419. pause();
  420. }
  421. }
  422. break;
  423. case RecoveryUI::ENQUEUE:
  424. EnqueueKey(key_code);
  425. break;
  426. }
  427. }
  428. }
  429. void* RecoveryUI::time_key_helper(void* cookie) {
  430. key_timer_t* info = static_cast<key_timer_t*>(cookie);
  431. info->ui->time_key(info->key_code, info->count);
  432. delete info;
  433. return nullptr;
  434. }
  435. void RecoveryUI::time_key(int key_code, int count) {
  436. usleep(750000); // 750 ms == "long"
  437. bool long_press = false;
  438. pthread_mutex_lock(&event_queue_mutex);
  439. if (key_last_down == key_code && key_down_count == count) {
  440. long_press = key_long_press = true;
  441. }
  442. pthread_mutex_unlock(&event_queue_mutex);
  443. if (long_press) KeyLongPress(key_code);
  444. }
  445. void RecoveryUI::EnqueueKey(int key_code) {
  446. pthread_mutex_lock(&event_queue_mutex);
  447. const int queue_max = sizeof(event_queue) / sizeof(event_queue[0]);
  448. if (event_queue_len < queue_max) {
  449. InputEvent event(key_code);
  450. event_queue[event_queue_len++] = event;
  451. pthread_cond_signal(&event_queue_cond);
  452. }
  453. pthread_mutex_unlock(&event_queue_mutex);
  454. }
  455. void RecoveryUI::EnqueueTouch(const Point& pos) {
  456. pthread_mutex_lock(&event_queue_mutex);
  457. const int queue_max = sizeof(event_queue) / sizeof(event_queue[0]);
  458. if (event_queue_len < queue_max) {
  459. InputEvent event(pos);
  460. event_queue[event_queue_len++] = event;
  461. pthread_cond_signal(&event_queue_cond);
  462. }
  463. pthread_mutex_unlock(&event_queue_mutex);
  464. }
  465. RecoveryUI::InputEvent RecoveryUI::WaitInputEvent() {
  466. pthread_mutex_lock(&event_queue_mutex);
  467. // Time out after UI_WAIT_KEY_TIMEOUT_SEC, unless a USB cable is
  468. // plugged in.
  469. do {
  470. struct timeval now;
  471. struct timespec timeout;
  472. gettimeofday(&now, nullptr);
  473. timeout.tv_sec = now.tv_sec;
  474. timeout.tv_nsec = now.tv_usec * 1000;
  475. timeout.tv_sec += UI_WAIT_KEY_TIMEOUT_SEC;
  476. int rc = 0;
  477. while (event_queue_len == 0 && rc != ETIMEDOUT) {
  478. struct timespec key_timeout;
  479. gettimeofday(&now, nullptr);
  480. key_timeout.tv_sec = now.tv_sec + 1;
  481. key_timeout.tv_nsec = now.tv_usec * 1000;
  482. rc = pthread_cond_timedwait(&event_queue_cond, &event_queue_mutex, &key_timeout);
  483. if (rc == ETIMEDOUT) {
  484. if (VolumesChanged()) {
  485. pthread_mutex_unlock(&event_queue_mutex);
  486. InputEvent event(KEY_REFRESH);
  487. return event;
  488. }
  489. if (key_timeout.tv_sec <= timeout.tv_sec) {
  490. rc = 0;
  491. ui->Redraw();
  492. }
  493. }
  494. }
  495. if (screensaver_state_ != ScreensaverState::DISABLED) {
  496. if (rc == ETIMEDOUT) {
  497. // Lower the brightness level: NORMAL -> DIMMED; DIMMED -> OFF.
  498. if (screensaver_state_ == ScreensaverState::NORMAL) {
  499. if (android::base::WriteStringToFile(std::to_string(brightness_dimmed_value_),
  500. brightness_file_)) {
  501. LOG(INFO) << "Brightness: " << brightness_dimmed_value_ << " (" << brightness_dimmed_
  502. << "%)";
  503. screensaver_state_ = ScreensaverState::DIMMED;
  504. }
  505. } else if (screensaver_state_ == ScreensaverState::DIMMED) {
  506. if (android::base::WriteStringToFile("0", brightness_file_)) {
  507. LOG(INFO) << "Brightness: 0 (off)";
  508. screensaver_state_ = ScreensaverState::OFF;
  509. }
  510. }
  511. } else if (screensaver_state_ != ScreensaverState::NORMAL) {
  512. // Drop the first key if it's changing from OFF to NORMAL.
  513. if (screensaver_state_ == ScreensaverState::OFF) {
  514. if (event_queue_len > 0) {
  515. memcpy(&event_queue[0], &event_queue[1], sizeof(int) * --event_queue_len);
  516. }
  517. }
  518. // Reset the brightness to normal.
  519. if (android::base::WriteStringToFile(std::to_string(brightness_normal_value_),
  520. brightness_file_)) {
  521. screensaver_state_ = ScreensaverState::NORMAL;
  522. LOG(INFO) << "Brightness: " << brightness_normal_value_ << " (" << brightness_normal_
  523. << "%)";
  524. }
  525. }
  526. }
  527. } while (IsUsbConnected() && event_queue_len == 0);
  528. InputEvent event;
  529. if (event_queue_len > 0) {
  530. event = event_queue[0];
  531. memcpy(&event_queue[0], &event_queue[1], sizeof(event_queue[0]) * --event_queue_len);
  532. }
  533. pthread_mutex_unlock(&event_queue_mutex);
  534. return event;
  535. }
  536. void RecoveryUI::CancelWaitKey()
  537. {
  538. pthread_mutex_lock(&event_queue_mutex);
  539. InputEvent event(KEY_REFRESH);
  540. event_queue[event_queue_len++] = event;
  541. pthread_cond_signal(&event_queue_cond);
  542. pthread_mutex_unlock(&event_queue_mutex);
  543. }
  544. bool RecoveryUI::IsUsbConnected() {
  545. int fd = open("/sys/class/android_usb/android0/state", O_RDONLY);
  546. if (fd < 0) {
  547. printf("failed to open /sys/class/android_usb/android0/state: %s\n", strerror(errno));
  548. return 0;
  549. }
  550. char buf;
  551. // USB is connected if android_usb state is CONNECTED or CONFIGURED.
  552. int connected = (TEMP_FAILURE_RETRY(read(fd, &buf, 1)) == 1) && (buf == 'C');
  553. if (close(fd) < 0) {
  554. printf("failed to close /sys/class/android_usb/android0/state: %s\n", strerror(errno));
  555. }
  556. return connected;
  557. }
  558. bool RecoveryUI::IsKeyPressed(int key) {
  559. pthread_mutex_lock(&event_queue_mutex);
  560. int pressed = key_pressed[key];
  561. pthread_mutex_unlock(&event_queue_mutex);
  562. return pressed;
  563. }
  564. bool RecoveryUI::IsLongPress() {
  565. pthread_mutex_lock(&event_queue_mutex);
  566. bool result = key_long_press;
  567. pthread_mutex_unlock(&event_queue_mutex);
  568. return result;
  569. }
  570. bool RecoveryUI::HasThreeButtons() {
  571. return has_power_key && has_up_key && has_down_key;
  572. }
  573. bool RecoveryUI::HasPowerKey() const {
  574. return has_power_key;
  575. }
  576. bool RecoveryUI::HasTouchScreen() const {
  577. return has_touch_screen;
  578. }
  579. void RecoveryUI::FlushKeys() {
  580. pthread_mutex_lock(&event_queue_mutex);
  581. event_queue_len = 0;
  582. pthread_mutex_unlock(&event_queue_mutex);
  583. }
  584. RecoveryUI::KeyAction RecoveryUI::CheckKey(int key, bool is_long_press) {
  585. pthread_mutex_lock(&event_queue_mutex);
  586. key_long_press = false;
  587. pthread_mutex_unlock(&event_queue_mutex);
  588. // If we have power and volume up keys, that chord is the signal to toggle the text display.
  589. if (HasThreeButtons() || (HasPowerKey() && HasTouchScreen() && touch_screen_allowed_)) {
  590. if ((key == KEY_VOLUMEUP || key == KEY_UP) && IsKeyPressed(KEY_POWER)) {
  591. return TOGGLE;
  592. }
  593. } else {
  594. // Otherwise long press of any button toggles to the text display,
  595. // and there's no way to toggle back (but that's pretty useless anyway).
  596. if (is_long_press && !IsTextVisible()) {
  597. return TOGGLE;
  598. }
  599. // Also, for button-limited devices, a long press is translated to KEY_ENTER.
  600. if (is_long_press && IsTextVisible()) {
  601. EnqueueKey(KEY_ENTER);
  602. return IGNORE;
  603. }
  604. }
  605. // Press power seven times in a row to reboot.
  606. if (key == KEY_POWER) {
  607. pthread_mutex_lock(&event_queue_mutex);
  608. bool reboot_enabled = enable_reboot;
  609. pthread_mutex_unlock(&event_queue_mutex);
  610. if (reboot_enabled) {
  611. ++consecutive_power_keys;
  612. if (consecutive_power_keys >= 7) {
  613. return REBOOT;
  614. }
  615. }
  616. } else {
  617. consecutive_power_keys = 0;
  618. }
  619. last_key = key;
  620. return (IsTextVisible() || screensaver_state_ == ScreensaverState::OFF) ? ENQUEUE : IGNORE;
  621. }
  622. void RecoveryUI::KeyLongPress(int) {
  623. }
  624. void RecoveryUI::SetEnableReboot(bool enabled) {
  625. pthread_mutex_lock(&event_queue_mutex);
  626. enable_reboot = enabled;
  627. pthread_mutex_unlock(&event_queue_mutex);
  628. }
  629. bool RecoveryUI::VolumesChanged() {
  630. bool ret = volumes_changed_;
  631. volumes_changed_ = false;
  632. return ret;
  633. }