Verbatim copy of gralloc, hwcomposer, framebuffer, vnc_server and wpa_supllicatn_lib
Taken from: persistent-https://googleplex-android.git.corp.google.com/device/google/gce
Commit: 7d529657f7aae5fae8ee1781f6157f7b17da7863
Test: None
Change-Id: Ie3502c92228ca6aff9fe53e7f1e9a9e4cd4a0e80
diff --git a/guest/frontend/vnc_server/Android.mk b/guest/frontend/vnc_server/Android.mk
new file mode 100644
index 0000000..587986a
--- /dev/null
+++ b/guest/frontend/vnc_server/Android.mk
@@ -0,0 +1,89 @@
+# Copyright (C) 2016 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.
+#
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+
+ifneq (,$(filter $(PLATFORM_SDK_VERSION),16 17 18 19 21 22 23)) # J K L M
+ # prior to (not including) nyc, libjpeg was used instead of libjpeg turbo
+ # the libjpeg turbo in external/ is a backport with the shared library name
+ # changed to libjpeg_turbo to avoid conflict with the system's libjpeg
+ LIBJPEG_TURBO_NAME := libjpeg_turbo
+else
+ # nyc and later use libjpeg turbo under its usual name
+ LIBJPEG_TURBO_NAME := libjpeg
+endif
+
+LOCAL_C_INCLUDES := \
+ device/google/gce/sensors \
+ device/google/gce/include \
+ external/libjpeg-turbo \
+ external/jsoncpp/include
+
+include device/google/gce/libs/base/libbase.mk
+LOCAL_C_INCLUDES += $(GCE_LIBBASE_INCLUDE_DIR)
+
+LOCAL_MODULE := vnc_server
+LOCAL_VENDOR_MODULE := true
+LOCAL_MODULE_TAGS := optional
+LOCAL_SRC_FILES := \
+ blackboard.cpp \
+ frame_buffer_watcher.cpp \
+ jpeg_compressor.cpp \
+ main.cpp \
+ simulated_hw_composer.cpp \
+ tcp_socket.cpp \
+ VirtualInputDevice.cpp \
+ virtual_inputs.cpp \
+ vnc_client_connection.cpp \
+ vnc_server.cpp \
+
+LOCAL_CFLAGS := \
+ $(GCE_VERSION_CFLAGS) \
+ -std=gnu++11 \
+ -Wall -Werror \
+ -Wno-error-unused -Wno-error=unused-parameter \
+ -Wno-attributes \
+ -DGCE_32_BIT_GRAPHICS
+
+ifeq (0, $(shell test $(PLATFORM_SDK_VERSION) -ge 18; echo $$?))
+LOCAL_CFLAGS += -Wno-error=implicit-exception-spec-mismatch
+endif
+
+LOCAL_SHARED_LIBRARIES := $(LIBJPEG_TURBO_NAME)
+LOCAL_STATIC_LIBRARIES := libcutils liblog
+LOCAL_CLANG := true
+
+ifeq (0, $(shell test $(PLATFORM_SDK_VERSION) -le 22; echo $$?)) #lmp-mr1 and down
+ LOCAL_STATIC_LIBRARIES += \
+ libgceframebuffer_cxx \
+ libgcemetadata_cxx \
+ libjsoncpp_cxx \
+ libgcecutils_cxx \
+
+ include external/libcxx/libcxx.mk
+else
+ LOCAL_STATIC_LIBRARIES += \
+ libgcemetadata \
+ libjsoncpp \
+
+ LOCAL_SHARED_LIBRARIES += \
+ libgceframebuffer \
+ libgcecutils \
+
+endif
+
+include $(BUILD_EXECUTABLE)
diff --git a/guest/frontend/vnc_server/VirtualInputDevice.cpp b/guest/frontend/vnc_server/VirtualInputDevice.cpp
new file mode 100644
index 0000000..badeb07
--- /dev/null
+++ b/guest/frontend/vnc_server/VirtualInputDevice.cpp
@@ -0,0 +1,428 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/input.h>
+#include <linux/uinput.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <vector>
+
+#include "keysyms.h"
+
+#define LOG_TAG "RemoterVirtualInput"
+#include <cutils/log.h>
+#include <cutils/properties.h>
+
+#include "VirtualInputDevice.h"
+
+#define ARRAY_SIZE(a) \
+ ((sizeof(a) / sizeof(*(a))) / \
+ static_cast<size_t>(!(sizeof(a) % sizeof(*(a)))))
+
+//////////////////////////
+// VirtualButton Support
+//////////////////////////
+
+namespace avd {
+uint32_t VirtualButton::Senabled_events_[] = {EV_KEY};
+
+VirtualButton::VirtualButton(const char* name, uint32_t input_keycode)
+ : VirtualInputDevice(name, BUS_USB, 0x6006, 0x6007, 1),
+ input_keycode_(input_keycode) {
+ if (!VirtualInputDevice::Init(Senabled_events_, ARRAY_SIZE(Senabled_events_),
+ &input_keycode_, 1, NULL, 0, NULL, 0)) {
+ LOG_FATAL("VirtualInputDevice Init() failed");
+ }
+}
+
+void VirtualButton::HandleButtonPressEvent(bool button_down) {
+ EmitEvent(EV_KEY, input_keycode_, button_down);
+ EmitEvent(EV_SYN, 0, 0);
+}
+
+//////////////////////////
+// VirtualKeyboard Support
+//////////////////////////
+
+uint32_t VirtualKeyboard::Senabled_events_[] = {EV_KEY};
+
+struct KeyEventToInput {
+ uint32_t xk;
+ uint32_t input_code;
+};
+
+static const KeyEventToInput key_table[] = {
+ {xk::AltLeft, KEY_LEFTALT},
+ {xk::ControlLeft, KEY_LEFTCTRL},
+ {xk::ShiftLeft, KEY_LEFTSHIFT},
+ {xk::AltRight, KEY_RIGHTALT},
+ {xk::ControlRight, KEY_RIGHTCTRL},
+ {xk::ShiftRight, KEY_RIGHTSHIFT},
+ {xk::MetaLeft, KEY_LEFTMETA},
+ {xk::MetaRight, KEY_RIGHTMETA},
+ {xk::MultiKey, KEY_COMPOSE},
+
+ {xk::CapsLock, KEY_CAPSLOCK},
+ {xk::NumLock, KEY_NUMLOCK},
+ {xk::ScrollLock, KEY_SCROLLLOCK},
+
+ {xk::BackSpace, KEY_BACKSPACE},
+ {xk::Tab, KEY_TAB},
+ {xk::Return, KEY_ENTER},
+ {xk::Escape, KEY_ESC},
+
+ {' ', KEY_SPACE},
+ {'!', KEY_1},
+ {'"', KEY_APOSTROPHE},
+ {'#', KEY_3},
+ {'$', KEY_4},
+ {'%', KEY_5},
+ {'^', KEY_6},
+ {'&', KEY_7},
+ {'\'', KEY_APOSTROPHE},
+ {'(', KEY_9},
+ {')', KEY_0},
+ {'*', KEY_8},
+ {'+', KEY_EQUAL},
+ {',', KEY_COMMA},
+ {'-', KEY_MINUS},
+ {'.', KEY_DOT},
+ {'/', KEY_SLASH},
+ {'0', KEY_0},
+ {'1', KEY_1},
+ {'2', KEY_2},
+ {'3', KEY_3},
+ {'4', KEY_4},
+ {'5', KEY_5},
+ {'6', KEY_6},
+ {'7', KEY_7},
+ {'8', KEY_8},
+ {'9', KEY_9},
+ {':', KEY_SEMICOLON},
+ {';', KEY_SEMICOLON},
+ {'<', KEY_COMMA},
+ {'=', KEY_EQUAL},
+ {'>', KEY_DOT},
+ {'?', KEY_SLASH},
+ {'@', KEY_2},
+ {'A', KEY_A},
+ {'B', KEY_B},
+ {'C', KEY_C},
+ {'D', KEY_D},
+ {'E', KEY_E},
+ {'F', KEY_F},
+ {'G', KEY_G},
+ {'H', KEY_H},
+ {'I', KEY_I},
+ {'J', KEY_J},
+ {'K', KEY_K},
+ {'L', KEY_L},
+ {'M', KEY_M},
+ {'N', KEY_N},
+ {'O', KEY_O},
+ {'P', KEY_P},
+ {'Q', KEY_Q},
+ {'R', KEY_R},
+ {'S', KEY_S},
+ {'T', KEY_T},
+ {'U', KEY_U},
+ {'V', KEY_V},
+ {'W', KEY_W},
+ {'X', KEY_X},
+ {'Y', KEY_Y},
+ {'Z', KEY_Z},
+ {'[', KEY_LEFTBRACE},
+ {'\\', KEY_BACKSLASH},
+ {']', KEY_RIGHTBRACE},
+ {'-', KEY_MINUS},
+ {'_', KEY_MINUS},
+ {'`', KEY_GRAVE},
+ {'a', KEY_A},
+ {'b', KEY_B},
+ {'c', KEY_C},
+ {'d', KEY_D},
+ {'e', KEY_E},
+ {'f', KEY_F},
+ {'g', KEY_G},
+ {'h', KEY_H},
+ {'i', KEY_I},
+ {'j', KEY_J},
+ {'k', KEY_K},
+ {'l', KEY_L},
+ {'m', KEY_M},
+ {'n', KEY_N},
+ {'o', KEY_O},
+ {'p', KEY_P},
+ {'q', KEY_Q},
+ {'r', KEY_R},
+ {'s', KEY_S},
+ {'t', KEY_T},
+ {'u', KEY_U},
+ {'v', KEY_V},
+ {'w', KEY_W},
+ {'x', KEY_X},
+ {'y', KEY_Y},
+ {'z', KEY_Z},
+ {'{', KEY_LEFTBRACE},
+ {'\\', KEY_BACKSLASH},
+ {'|', KEY_BACKSLASH},
+ {'}', KEY_RIGHTBRACE},
+ {'~', KEY_GRAVE},
+
+ {xk::F1, KEY_F1},
+ {xk::F2, KEY_F2},
+ {xk::F3, KEY_F3},
+ {xk::F4, KEY_F4},
+ {xk::F5, KEY_F5},
+ {xk::F6, KEY_F6},
+ {xk::F7, KEY_F7},
+ {xk::F8, KEY_F8},
+ {xk::F9, KEY_F9},
+ {xk::F10, KEY_F10},
+ {xk::F11, KEY_F11},
+ {xk::F12, KEY_F12},
+ {xk::F13, KEY_F13},
+ {xk::F14, KEY_F14},
+ {xk::F15, KEY_F15},
+ {xk::F16, KEY_F16},
+ {xk::F17, KEY_F17},
+ {xk::F18, KEY_F18},
+ {xk::F19, KEY_F19},
+ {xk::F20, KEY_F20},
+ {xk::F21, KEY_F21},
+ {xk::F22, KEY_F22},
+ {xk::F23, KEY_F23},
+ {xk::F24, KEY_F24},
+
+ {xk::Keypad0, KEY_KP0},
+ {xk::Keypad1, KEY_KP1},
+ {xk::Keypad2, KEY_KP2},
+ {xk::Keypad3, KEY_KP3},
+ {xk::Keypad4, KEY_KP4},
+ {xk::Keypad5, KEY_KP5},
+ {xk::Keypad6, KEY_KP6},
+ {xk::Keypad7, KEY_KP7},
+ {xk::Keypad8, KEY_KP8},
+ {xk::Keypad9, KEY_KP9},
+ {xk::KeypadMultiply, KEY_KPASTERISK},
+ {xk::KeypadSubtract, KEY_KPMINUS},
+ {xk::KeypadAdd, KEY_KPPLUS},
+ {xk::KeypadDecimal, KEY_KPDOT},
+ {xk::KeypadEnter, KEY_KPENTER},
+ {xk::KeypadDivide, KEY_KPSLASH},
+ {xk::KeypadEqual, KEY_KPEQUAL},
+ {xk::PlusMinus, KEY_KPPLUSMINUS},
+
+ {xk::SysReq, KEY_SYSRQ},
+ {xk::LineFeed, KEY_LINEFEED},
+ {xk::Home, KEY_HOME},
+ {xk::Up, KEY_UP},
+ {xk::PageUp, KEY_PAGEUP},
+ {xk::Left, KEY_LEFT},
+ {xk::Right, KEY_RIGHT},
+ {xk::End, KEY_END},
+ {xk::Down, KEY_DOWN},
+ {xk::PageDown, KEY_PAGEDOWN},
+ {xk::Insert, KEY_INSERT},
+ {xk::Delete, KEY_DELETE},
+ {xk::Pause, KEY_PAUSE},
+ {xk::KeypadSeparator, KEY_KPCOMMA},
+ {xk::Yen, KEY_YEN},
+ {xk::Cancel, KEY_STOP},
+ {xk::Redo, KEY_AGAIN},
+ {xk::Undo, KEY_UNDO},
+ {xk::Find, KEY_FIND},
+ {xk::Print, KEY_PRINT},
+ {xk::VolumeDown, KEY_VOLUMEDOWN},
+ {xk::Mute, KEY_MUTE},
+ {xk::VolumeUp, KEY_VOLUMEUP},
+ {xk::Menu, KEY_MENU},
+ {xk::VNCMenu, KEY_MENU},
+};
+
+VirtualKeyboard::VirtualKeyboard(const char* name)
+ : VirtualInputDevice(name, BUS_USB, 0x6006, 0x6008, 1) {
+ std::vector<uint32_t> keycodes(ARRAY_SIZE(key_table));
+ for (size_t i = 0; i < keycodes.size(); ++i) {
+ keymapping_[key_table[i].xk] = key_table[i].input_code;
+ keycodes[i] = key_table[i].input_code;
+ }
+
+ if (!VirtualInputDevice::Init(Senabled_events_, ARRAY_SIZE(Senabled_events_),
+ &keycodes[0], keycodes.size(), NULL, 0, NULL,
+ 0)) {
+ LOG_FATAL("VirtualInputDevice Init() failed");
+ }
+}
+
+void VirtualKeyboard::GenerateKeyPressEvent(int keycode, bool button_down) {
+ if (keymapping_.count(keycode)) {
+ EmitEvent(EV_KEY, keymapping_[keycode], button_down);
+ EmitEvent(EV_SYN, 0, 0);
+ }
+ ALOGI("Unknown keycode %d", keycode);
+}
+
+//////////////////////////
+// VirtualTouchPad Support
+//////////////////////////
+
+uint32_t VirtualTouchPad::Senabled_events_[] = {EV_ABS, EV_KEY, EV_SYN};
+uint32_t VirtualTouchPad::Senabled_keys_[] = {BTN_TOUCH};
+uint32_t VirtualTouchPad::Senabled_abs_[] = {ABS_X, ABS_Y};
+uint32_t VirtualTouchPad::Senabled_props_[] = {INPUT_PROP_DIRECT};
+
+VirtualTouchPad::VirtualTouchPad(const char* name, int x_res, int y_res)
+ : VirtualInputDevice(name, BUS_USB, 0x6006, 0x6006, 1),
+ x_res_(x_res),
+ y_res_(y_res) {
+ // Customization of uinput_user_dev() must happen before calling our base
+ // Init().
+ uinput_user_dev()->absmin[ABS_X] = 0;
+ uinput_user_dev()->absmax[ABS_X] = x_res_;
+ uinput_user_dev()->absmin[ABS_Y] = 0;
+ uinput_user_dev()->absmax[ABS_Y] = y_res_;
+
+ if (!VirtualInputDevice::Init(Senabled_events_, ARRAY_SIZE(Senabled_events_),
+ Senabled_keys_, ARRAY_SIZE(Senabled_keys_),
+ Senabled_abs_, ARRAY_SIZE(Senabled_abs_),
+ Senabled_props_, ARRAY_SIZE(Senabled_props_))) {
+ LOG_FATAL("VirtualInputDevice Init() failed");
+ }
+}
+
+void VirtualTouchPad::HandlePointerEvent(bool touch_down, int x, int y) {
+ EmitEvent(EV_ABS, ABS_X, x);
+ EmitEvent(EV_ABS, ABS_Y, y);
+ EmitEvent(EV_KEY, BTN_TOUCH, touch_down);
+ EmitEvent(EV_SYN, 0, 0);
+}
+
+//////////////////////////////////
+// Base VirtualInputDevice Support
+//////////////////////////////////
+VirtualInputDevice::VirtualInputDevice(const char* name, uint16_t bus_type,
+ uint16_t vendor, uint16_t product,
+ uint16_t version)
+ : fd_(-1) {
+ memset(&uinput_user_dev_, 0, sizeof(uinput_user_dev_));
+ strncpy(uinput_user_dev_.name, name, sizeof(uinput_user_dev_.name));
+ uinput_user_dev_.id.bustype = bus_type;
+ uinput_user_dev_.id.vendor = vendor;
+ uinput_user_dev_.id.product = product;
+ uinput_user_dev_.id.version = version;
+}
+
+VirtualInputDevice::~VirtualInputDevice() {
+ if (fd_ != -1) {
+ close(fd_);
+ fd_ = -1;
+ }
+}
+
+bool VirtualInputDevice::Init(uint32_t* events, int num_events, uint32_t* keys,
+ int num_keys, uint32_t* abs, int num_abs,
+ uint32_t* props, int num_props) {
+ if ((fd_ = open("/dev/uinput", O_WRONLY | O_NONBLOCK)) < 0) {
+ SLOGE("Failed to open /dev/uinput (%s)", strerror(errno));
+ return false;
+ }
+ if (events && !EnableEventBits(events, num_events)) {
+ SLOGE("Failed to set event bits (%s)", strerror(errno));
+ return false;
+ }
+ if (keys && !EnableKeyBits(keys, num_keys)) {
+ SLOGE("Failed to set key bits (%s)", strerror(errno));
+ return false;
+ }
+ if (abs && !EnableAbsBits(abs, num_abs)) {
+ SLOGE("Failed to set abs bits (%s)", strerror(errno));
+ return false;
+ }
+ if (props && !EnablePropBits(props, num_props)) {
+ SLOGE("Failed to set prop bits (%s)", strerror(errno));
+ return false;
+ }
+ if (!FinalizeDeviceCreation()) {
+ SLOGE("Failed to finalize device creation (%s)", strerror(errno));
+ return false;
+ }
+ return true;
+}
+
+bool VirtualInputDevice::EmitEvent(uint16_t type, uint16_t code,
+ uint32_t value) {
+ struct input_event ev;
+ ev.type = type;
+ ev.code = code;
+ ev.value = value;
+ if (write(fd_, &ev, sizeof(ev)) < 0) {
+ SLOGE("write() failed (%s)", strerror(errno));
+ return false;
+ }
+ return true;
+}
+
+bool VirtualInputDevice::DoIoctls(int request, uint32_t* list,
+ int num_elements) {
+ for (int i = 0; i < num_elements; i++) {
+ int rc = ioctl(fd_, request, *list++);
+ if (rc < 0) {
+ SLOGE("ioctl failed (%s)", strerror(errno));
+ return false;
+ }
+ }
+ return true;
+}
+
+bool VirtualInputDevice::EnableEventBits(uint32_t* events, int num_elements) {
+ return DoIoctls(UI_SET_EVBIT, events, num_elements);
+}
+
+bool VirtualInputDevice::EnableKeyBits(uint32_t* keys, int num_elements) {
+ return DoIoctls(UI_SET_KEYBIT, keys, num_elements);
+}
+
+bool VirtualInputDevice::EnableAbsBits(uint32_t* abs, int num_elements) {
+ return DoIoctls(UI_SET_ABSBIT, abs, num_elements);
+}
+
+bool VirtualInputDevice::EnablePropBits(uint32_t* props, int num_elements) {
+// JB and ICE do not have the latest uinput headers.
+#ifndef UI_SET_PROPBIT
+#define UI_SET_PROPBIT _IOW(UINPUT_IOCTL_BASE, 110, int)
+#endif // #ifndef UI_SET_PROPBIT
+ return DoIoctls(UI_SET_PROPBIT, props, num_elements);
+}
+
+bool VirtualInputDevice::FinalizeDeviceCreation() {
+ if (write(fd_, &uinput_user_dev_, sizeof(uinput_user_dev_)) < 0) {
+ SLOGE("Unable to set input device info (%s)", strerror(errno));
+ return false;
+ }
+ if (ioctl(fd_, UI_DEV_CREATE) < 0) {
+ SLOGE("Unable to create input device (%s)", strerror(errno));
+ return false;
+ }
+ return true;
+}
+
+} // namespace avd
diff --git a/guest/frontend/vnc_server/VirtualInputDevice.h b/guest/frontend/vnc_server/VirtualInputDevice.h
new file mode 100644
index 0000000..02605a2
--- /dev/null
+++ b/guest/frontend/vnc_server/VirtualInputDevice.h
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#ifndef VIRTUAL_INPUT_DEVICE_H_
+#define VIRTUAL_INPUT_DEVICE_H_
+
+#include <linux/uinput.h>
+#include <map>
+
+namespace avd {
+// Base virtual input device class which contains a bunch of boiler-plate code.
+class VirtualInputDevice {
+public:
+ VirtualInputDevice(const char* name, uint16_t bus_type, uint16_t vendor,
+ uint16_t product, uint16_t version);
+ virtual ~VirtualInputDevice();
+
+protected:
+ bool Init(uint32_t* events, int num_events, uint32_t* keys, int num_keys,
+ uint32_t* abs, int num_abs, uint32_t* props, int num_props);
+
+ bool EmitEvent(uint16_t type, uint16_t code, uint32_t value);
+
+ struct uinput_user_dev* uinput_user_dev() { return &uinput_user_dev_; }
+
+private:
+ bool EnableEventBits(uint32_t* events, int num_elements);
+ bool EnableKeyBits(uint32_t* keys, int num_elements);
+ bool EnableAbsBits(uint32_t* abs, int num_elements);
+ bool EnablePropBits(uint32_t* props, int num_elements);
+ bool DoIoctls(int request, uint32_t* list, int num_elements);
+ bool FinalizeDeviceCreation();
+
+ int fd_;
+ struct uinput_user_dev uinput_user_dev_;
+};
+
+// Virtual touch-pad.
+class VirtualTouchPad : public VirtualInputDevice {
+public:
+ VirtualTouchPad(const char* name, int x_res, int y_res);
+ virtual ~VirtualTouchPad() {}
+
+ void HandlePointerEvent(bool touch_down, int x, int y);
+
+private:
+ static uint32_t Senabled_events_[];
+ static uint32_t Senabled_keys_[];
+ static uint32_t Senabled_abs_[];
+ static uint32_t Senabled_props_[];
+
+ int x_res_;
+ int y_res_;
+};
+
+// Virtual button.
+class VirtualButton : public VirtualInputDevice {
+public:
+ VirtualButton(const char* name, uint32_t input_keycode);
+ virtual ~VirtualButton() {}
+
+ void HandleButtonPressEvent(bool button_down);
+
+private:
+ static uint32_t Senabled_events_[];
+ uint32_t input_keycode_;
+};
+
+// Virtual keyboard.
+class VirtualKeyboard : public VirtualInputDevice {
+public:
+ VirtualKeyboard(const char* name);
+ virtual ~VirtualKeyboard() {}
+
+ void GenerateKeyPressEvent(int code, bool down);
+
+private:
+ static uint32_t Senabled_events_[];
+ std::map<uint32_t, uint32_t> keymapping_;
+};
+
+} // namespace avd
+#endif
+
diff --git a/guest/frontend/vnc_server/blackboard.cpp b/guest/frontend/vnc_server/blackboard.cpp
new file mode 100644
index 0000000..ce4db8d
--- /dev/null
+++ b/guest/frontend/vnc_server/blackboard.cpp
@@ -0,0 +1,143 @@
+#include "blackboard.h"
+#include "frame_buffer_watcher.h"
+#include <utility>
+#include <algorithm>
+
+#define LOG_TAG "GceVNCServer"
+#include <cutils/log.h>
+
+using avd::vnc::BlackBoard;
+using avd::vnc::Stripe;
+
+avd::vnc::SeqNumberVec avd::vnc::MakeSeqNumberVec() {
+ return SeqNumberVec(FrameBufferWatcher::StripesPerFrame());
+}
+
+void BlackBoard::NewStripeReady(int index, StripeSeqNumber seq_num) {
+ std::lock_guard<std::mutex> guard(m_);
+ D("new stripe arrived from frame watcher");
+ auto& current_seq_num = most_recent_stripe_seq_nums_[index];
+ current_seq_num = std::max(current_seq_num, seq_num);
+ for (auto& client : clients_) {
+ if (client.second.ready_to_receive) {
+ client.second.new_frame_cv.notify_one();
+ }
+ }
+}
+
+void BlackBoard::Register(const VncClientConnection* conn) {
+ {
+ std::lock_guard<std::mutex> guard(m_);
+ ALOG_ASSERT(!clients_.count(conn));
+ clients_[conn]; // constructs new state in place
+ }
+ new_client_cv_.notify_one();
+}
+
+void BlackBoard::Unregister(const VncClientConnection* conn) {
+ std::lock_guard<std::mutex> guard(m_);
+ ALOG_ASSERT(clients_.count(conn));
+ clients_.erase(clients_.find(conn));
+}
+
+bool BlackBoard::NoNewStripesFor(const SeqNumberVec& seq_nums) const {
+ ALOG_ASSERT(seq_nums.size() == most_recent_stripe_seq_nums.size());
+ for (auto state_seq_num = seq_nums.begin(),
+ held_seq_num = most_recent_stripe_seq_nums_.begin();
+ state_seq_num != seq_nums.end(); ++state_seq_num, ++held_seq_num) {
+ if (*state_seq_num < *held_seq_num) {
+ return false;
+ }
+ }
+ return true;
+}
+
+avd::vnc::StripePtrVec BlackBoard::WaitForSenderWork(
+ const VncClientConnection* conn) {
+ std::unique_lock<std::mutex> guard(m_);
+ auto& state = GetStateForClient(conn);
+ D("Waiting for stripe...");
+ while (!state.closed &&
+ (!state.ready_to_receive || NoNewStripesFor(state.stripe_seq_nums))) {
+ state.new_frame_cv.wait(guard);
+ }
+ D("At least one new stripe is available, should unblock %p", conn);
+ state.ready_to_receive = false;
+ auto new_stripes = frame_buffer_watcher_->StripesNewerThan(
+ state.orientation, state.stripe_seq_nums);
+ for (auto& s : new_stripes) {
+ state.stripe_seq_nums[s->index] = s->seq_number;
+ }
+ return new_stripes;
+}
+
+void BlackBoard::WaitForAtLeastOneClientConnection() {
+ std::unique_lock<std::mutex> guard(m_);
+ while (clients_.empty()) {
+ new_client_cv_.wait(guard);
+ }
+}
+
+void BlackBoard::SetOrientation(const VncClientConnection* conn,
+ ScreenOrientation orientation) {
+ std::lock_guard<std::mutex> guard(m_);
+ auto& state = GetStateForClient(conn);
+ state.orientation = orientation;
+ // After an orientation change the vnc client will need all stripes from
+ // the new orientation, regardless of age.
+ ResetToZero(&state.stripe_seq_nums);
+}
+
+void BlackBoard::SignalClientNeedsEntireScreen(
+ const VncClientConnection* conn) {
+ std::lock_guard<std::mutex> guard(m_);
+ ResetToZero(&GetStateForClient(conn).stripe_seq_nums);
+}
+
+void BlackBoard::ResetToZero(SeqNumberVec* seq_nums) {
+ seq_nums->assign(FrameBufferWatcher::StripesPerFrame(), StripeSeqNumber{});
+}
+
+void BlackBoard::FrameBufferUpdateRequestReceived(
+ const VncClientConnection* conn) {
+ std::lock_guard<std::mutex> guard(m_);
+ D("Received frame buffer update request");
+ auto& state = GetStateForClient(conn);
+ state.ready_to_receive = true;
+ state.new_frame_cv.notify_one();
+}
+
+void BlackBoard::StopWaiting(const VncClientConnection* conn) {
+ std::lock_guard<std::mutex> guard(m_);
+ auto& state = GetStateForClient(conn);
+ state.closed = true;
+ // Wake up the thread that might be in WaitForSenderWork()
+ state.new_frame_cv.notify_one();
+}
+
+void BlackBoard::set_frame_buffer_watcher(
+ avd::vnc::FrameBufferWatcher* frame_buffer_watcher) {
+ std::lock_guard<std::mutex> guard(m_);
+ frame_buffer_watcher_ = frame_buffer_watcher;
+}
+
+void BlackBoard::set_jpeg_quality_level(int quality_level) {
+ // NOTE all vnc clients share a common jpeg quality level because the
+ // server doesn't compress per-client. The quality level for all clients
+ // will be whatever the most recent set was by any client.
+ std::lock_guard<std::mutex> guard(m_);
+ if (quality_level < kJpegMinQualityEncoding ||
+ quality_level > kJpegMaxQualityEncoding) {
+ ALOGW("Bogus jpeg quality level: %d. Quality must be in range [%d, %d]",
+ quality_level, kJpegMinQualityEncoding, kJpegMaxQualityEncoding);
+ return;
+ }
+ jpeg_quality_level_ = 55 + (5 * (quality_level + 32));
+ D("jpeg quality level set to %d%%:", jpeg_quality_level_);
+}
+
+BlackBoard::ClientFBUState& BlackBoard::GetStateForClient(
+ const VncClientConnection* conn) {
+ ALOG_ASSERT(clients_.count(conn));
+ return clients_[conn];
+}
diff --git a/guest/frontend/vnc_server/blackboard.h b/guest/frontend/vnc_server/blackboard.h
new file mode 100644
index 0000000..49a169f
--- /dev/null
+++ b/guest/frontend/vnc_server/blackboard.h
@@ -0,0 +1,102 @@
+#ifndef DEVICE_GOOGLE_GCE_GCE_UTILS_GCE_VNC_SERVER_BLACKBOARD_H_
+#define DEVICE_GOOGLE_GCE_GCE_UTILS_GCE_VNC_SERVER_BLACKBOARD_H_
+
+#include "vnc_utils.h"
+
+#include <android-base/thread_annotations.h>
+
+#include <unordered_map>
+#include <mutex>
+#include <condition_variable>
+#include <memory>
+
+namespace avd {
+namespace vnc {
+
+class VncClientConnection;
+class FrameBufferWatcher;
+using StripePtrVec = std::vector<std::shared_ptr<const Stripe>>;
+using SeqNumberVec = std::vector<StripeSeqNumber>;
+
+SeqNumberVec MakeSeqNumberVec();
+
+class BlackBoard {
+ private:
+ struct ClientFBUState {
+ bool ready_to_receive{};
+ ScreenOrientation orientation{};
+ std::condition_variable new_frame_cv;
+ SeqNumberVec stripe_seq_nums = MakeSeqNumberVec();
+ bool closed{};
+ };
+
+ public:
+ class Registerer {
+ public:
+ Registerer(BlackBoard* bb, const VncClientConnection* conn)
+ : bb_{bb}, conn_{conn} {
+ bb->Register(conn);
+ }
+ ~Registerer() {
+ bb_->Unregister(conn_);
+ }
+ Registerer(const Registerer&) = delete;
+ Registerer& operator=(const Registerer&) = delete;
+
+ private:
+ BlackBoard* bb_{};
+ const VncClientConnection* conn_{};
+ };
+
+ BlackBoard() = default;
+ BlackBoard(const BlackBoard&) = delete;
+ BlackBoard& operator=(const BlackBoard&) = delete;
+
+ bool NoNewStripesFor(const SeqNumberVec& seq_nums) const REQUIRES(m_);
+ void NewStripeReady(int index, StripeSeqNumber seq_num);
+ void Register(const VncClientConnection* conn);
+ void Unregister(const VncClientConnection* conn);
+
+ StripePtrVec WaitForSenderWork(const VncClientConnection* conn);
+
+ void WaitForAtLeastOneClientConnection();
+
+ void FrameBufferUpdateRequestReceived(const VncClientConnection* conn);
+ // Setting orientation implies needing the entire screen
+ void SetOrientation(const VncClientConnection* conn,
+ ScreenOrientation orientation);
+ void SignalClientNeedsEntireScreen(const VncClientConnection* conn);
+
+ void StopWaiting(const VncClientConnection* conn);
+
+ void set_frame_buffer_watcher(FrameBufferWatcher* frame_buffer_watcher);
+
+ // quality_level must be the value received from the client, in the range
+ // [kJpegMinQualityEncoding, kJpegMaxQualityEncoding], else it is ignored.
+ void set_jpeg_quality_level(int quality_level);
+
+ int jpeg_quality_level() const {
+ std::lock_guard<std::mutex> guard(m_);
+ return jpeg_quality_level_;
+ }
+
+ private:
+ ClientFBUState& GetStateForClient(const VncClientConnection* conn)
+ REQUIRES(m_);
+ static void ResetToZero(SeqNumberVec* seq_nums);
+
+ mutable std::mutex m_;
+ SeqNumberVec most_recent_stripe_seq_nums_ GUARDED_BY(m_) = MakeSeqNumberVec();
+ std::unordered_map<const VncClientConnection*, ClientFBUState> clients_
+ GUARDED_BY(m_);
+ int jpeg_quality_level_ GUARDED_BY(m_) = 100;
+ std::condition_variable new_client_cv_;
+ // NOTE the FrameBufferWatcher pointer itself should be
+ // guarded, but not the pointee.
+ FrameBufferWatcher* frame_buffer_watcher_ GUARDED_BY(m_){};
+};
+
+} // namespace vnc
+} // namespace avd
+
+#endif
diff --git a/guest/frontend/vnc_server/frame_buffer_watcher.cpp b/guest/frontend/vnc_server/frame_buffer_watcher.cpp
new file mode 100644
index 0000000..c592765
--- /dev/null
+++ b/guest/frontend/vnc_server/frame_buffer_watcher.cpp
@@ -0,0 +1,173 @@
+#include "vnc_utils.h"
+#include "frame_buffer_watcher.h"
+#include <ThreadSafeQueue.hpp>
+
+#include <algorithm>
+#include <cstdint>
+#include <cstring>
+#include <iterator>
+#include <memory>
+#include <mutex>
+#include <thread>
+#include <utility>
+
+#define LOG_TAG "GceVNCServer"
+#include <cutils/log.h>
+
+using avd::vnc::FrameBufferWatcher;
+
+FrameBufferWatcher::FrameBufferWatcher(BlackBoard* bb)
+ : bb_{bb}, hwcomposer{bb_} {
+ for (auto& stripes_vec : stripes_) {
+ std::generate_n(std::back_inserter(stripes_vec),
+ SimulatedHWComposer::NumberOfStripes(),
+ std::make_shared<Stripe>);
+ }
+ bb_->set_frame_buffer_watcher(this);
+ auto num_workers = std::max(std::thread::hardware_concurrency(), 1u);
+ std::generate_n(std::back_inserter(workers_), num_workers, [this] {
+ return std::thread{&FrameBufferWatcher::Worker, this};
+ });
+}
+
+FrameBufferWatcher::~FrameBufferWatcher() {
+ {
+ std::lock_guard<std::mutex> guard(m_);
+ closed_ = true;
+ }
+ for (auto& tid : workers_) {
+ tid.join();
+ }
+}
+
+bool FrameBufferWatcher::closed() const {
+ std::lock_guard<std::mutex> guard(m_);
+ return closed_;
+}
+
+avd::vnc::Stripe FrameBufferWatcher::Rotated(Stripe stripe) {
+ LOG_ALWAYS_FATAL_IF(stripe.orientation == ScreenOrientation::Landscape,
+ "Rotating a landscape stripe, this is a mistake");
+ auto w = stripe.width;
+ auto h = stripe.height;
+ const auto& raw = stripe.raw_data;
+ Message rotated(raw.size(), 0xAA);
+ for (std::uint16_t i = 0; i < w; ++i) {
+ for (std::uint16_t j = 0; j < h; ++j) {
+ auto to = (i * h + j) * BytesPerPixel();
+ auto from = (w - (i + 1) + w * j) * BytesPerPixel();
+ ALOG_ASSERT(from < raw.size());
+ ALOG_ASSERT(to < rotated.size());
+ std::memcpy(&rotated[to], &raw[from], BytesPerPixel());
+ }
+ }
+ std::swap(stripe.x, stripe.y);
+ std::swap(stripe.width, stripe.height);
+ stripe.raw_data = std::move(rotated);
+ stripe.orientation = ScreenOrientation::Landscape;
+ return stripe;
+}
+
+bool FrameBufferWatcher::StripeIsDifferentFromPrevious(
+ const Stripe& stripe) const {
+ return Stripes(stripe.orientation)[stripe.index]->raw_data != stripe.raw_data;
+}
+
+avd::vnc::StripePtrVec FrameBufferWatcher::StripesNewerThan(
+ ScreenOrientation orientation, const SeqNumberVec& seq_numbers) const {
+ std::lock_guard<std::mutex> guard(stripes_lock_);
+ const auto& stripes = Stripes(orientation);
+ ALOG_ASSERT(seq_numbers.size() == stripes.size());
+ StripePtrVec new_stripes;
+ auto seq_number_it = seq_numbers.begin();
+ std::copy_if(stripes.begin(), stripes.end(), std::back_inserter(new_stripes),
+ [seq_number_it](const StripePtrVec::value_type& s) mutable {
+ return *(seq_number_it++) < s->seq_number;
+ });
+ return new_stripes;
+}
+
+avd::vnc::StripePtrVec& FrameBufferWatcher::Stripes(
+ ScreenOrientation orientation) {
+ return stripes_[static_cast<int>(orientation)];
+}
+
+const avd::vnc::StripePtrVec& FrameBufferWatcher::Stripes(
+ ScreenOrientation orientation) const {
+ return stripes_[static_cast<int>(orientation)];
+}
+
+bool FrameBufferWatcher::UpdateMostRecentSeqNumIfStripeIsNew(
+ const Stripe& stripe) {
+ if (most_recent_identical_stripe_seq_nums_[stripe.index] <=
+ stripe.seq_number) {
+ most_recent_identical_stripe_seq_nums_[stripe.index] = stripe.seq_number;
+ return true;
+ }
+ return false;
+}
+
+bool FrameBufferWatcher::UpdateStripeIfStripeIsNew(
+ const std::shared_ptr<const Stripe>& stripe) {
+ std::lock_guard<std::mutex> guard(stripes_lock_);
+ if (UpdateMostRecentSeqNumIfStripeIsNew(*stripe)) {
+ Stripes(stripe->orientation)[stripe->index] = stripe;
+ return true;
+ }
+ return false;
+}
+
+void FrameBufferWatcher::CompressStripe(JpegCompressor* jpeg_compressor,
+ Stripe* stripe) {
+ stripe->jpeg_data = jpeg_compressor->Compress(
+ stripe->raw_data, bb_->jpeg_quality_level(), 0, 0, stripe->width,
+ stripe->height, stripe->width);
+}
+
+void FrameBufferWatcher::Worker() {
+ JpegCompressor jpeg_compressor;
+#ifdef FUZZ_TEST_VNC
+ std::default_random_engine e{std::random_device{}()};
+ std::uniform_int_distribution<int> random{0, 2};
+#endif
+ while (!closed()) {
+ auto portrait_stripe = hwcomposer.GetNewStripe();
+ if (closed()) {
+ break;
+ }
+ {
+ // TODO(haining) use if (with init) and else for c++17 instead of extra
+ // scope and continue
+ // if (std::lock_guard guard(stripes_lock_); /*condition*/) { }
+ std::lock_guard<std::mutex> guard(stripes_lock_);
+ if (!StripeIsDifferentFromPrevious(portrait_stripe)) {
+ UpdateMostRecentSeqNumIfStripeIsNew(portrait_stripe);
+ continue;
+ }
+ }
+ auto seq_num = portrait_stripe.seq_number;
+ auto index = portrait_stripe.index;
+ auto landscape_stripe = Rotated(portrait_stripe);
+ auto stripes = {std::make_shared<Stripe>(std::move(portrait_stripe)),
+ std::make_shared<Stripe>(std::move(landscape_stripe))};
+ for (auto& stripe : stripes) {
+#ifdef FUZZ_TEST_VNC
+ if (random(e)) {
+ usleep(10000);
+ }
+#endif
+ CompressStripe(&jpeg_compressor, stripe.get());
+ }
+ bool any_new_stripes = false;
+ for (auto& stripe : stripes) {
+ any_new_stripes = UpdateStripeIfStripeIsNew(stripe) || any_new_stripes;
+ }
+ if (any_new_stripes) {
+ bb_->NewStripeReady(index, seq_num);
+ }
+ }
+}
+
+int FrameBufferWatcher::StripesPerFrame() {
+ return SimulatedHWComposer::NumberOfStripes();
+}
diff --git a/guest/frontend/vnc_server/frame_buffer_watcher.h b/guest/frontend/vnc_server/frame_buffer_watcher.h
new file mode 100644
index 0000000..540f3db
--- /dev/null
+++ b/guest/frontend/vnc_server/frame_buffer_watcher.h
@@ -0,0 +1,62 @@
+#ifndef DEVICE_GOOGLE_GCE_GCE_UTILS_GCE_VNC_SERVER_FRAME_BUFFER_WATCHER_H_
+#define DEVICE_GOOGLE_GCE_GCE_UTILS_GCE_VNC_SERVER_FRAME_BUFFER_WATCHER_H_
+
+#include "blackboard.h"
+#include "jpeg_compressor.h"
+#include "simulated_hw_composer.h"
+
+#include <vector>
+#include <memory>
+#include <mutex>
+#include <thread>
+#include <utility>
+
+namespace avd {
+namespace vnc {
+class FrameBufferWatcher {
+ public:
+ explicit FrameBufferWatcher(BlackBoard* bb);
+ FrameBufferWatcher(const FrameBufferWatcher&) = delete;
+ FrameBufferWatcher& operator=(const FrameBufferWatcher&) = delete;
+ ~FrameBufferWatcher();
+
+ StripePtrVec StripesNewerThan(ScreenOrientation orientation,
+ const SeqNumberVec& seq_num) const;
+ static int StripesPerFrame();
+
+ private:
+ static Stripe Rotated(Stripe stripe);
+
+ bool closed() const;
+ bool StripeIsDifferentFromPrevious(const Stripe& stripe) const
+ REQUIRES(stripes_lock_);
+ // returns true if stripe is still considered new and seq number was updated
+ bool UpdateMostRecentSeqNumIfStripeIsNew(const Stripe& stripe)
+ REQUIRES(stripes_lock_);
+ // returns true if stripe is still considered new and was updated
+ bool UpdateStripeIfStripeIsNew(const std::shared_ptr<const Stripe>& stripe)
+ EXCLUDES(stripes_lock_);
+ // Compresses stripe->raw_data to stripe->jpeg_data
+ void CompressStripe(JpegCompressor* jpeg_compressor, Stripe* stripe);
+ void Worker();
+ void Updater();
+
+ StripePtrVec& Stripes(ScreenOrientation orientation) REQUIRES(stripes_lock_);
+ const StripePtrVec& Stripes(ScreenOrientation orientation) const
+ REQUIRES(stripes_lock_);
+
+ std::vector<std::thread> workers_;
+ mutable std::mutex stripes_lock_;
+ std::array<StripePtrVec, kNumOrientations> stripes_ GUARDED_BY(stripes_lock_);
+ SeqNumberVec most_recent_identical_stripe_seq_nums_
+ GUARDED_BY(stripes_lock_) = MakeSeqNumberVec();
+ mutable std::mutex m_;
+ bool closed_ GUARDED_BY(m_){};
+ BlackBoard* bb_{};
+ SimulatedHWComposer hwcomposer{bb_};
+};
+
+} // namespace vnc
+} // namespace avd
+
+#endif
diff --git a/guest/frontend/vnc_server/jpeg_compressor.cpp b/guest/frontend/vnc_server/jpeg_compressor.cpp
new file mode 100644
index 0000000..3129184
--- /dev/null
+++ b/guest/frontend/vnc_server/jpeg_compressor.cpp
@@ -0,0 +1,65 @@
+#include "jpeg_compressor.h"
+#include "vnc_utils.h"
+
+#include <stdio.h> // stdio.h must appear before jpeglib.h
+#include <jpeglib.h>
+
+#define LOG_TAG "GceVNCServer"
+#include <cutils/log.h>
+
+using avd::vnc::JpegCompressor;
+
+namespace {
+void InitCinfo(jpeg_compress_struct* cinfo, jpeg_error_mgr* err,
+ std::uint16_t width, std::uint16_t height, int jpeg_quality) {
+ cinfo->err = jpeg_std_error(err);
+ jpeg_create_compress(cinfo);
+
+ cinfo->image_width = width;
+ cinfo->image_height = height;
+ cinfo->input_components = avd::vnc::BytesPerPixel();
+ cinfo->in_color_space = JCS_EXT_RGBX;
+
+ jpeg_set_defaults(cinfo);
+ jpeg_set_quality(cinfo, jpeg_quality, true);
+}
+} // namespace
+
+avd::vnc::Message JpegCompressor::Compress(const Message& frame,
+ int jpeg_quality, std::uint16_t x,
+ std::uint16_t y, std::uint16_t width,
+ std::uint16_t height,
+ int screen_width) {
+ jpeg_compress_struct cinfo{};
+ jpeg_error_mgr err{};
+ InitCinfo(&cinfo, &err, width, height, jpeg_quality);
+
+ auto* compression_buffer = buffer_.get();
+ auto compression_buffer_size = buffer_capacity_;
+ jpeg_mem_dest(&cinfo, &compression_buffer, &compression_buffer_size);
+ jpeg_start_compress(&cinfo, true);
+
+ while (cinfo.next_scanline < cinfo.image_height) {
+ auto row = static_cast<JSAMPROW>(const_cast<std::uint8_t*>(
+ &frame[(y * screen_width * BytesPerPixel()) +
+ (cinfo.next_scanline * BytesPerPixel() * screen_width) +
+ (x * BytesPerPixel())]));
+ jpeg_write_scanlines(&cinfo, &row, 1);
+ }
+ jpeg_finish_compress(&cinfo);
+ jpeg_destroy_compress(&cinfo);
+
+ UpdateBuffer(compression_buffer, compression_buffer_size);
+ return {compression_buffer, compression_buffer + compression_buffer_size};
+}
+
+void JpegCompressor::UpdateBuffer(std::uint8_t* compression_buffer,
+ unsigned long compression_buffer_size) {
+ if (buffer_capacity_ < compression_buffer_size) {
+ ALOG_ASSERT(buffer_ != compression_buffer);
+ buffer_capacity_ = compression_buffer_size;
+ buffer_.reset(compression_buffer);
+ } else {
+ ALOG_ASSERT(buffer_ == compression_buffer);
+ }
+}
diff --git a/guest/frontend/vnc_server/jpeg_compressor.h b/guest/frontend/vnc_server/jpeg_compressor.h
new file mode 100644
index 0000000..db93267
--- /dev/null
+++ b/guest/frontend/vnc_server/jpeg_compressor.h
@@ -0,0 +1,41 @@
+#ifndef DEVICE_GOOGLE_GCE_GCE_UTILS_GCE_VNC_SERVER_JPEG_COMPRESSOR_H_
+#define DEVICE_GOOGLE_GCE_GCE_UTILS_GCE_VNC_SERVER_JPEG_COMPRESSOR_H_
+
+#include "vnc_utils.h"
+
+#include <memory>
+#include <cstdlib>
+#include <cstdint>
+
+namespace avd {
+namespace vnc {
+
+// libjpeg-turbo with jpeg_mem_dest (using memory as a destination) is funky.
+// If you give it a buffer that is big enough it will use it.
+// If you give it a buffer that is too small, it will allocate a new buffer
+// but will NOT free the buffer you gave it.
+// This class keeps track of the capacity of the working buffer, and frees the
+// old buffer if libjpeg-turbo silently discards it.
+class JpegCompressor {
+ public:
+ Message Compress(const Message& frame, int jpeg_quality, std::uint16_t x,
+ std::uint16_t y, std::uint16_t width, std::uint16_t height,
+ int screen_width);
+
+ private:
+ void UpdateBuffer(std::uint8_t* compression_buffer,
+ unsigned long compression_buffer_size);
+ struct Freer {
+ void operator()(void* p) const {
+ std::free(p);
+ }
+ };
+
+ std::unique_ptr<std::uint8_t, Freer> buffer_;
+ unsigned long buffer_capacity_{};
+};
+
+} // namespace vnc
+} // namespace avd
+
+#endif
diff --git a/guest/frontend/vnc_server/keysyms.h b/guest/frontend/vnc_server/keysyms.h
new file mode 100644
index 0000000..7973957
--- /dev/null
+++ b/guest/frontend/vnc_server/keysyms.h
@@ -0,0 +1,94 @@
+#ifndef DEVICE_GOOGLE_GCE_KEYSYMS_H_
+#define DEVICE_GOOGLE_GCE_KEYSYMS_H_
+
+namespace avd {
+namespace xk {
+
+constexpr uint32_t BackSpace = 0xff08,
+ Tab = 0xff09,
+ Return = 0xff0d,
+ Enter = Return,
+ Escape = 0xff1b,
+ MultiKey = 0xff20,
+ Insert = 0xff63,
+ Delete = 0xffff,
+ Pause = 0xff13,
+ Home = 0xff50,
+ End = 0xff57,
+ PageUp = 0xff55,
+ PageDown = 0xff56,
+ Left = 0xff51,
+ Up = 0xff52,
+ Right = 0xff53,
+ Down = 0xff54,
+ F1 = 0xffbe,
+ F2 = 0xffbf,
+ F3 = 0xffc0,
+ F4 = 0xffc1,
+ F5 = 0xffc2,
+ F6 = 0xffc3,
+ F7 = 0xffc4,
+ F8 = 0xffc5,
+ F9 = 0xffc6,
+ F10 = 0xffc7,
+ F11 = 0xffc8,
+ F12 = 0xffc9,
+ F13 = 0xffca,
+ F14 = 0xffcb,
+ F15 = 0xffcc,
+ F16 = 0xffcd,
+ F17 = 0xffce,
+ F18 = 0xffcf,
+ F19 = 0xffd0,
+ F20 = 0xffd1,
+ F21 = 0xffd2,
+ F22 = 0xffd3,
+ F23 = 0xffd4,
+ F24 = 0xffd5,
+ ShiftLeft = 0xffe1,
+ ShiftRight = 0xffe2,
+ ControlLeft = 0xffe3,
+ ControlRight = 0xffe4,
+ MetaLeft = 0xffe7,
+ MetaRight = 0xffe8,
+ AltLeft = 0xffe9,
+ AltRight = 0xffea,
+ CapsLock = 0xffe5,
+ NumLock = 0xff7f,
+ ScrollLock = 0xff14,
+ Keypad0 = 0xffb0,
+ Keypad1 = 0xffb1,
+ Keypad2 = 0xffb2,
+ Keypad3 = 0xffb3,
+ Keypad4 = 0xffb4,
+ Keypad5 = 0xffb5,
+ Keypad6 = 0xffb6,
+ Keypad7 = 0xffb7,
+ Keypad8 = 0xffb8,
+ Keypad9 = 0xffb9,
+ KeypadMultiply = 0xffaa,
+ KeypadSubtract = 0xffad,
+ KeypadAdd = 0xffab,
+ KeypadDecimal = 0xffae,
+ KeypadEnter = 0xff8d,
+ KeypadDivide = 0xffaf,
+ KeypadEqual = 0xffbd,
+ PlusMinus = 0xb1,
+ SysReq = 0xff15,
+ LineFeed = 0xff0a,
+ KeypadSeparator = 0xffac,
+ Yen = 0xa5,
+ Cancel = 0xff69,
+ Undo = 0xff65,
+ Redo = 0xff66,
+ Find = 0xff68,
+ Print = 0xff61,
+ VolumeDown = 0x1008ff11,
+ Mute = 0x1008ff12,
+ VolumeUp = 0x1008ff13,
+ Menu = 0xff67,
+ VNCMenu = 0xffed; // VNC seems to translate MENU to this
+
+} // namespace xk
+} // namespace avd
+#endif
diff --git a/guest/frontend/vnc_server/main.cpp b/guest/frontend/vnc_server/main.cpp
new file mode 100644
index 0000000..2436a65
--- /dev/null
+++ b/guest/frontend/vnc_server/main.cpp
@@ -0,0 +1,25 @@
+#include "vnc_server.h"
+
+#include <signal.h>
+#include <algorithm>
+#include <string>
+
+namespace {
+constexpr int kVncServerPort = 6444;
+
+// TODO(haining) use gflags when available
+bool HasAggressiveFlag(int argc, char* argv[]) {
+ const std::string kAggressive = "--aggressive";
+ auto end = argv + argc;
+ return std::find(argv, end, kAggressive) != end;
+}
+} // namespace
+
+int main(int argc, char* argv[]) {
+ struct sigaction new_action, old_action;
+ memset(&new_action, 0, sizeof(new_action));
+ new_action.sa_handler = SIG_IGN;
+ sigaction(SIGPIPE, &new_action, &old_action);
+ avd::vnc::VncServer vnc_server(kVncServerPort, HasAggressiveFlag(argc, argv));
+ vnc_server.MainLoop();
+}
diff --git a/guest/frontend/vnc_server/simulated_hw_composer.cpp b/guest/frontend/vnc_server/simulated_hw_composer.cpp
new file mode 100644
index 0000000..84a87d2
--- /dev/null
+++ b/guest/frontend/vnc_server/simulated_hw_composer.cpp
@@ -0,0 +1,106 @@
+#include "simulated_hw_composer.h"
+
+using avd::vnc::SimulatedHWComposer;
+
+SimulatedHWComposer::SimulatedHWComposer(BlackBoard* bb)
+ :
+#ifdef FUZZ_TEST_VNC
+ engine_{std::random_device{}()},
+#endif
+ control_{GceFrameBufferControl::getInstance()},
+ bb_{bb},
+ stripes_(kMaxQueueElements, &SimulatedHWComposer::EraseHalfOfElements) {
+ void* p{};
+ GceFrameBuffer::OpenAndMapFrameBuffer(&p, &frame_buffer_fd_);
+ frame_buffer_memory_ = static_cast<char*>(p);
+ stripe_maker_ = std::thread(&SimulatedHWComposer::MakeStripes, this);
+}
+
+SimulatedHWComposer::~SimulatedHWComposer() {
+ close();
+ stripe_maker_.join();
+ GceFrameBuffer::UnmapAndCloseFrameBuffer(frame_buffer_memory_,
+ frame_buffer_fd_);
+}
+
+avd::vnc::Stripe SimulatedHWComposer::GetNewStripe() {
+ auto s = stripes_.Pop();
+#ifdef FUZZ_TEST_VNC
+ if (random_(engine_)) {
+ usleep(7000);
+ stripes_.Push(std::move(s));
+ s = stripes_.Pop();
+ }
+#endif
+ return s;
+}
+
+bool SimulatedHWComposer::closed() {
+ std::lock_guard<std::mutex> guard(m_);
+ return closed_;
+}
+
+void SimulatedHWComposer::close() {
+ std::lock_guard<std::mutex> guard(m_);
+ closed_ = true;
+}
+
+// Assuming the number of stripes is less than half the size of the queue
+// this will be safe as the newest stripes won't be lost. In the real
+// hwcomposer, where stripes are coming in a different order, the full
+// queue case would probably need a different approach to be safe.
+void SimulatedHWComposer::EraseHalfOfElements(
+ ThreadSafeQueue<Stripe>::QueueImpl* q) {
+ q->erase(q->begin(), std::next(q->begin(), kMaxQueueElements / 2));
+}
+
+void SimulatedHWComposer::MakeStripes() {
+ std::uint32_t previous_seq_num{};
+ auto screen_height = ActualScreenHeight();
+ Message raw_screen;
+ std::uint64_t stripe_seq_num = 1;
+ while (!closed()) {
+ bb_->WaitForAtLeastOneClientConnection();
+ int y_offset{};
+ control_.WaitForFrameBufferChangeSince(previous_seq_num, &y_offset,
+ &previous_seq_num, nullptr);
+
+ const auto* frame_start =
+ frame_buffer_memory_ + y_offset * ActualScreenWidth() * BytesPerPixel();
+ raw_screen.assign(frame_start, frame_start + ScreenSizeInBytes());
+
+ for (int i = 0; i < kNumStripes; ++i) {
+ ++stripe_seq_num;
+ std::uint16_t y = (screen_height / kNumStripes) * i;
+
+ // Last frames on the right and/or bottom handle extra pixels
+ // when a screen dimension is not evenly divisible by Frame::kNumSlots.
+ std::uint16_t height =
+ screen_height / kNumStripes +
+ (i + 1 == kNumStripes ? screen_height % kNumStripes : 0);
+ const auto* raw_start =
+ &raw_screen[y * ActualScreenWidth() * BytesPerPixel()];
+ const auto* raw_end =
+ raw_start + (height * ActualScreenWidth() * BytesPerPixel());
+ // creating a named object and setting individual data members in order
+ // to make klp happy
+ // TODO (haining) construct this inside the call when not compiling
+ // on klp
+ Stripe s{};
+ s.index = i;
+ s.frame_id = previous_seq_num;
+ s.x = 0;
+ s.y = y;
+ s.width = ActualScreenWidth();
+ s.height = height;
+ s.raw_data.assign(raw_start, raw_end);
+ s.seq_number = StripeSeqNumber{stripe_seq_num};
+ s.orientation = ScreenOrientation::Portrait;
+ stripes_.Push(std::move(s));
+ }
+ }
+}
+
+int SimulatedHWComposer::NumberOfStripes() {
+ return kNumStripes;
+}
diff --git a/guest/frontend/vnc_server/simulated_hw_composer.h b/guest/frontend/vnc_server/simulated_hw_composer.h
new file mode 100644
index 0000000..3b68548
--- /dev/null
+++ b/guest/frontend/vnc_server/simulated_hw_composer.h
@@ -0,0 +1,57 @@
+#ifndef DEVICE_GOOGLE_GCE_GCE_UTILS_GCE_VNC_SERVER_SIMULATED_HW_COMPOSER_H_
+#define DEVICE_GOOGLE_GCE_GCE_UTILS_GCE_VNC_SERVER_SIMULATED_HW_COMPOSER_H_
+
+#include "blackboard.h"
+
+#include <GceFrameBuffer.h>
+#include <GceFrameBufferControl.h>
+#include <ThreadSafeQueue.hpp>
+
+#include <mutex>
+#include <thread>
+
+#include <condition_variable>
+#ifdef FUZZ_TEST_VNC
+#include <random>
+#endif
+
+namespace avd {
+namespace vnc {
+class SimulatedHWComposer {
+ public:
+ SimulatedHWComposer(BlackBoard* bb);
+ SimulatedHWComposer(const SimulatedHWComposer&) = delete;
+ SimulatedHWComposer& operator=(const SimulatedHWComposer&) = delete;
+ ~SimulatedHWComposer();
+
+ Stripe GetNewStripe();
+
+ // NOTE not constexpr on purpose
+ static int NumberOfStripes();
+
+ private:
+ bool closed();
+ void close();
+ static void EraseHalfOfElements(ThreadSafeQueue<Stripe>::QueueImpl* q);
+ void MakeStripes();
+
+#ifdef FUZZ_TEST_VNC
+ std::default_random_engine engine_;
+ std::uniform_int_distribution<int> random_ =
+ std::uniform_int_distribution<int>{0, 2};
+#endif
+ static constexpr int kNumStripes = 8;
+ constexpr static std::size_t kMaxQueueElements = 64;
+ bool closed_ GUARDED_BY(m_){};
+ std::mutex m_;
+ GceFrameBufferControl& control_;
+ BlackBoard* bb_{};
+ ThreadSafeQueue<Stripe> stripes_;
+ std::thread stripe_maker_;
+ char* frame_buffer_memory_{};
+ int frame_buffer_fd_{};
+};
+} // namespace vnc
+} // namespace avd
+
+#endif
diff --git a/guest/frontend/vnc_server/tcp_socket.cpp b/guest/frontend/vnc_server/tcp_socket.cpp
new file mode 100644
index 0000000..2ccdcf9
--- /dev/null
+++ b/guest/frontend/vnc_server/tcp_socket.cpp
@@ -0,0 +1,61 @@
+#include "tcp_socket.h"
+#include <cerrno>
+
+#include <cutils/sockets.h>
+#define LOG_TAG "GceVNCServer"
+#include <cutils/log.h>
+
+using avd::vnc::ClientSocket;
+using avd::vnc::ServerSocket;
+using avd::vnc::Message;
+
+Message ClientSocket::Recv(size_t length) {
+ Message buf(length);
+ ssize_t total_read = 0;
+ while (total_read < static_cast<ssize_t>(length)) {
+ auto just_read = read(fd_, &buf[total_read], buf.size() - total_read);
+ if (just_read <= 0) {
+ if (just_read < 0) {
+ ALOGE("read() error: %s", strerror(errno));
+ }
+ other_side_closed_ = true;
+ return Message{};
+ }
+ total_read += just_read;
+ }
+ ALOG_ASSERT(total_read == static_cast<ssize_t>(length));
+ return buf;
+}
+
+ssize_t ClientSocket::Send(const uint8_t* data, std::size_t size) {
+ std::lock_guard<std::mutex> lock(send_lock_);
+ ssize_t written{};
+ while (written < static_cast<ssize_t>(size)) {
+ auto just_written = write(fd_, data + written, size - written);
+ if (just_written <= 0) {
+ ALOGI("Couldn't write to vnc client: %s", strerror(errno));
+ return just_written;
+ }
+ written += just_written;
+ }
+ return written;
+}
+
+ssize_t ClientSocket::Send(const Message& message) {
+ return Send(&message[0], message.size());
+}
+
+ServerSocket::ServerSocket(int port)
+ : fd_{socket_inaddr_any_server(port, SOCK_STREAM)} {
+ if (fd_ < 0) {
+ LOG_FATAL("Couldn't open streaming server on port %d", port);
+ }
+}
+
+ClientSocket ServerSocket::Accept() {
+ int client = accept(fd_, nullptr, nullptr);
+ if (client < 0) {
+ LOG_FATAL("Error attemping to accept: %s", strerror(errno));
+ }
+ return ClientSocket{client};
+}
diff --git a/guest/frontend/vnc_server/tcp_socket.h b/guest/frontend/vnc_server/tcp_socket.h
new file mode 100644
index 0000000..701ea0e
--- /dev/null
+++ b/guest/frontend/vnc_server/tcp_socket.h
@@ -0,0 +1,87 @@
+#ifndef DEVICE_GOOGLE_GCE_GCE_UTILS_GCE_VNC_SERVER_TCPSOCKET_H_
+#define DEVICE_GOOGLE_GCE_GCE_UTILS_GCE_VNC_SERVER_TCPSOCKET_H_
+
+#include "vnc_utils.h"
+
+#include <mutex>
+#include <cstdint>
+#include <cstddef>
+
+#include <unistd.h>
+
+namespace avd {
+namespace vnc {
+
+class ServerSocket;
+
+// Recv and Send wait until all data has been received or sent.
+// Send is thread safe in this regard, Recv is not.
+class ClientSocket {
+ public:
+ ClientSocket(ClientSocket&& other) : fd_{other.fd_} {
+ other.fd_ = -1;
+ }
+
+ ClientSocket& operator=(ClientSocket&& other) {
+ if (fd_ >= 0) {
+ close(fd_);
+ }
+ fd_ = other.fd_;
+ other.fd_ = -1;
+ return *this;
+ }
+
+ ClientSocket(const ClientSocket&) = delete;
+ ClientSocket& operator=(const ClientSocket&) = delete;
+
+ ~ClientSocket() {
+ if (fd_ >= 0) {
+ close(fd_);
+ }
+ }
+
+ Message Recv(std::size_t length);
+ ssize_t Send(const std::uint8_t* data, std::size_t size);
+ ssize_t Send(const Message& message);
+
+ template <std::size_t N>
+ ssize_t Send(const std::uint8_t (&data)[N]) {
+ return Send(data, N);
+ }
+
+ bool closed() const {
+ return other_side_closed_;
+ }
+
+ private:
+ friend ServerSocket;
+ explicit ClientSocket(int fd) : fd_(fd) {}
+
+ int fd_ = -1;
+ bool other_side_closed_{};
+ std::mutex send_lock_;
+};
+
+class ServerSocket {
+ public:
+ explicit ServerSocket(int port);
+
+ ServerSocket(const ServerSocket&) = delete;
+ ServerSocket& operator=(const ServerSocket&) = delete;
+
+ ~ServerSocket() {
+ if (fd_ >= 0) {
+ close(fd_);
+ }
+ }
+
+ ClientSocket Accept();
+
+ private:
+ int fd_ = -1;
+};
+
+} // namespace vnc
+} // namespace avd
+
+#endif
diff --git a/guest/frontend/vnc_server/virtual_inputs.cpp b/guest/frontend/vnc_server/virtual_inputs.cpp
new file mode 100644
index 0000000..c946d57
--- /dev/null
+++ b/guest/frontend/vnc_server/virtual_inputs.cpp
@@ -0,0 +1,19 @@
+#include "virtual_inputs.h"
+#include <mutex>
+
+using avd::vnc::VirtualInputs;
+
+void VirtualInputs::GenerateKeyPressEvent(int code, bool down) {
+ std::lock_guard<std::mutex> guard(m_);
+ virtual_keyboard_.GenerateKeyPressEvent(code, down);
+}
+
+void VirtualInputs::PressPowerButton(bool down) {
+ std::lock_guard<std::mutex> guard(m_);
+ virtual_power_button_.HandleButtonPressEvent(down);
+}
+
+void VirtualInputs::HandlePointerEvent(bool touch_down, int x, int y) {
+ std::lock_guard<std::mutex> guard(m_);
+ virtual_touch_pad_.HandlePointerEvent(touch_down, x, y);
+}
diff --git a/guest/frontend/vnc_server/virtual_inputs.h b/guest/frontend/vnc_server/virtual_inputs.h
new file mode 100644
index 0000000..fba9ede
--- /dev/null
+++ b/guest/frontend/vnc_server/virtual_inputs.h
@@ -0,0 +1,32 @@
+#ifndef DEVICE_GOOGLE_GCE_GCE_UTILS_GCE_VNC_SERVER_VIRTUAL_INPUTS_H_
+#define DEVICE_GOOGLE_GCE_GCE_UTILS_GCE_VNC_SERVER_VIRTUAL_INPUTS_H_
+
+#include "VirtualInputDevice.h"
+#include "vnc_utils.h"
+
+#include <linux/input.h>
+#include <android-base/thread_annotations.h>
+
+#include <mutex>
+
+namespace avd {
+namespace vnc {
+
+class VirtualInputs {
+ public:
+ void GenerateKeyPressEvent(int code, bool down);
+ void PressPowerButton(bool down);
+ void HandlePointerEvent(bool touch_down, int x, int y);
+
+ private:
+ std::mutex m_;
+ VirtualKeyboard virtual_keyboard_ GUARDED_BY(m_){"remote-keyboard"};
+ VirtualTouchPad virtual_touch_pad_ GUARDED_BY(m_){
+ "remote-touchpad", ActualScreenWidth(), ActualScreenHeight()};
+ VirtualButton virtual_power_button_ GUARDED_BY(m_){"remote-power", KEY_POWER};
+};
+
+} // namespace vnc
+} // namespace avd
+
+#endif
diff --git a/guest/frontend/vnc_server/vnc_client_connection.cpp b/guest/frontend/vnc_server/vnc_client_connection.cpp
new file mode 100644
index 0000000..b751cf6
--- /dev/null
+++ b/guest/frontend/vnc_server/vnc_client_connection.cpp
@@ -0,0 +1,697 @@
+#include "vnc_client_connection.h"
+#include "vnc_utils.h"
+#include "tcp_socket.h"
+#include "keysyms.h"
+
+#include "InitialMetadataReader.h"
+#include <GceFrameBuffer.h>
+#include <gce_sensors_message.h>
+#include <sensors.h>
+
+#include <netinet/in.h>
+#include <sys/time.h>
+
+#include <algorithm>
+#include <cmath>
+#include <cstdint>
+#include <cstring>
+#include <memory>
+#include <mutex>
+#include <string>
+#include <thread>
+#include <utility>
+#include <vector>
+
+#ifdef LOG_TAG
+#undef LOG_TAG
+#endif
+#define LOG_TAG "GceVNCServer"
+#include <cutils/log.h>
+
+using avd::vnc::Message;
+using avd::vnc::VncClientConnection;
+using avd::vnc::Stripe;
+using avd::vnc::StripePtrVec;
+
+namespace {
+class BigEndianChecker {
+ public:
+ BigEndianChecker() {
+ uint32_t u = 1;
+ is_big_endian_ = *reinterpret_cast<const char*>(&u) == 0;
+ }
+ bool operator()() const {
+ return is_big_endian_;
+ }
+
+ private:
+ bool is_big_endian_{};
+};
+
+const BigEndianChecker ImBigEndian;
+
+constexpr int32_t kDesktopSizeEncoding = -223;
+constexpr int32_t kTightEncoding = 7;
+
+// These are the lengths not counting the first byte. The first byte
+// indicates the message type.
+constexpr size_t kSetPixelFormatLength = 19;
+constexpr size_t kFramebufferUpdateRequestLength = 9;
+constexpr size_t kSetEncodingsLength = 3; // more bytes follow
+constexpr size_t kKeyEventLength = 7;
+constexpr size_t kPointerEventLength = 5;
+constexpr size_t kClientCutTextLength = 7; // more bytes follow
+
+void AppendInNetworkByteOrder(Message* msg, const std::uint8_t b) {
+ msg->push_back(b);
+}
+
+void AppendInNetworkByteOrder(Message* msg, const std::uint16_t s) {
+ const std::uint16_t n = htons(s);
+ auto p = reinterpret_cast<const std::uint8_t*>(&n);
+ msg->insert(msg->end(), p, p + sizeof n);
+}
+
+void AppendInNetworkByteOrder(Message* msg, const std::uint32_t w) {
+ const std::uint32_t n = htonl(w);
+ auto p = reinterpret_cast<const std::uint8_t*>(&n);
+ msg->insert(msg->end(), p, p + sizeof n);
+}
+
+void AppendInNetworkByteOrder(Message* msg, const int32_t w) {
+ std::uint32_t u{};
+ std::memcpy(&u, &w, sizeof u);
+ AppendInNetworkByteOrder(msg, u);
+}
+
+void AppendInNetworkByteOrder(Message* msg, const std::string& str) {
+ msg->insert(msg->end(), str.begin(), str.end());
+}
+
+void AppendToMessage(Message*) {}
+
+template <typename T, typename... Ts>
+void AppendToMessage(Message* msg, T v, Ts... vals) {
+ AppendInNetworkByteOrder(msg, v);
+ AppendToMessage(msg, vals...);
+}
+
+template <typename... Ts>
+Message CreateMessage(Ts... vals) {
+ Message m;
+ AppendToMessage(&m, vals...);
+ return m;
+}
+
+std::string HostName() {
+ return avd::InitialMetadataReader::getInstance()->GetInstanceHostname();
+}
+
+std::uint16_t uint16_tAt(const void* p) {
+ std::uint16_t u{};
+ std::memcpy(&u, p, sizeof u);
+ return ntohs(u);
+}
+
+std::uint32_t uint32_tAt(const void* p) {
+ std::uint32_t u{};
+ std::memcpy(&u, p, sizeof u);
+ return ntohl(u);
+}
+
+std::int32_t int32_tAt(const void* p) {
+ std::uint32_t u{};
+ std::memcpy(&u, p, sizeof u);
+ u = ntohl(u);
+ std::int32_t s{};
+ std::memcpy(&s, &u, sizeof s);
+ return s;
+}
+
+std::uint32_t RedVal(std::uint32_t pixel) {
+ return (pixel >> GceFrameBuffer::kRedShift) &
+ ((0x1 << GceFrameBuffer::kRedBits) - 1);
+}
+
+std::uint32_t BlueVal(std::uint32_t pixel) {
+ return (pixel >> GceFrameBuffer::kBlueShift) &
+ ((0x1 << GceFrameBuffer::kBlueBits) - 1);
+}
+
+std::uint32_t GreenVal(std::uint32_t pixel) {
+ return (pixel >> GceFrameBuffer::kGreenShift) &
+ ((0x1 << GceFrameBuffer::kGreenBits) - 1);
+}
+}
+namespace avd {
+namespace vnc {
+bool operator==(const VncClientConnection::FrameBufferUpdateRequest& lhs,
+ const VncClientConnection::FrameBufferUpdateRequest& rhs) {
+ return lhs.x_pos == rhs.x_pos && lhs.y_pos == rhs.y_pos &&
+ lhs.width == rhs.width && lhs.height == rhs.height;
+}
+
+bool operator!=(const VncClientConnection::FrameBufferUpdateRequest& lhs,
+ const VncClientConnection::FrameBufferUpdateRequest& rhs) {
+ return !(lhs == rhs);
+}
+} // namespace vnc
+} // namespace avd // namespace
+
+VncClientConnection::VncClientConnection(ClientSocket client,
+ VirtualInputs* virtual_inputs,
+ BlackBoard* bb, bool aggressive)
+ : client_{std::move(client)},
+ sensor_event_hal_{avd::SharedFD::SocketSeqPacketClient(
+ gce_sensors_message::kSensorsHALSocketName)},
+ virtual_inputs_{virtual_inputs},
+ bb_{bb} {
+ frame_buffer_request_handler_tid_ = std::thread(
+ &VncClientConnection::FrameBufferUpdateRequestHandler, this, aggressive);
+}
+
+VncClientConnection::~VncClientConnection() {
+ {
+ std::lock_guard<std::mutex> guard(m_);
+ closed_ = true;
+ }
+ bb_->StopWaiting(this);
+ frame_buffer_request_handler_tid_.join();
+}
+
+void VncClientConnection::StartSession() {
+ SetupProtocol();
+ if (client_.closed()) {
+ return;
+ }
+ SetupSecurityType();
+ if (client_.closed()) {
+ return;
+ }
+ GetClientInit();
+ if (client_.closed()) {
+ return;
+ }
+ SendServerInit();
+ if (client_.closed()) {
+ return;
+ }
+ NormalSession();
+ ALOGI("vnc session terminated");
+}
+
+bool VncClientConnection::closed() {
+ std::lock_guard<std::mutex> guard(m_);
+ return closed_;
+}
+
+void VncClientConnection::SetupProtocol() {
+ static constexpr char kRFBVersion[] = "RFB 003.008\n";
+ static constexpr auto kVersionLen = (sizeof kRFBVersion) - 1;
+ client_.Send(reinterpret_cast<const std::uint8_t*>(kRFBVersion), kVersionLen);
+ auto client_protocol = client_.Recv(kVersionLen);
+ if (std::memcmp(&client_protocol[0], kRFBVersion,
+ std::min(kVersionLen, client_protocol.size())) != 0) {
+ client_protocol.push_back('\0');
+ ALOGE("vnc client wants a different protocol: %s",
+ reinterpret_cast<const char*>(&client_protocol[0]));
+ }
+}
+
+void VncClientConnection::SetupSecurityType() {
+ static constexpr std::uint8_t kNoneSecurity = 0x1;
+ // The first '0x1' indicates the number of items that follow
+ static constexpr std::uint8_t kOnlyNoneSecurity[] = {0x01, kNoneSecurity};
+ client_.Send(kOnlyNoneSecurity);
+ auto client_security = client_.Recv(1);
+ if (client_.closed()) {
+ return;
+ }
+ if (client_security.front() != kNoneSecurity) {
+ ALOGE("vnc client is asking for security type %d",
+ static_cast<int>(client_security.front()));
+ }
+ static constexpr std::uint8_t kZero[4] = {};
+ client_.Send(kZero);
+}
+
+void VncClientConnection::GetClientInit() {
+ auto client_shared = client_.Recv(1);
+}
+
+void VncClientConnection::SendServerInit() {
+ const std::string server_name = HostName();
+ std::lock_guard<std::mutex> guard(m_);
+ auto server_init = CreateMessage(
+ static_cast<std::uint16_t>(ScreenWidth()),
+ static_cast<std::uint16_t>(ScreenHeight()), pixel_format_.bits_per_pixel,
+ pixel_format_.depth, pixel_format_.big_endian, pixel_format_.true_color,
+ pixel_format_.red_max, pixel_format_.green_max, pixel_format_.blue_max,
+ pixel_format_.red_shift, pixel_format_.green_shift,
+ pixel_format_.blue_shift, std::uint16_t{}, // padding
+ std::uint8_t{}, // padding
+ static_cast<std::uint32_t>(server_name.size()), server_name);
+ client_.Send(server_init);
+}
+
+Message VncClientConnection::MakeFrameBufferUpdateHeader(
+ std::uint16_t num_stripes) {
+ return CreateMessage(std::uint8_t{0}, // message-type
+ std::uint8_t{}, // padding
+ std::uint16_t{num_stripes});
+}
+
+void VncClientConnection::AppendRawStripeHeader(Message* frame_buffer_update,
+ const Stripe& stripe) {
+ static constexpr int32_t kRawEncoding = 0;
+ AppendToMessage(frame_buffer_update, std::uint16_t{stripe.x},
+ std::uint16_t{stripe.y}, std::uint16_t{stripe.width},
+ std::uint16_t{stripe.height}, kRawEncoding);
+}
+
+void VncClientConnection::AppendJpegSize(Message* frame_buffer_update,
+ size_t jpeg_size) {
+ constexpr size_t kJpegSizeOneByteMax = 127;
+ constexpr size_t kJpegSizeTwoByteMax = 16383;
+ constexpr size_t kJpegSizeThreeByteMax = 4194303;
+
+ if (jpeg_size <= kJpegSizeOneByteMax) {
+ AppendToMessage(frame_buffer_update, static_cast<std::uint8_t>(jpeg_size));
+ } else if (jpeg_size <= kJpegSizeTwoByteMax) {
+ auto sz = static_cast<std::uint32_t>(jpeg_size);
+ AppendToMessage(frame_buffer_update,
+ static_cast<std::uint8_t>((sz & 0x7F) | 0x80),
+ static_cast<std::uint8_t>((sz >> 7) & 0xFF));
+ } else {
+ if (jpeg_size > kJpegSizeThreeByteMax) {
+ LOG_FATAL("jpeg size is too big: %d must be under %zu", jpeg_size,
+ kJpegSizeThreeByteMax);
+ }
+ const auto sz = static_cast<std::uint32_t>(jpeg_size);
+ AppendToMessage(frame_buffer_update,
+ static_cast<std::uint8_t>((sz & 0x7F) | 0x80),
+ static_cast<std::uint8_t>(((sz >> 7) & 0x7F) | 0x80),
+ static_cast<std::uint8_t>((sz >> 14) & 0xFF));
+ }
+}
+
+void VncClientConnection::AppendRawStripe(Message* frame_buffer_update,
+ const Stripe& stripe) const {
+ using Pixel = GceFrameBuffer::Pixel;
+ auto& fbu = *frame_buffer_update;
+ AppendRawStripeHeader(&fbu, stripe);
+ auto init_size = fbu.size();
+ fbu.insert(fbu.end(), stripe.raw_data.begin(), stripe.raw_data.end());
+ for (size_t i = init_size; i < fbu.size(); i += sizeof(Pixel)) {
+ ALOG_ASSERT((i + sizeof(Pixel)) < fbu.size());
+ Pixel raw_pixel{};
+ std::memcpy(&raw_pixel, &fbu[i], sizeof raw_pixel);
+ auto red = RedVal(raw_pixel);
+ auto green = GreenVal(raw_pixel);
+ auto blue = BlueVal(raw_pixel);
+ Pixel pixel = Pixel{red} << pixel_format_.red_shift |
+ Pixel{blue} << pixel_format_.blue_shift |
+ Pixel{green} << pixel_format_.green_shift;
+
+ if (bool(pixel_format_.big_endian) != ImBigEndian()) {
+ // flip them bits (refactor into function)
+ auto p = reinterpret_cast<char*>(&pixel);
+ std::swap(p[0], p[3]);
+ std::swap(p[1], p[2]);
+ }
+ ALOG_ASSERT(i + sizeof pixel <= fbu.size());
+ std::memcpy(&fbu[i], &pixel, sizeof pixel);
+ }
+}
+
+Message VncClientConnection::MakeRawFrameBufferUpdate(
+ const StripePtrVec& stripes) const {
+ auto fbu =
+ MakeFrameBufferUpdateHeader(static_cast<std::uint16_t>(stripes.size()));
+ for (auto& stripe : stripes) {
+ AppendRawStripe(&fbu, *stripe);
+ }
+ return fbu;
+}
+
+void VncClientConnection::AppendJpegStripeHeader(Message* frame_buffer_update,
+ const Stripe& stripe) {
+ static constexpr std::uint8_t kJpegEncoding = 0x90;
+ AppendToMessage(frame_buffer_update, stripe.x, stripe.y, stripe.width,
+ stripe.height, kTightEncoding, kJpegEncoding);
+ AppendJpegSize(frame_buffer_update, stripe.jpeg_data.size());
+}
+
+void VncClientConnection::AppendJpegStripe(Message* frame_buffer_update,
+ const Stripe& stripe) {
+ AppendJpegStripeHeader(frame_buffer_update, stripe);
+ frame_buffer_update->insert(frame_buffer_update->end(),
+ stripe.jpeg_data.begin(), stripe.jpeg_data.end());
+}
+
+Message VncClientConnection::MakeJpegFrameBufferUpdate(
+ const StripePtrVec& stripes) {
+ auto fbu =
+ MakeFrameBufferUpdateHeader(static_cast<std::uint16_t>(stripes.size()));
+ for (auto& stripe : stripes) {
+ AppendJpegStripe(&fbu, *stripe);
+ }
+ return fbu;
+}
+
+Message VncClientConnection::MakeFrameBufferUpdate(
+ const StripePtrVec& stripes) {
+ return use_jpeg_compression_ ? MakeJpegFrameBufferUpdate(stripes)
+ : MakeRawFrameBufferUpdate(stripes);
+}
+
+void VncClientConnection::FrameBufferUpdateRequestHandler(bool aggressive) {
+ BlackBoard::Registerer reg(bb_, this);
+ const StripeSeqNumber kBeginningOfTime{};
+
+ while (!closed()) {
+ auto stripes = bb_->WaitForSenderWork(this);
+ if (closed()) {
+ break;
+ }
+ LOG_ALWAYS_FATAL_IF(stripes.empty(), "Got 0 stripes");
+ {
+ // lock here so a portrait frame can't be sent after a landscape
+ // DesktopSize update, or vice versa.
+ std::lock_guard<std::mutex> guard(m_);
+ D("Sending update in %s mode",
+ current_orientation_ == ScreenOrientation::Portrait ? "portrait"
+ : "landscape");
+ client_.Send(MakeFrameBufferUpdate(stripes));
+ }
+ if (aggressive) {
+ bb_->FrameBufferUpdateRequestReceived(this);
+ }
+ }
+}
+
+void VncClientConnection::SendDesktopSizeUpdate() {
+ static constexpr int32_t kDesktopSizeEncoding = -223;
+ client_.Send(CreateMessage(std::uint8_t{0}, // message-type,
+ std::uint8_t{}, // padding
+ std::uint16_t{1}, // one pseudo rectangle
+ std::uint16_t{0}, std::uint16_t{0},
+ static_cast<std::uint16_t>(ScreenWidth()),
+ static_cast<std::uint16_t>(ScreenHeight()),
+ kDesktopSizeEncoding));
+}
+
+bool VncClientConnection::IsUrgent(
+ const FrameBufferUpdateRequest& update_request) const {
+ return !update_request.incremental ||
+ update_request != previous_update_request_;
+}
+
+void VncClientConnection::HandleFramebufferUpdateRequest() {
+ auto msg = client_.Recv(kFramebufferUpdateRequestLength);
+ if (msg.size() != kFramebufferUpdateRequestLength) {
+ return;
+ }
+ FrameBufferUpdateRequest fbur{msg[1] == 0, uint16_tAt(&msg[1]),
+ uint16_tAt(&msg[3]), uint16_tAt(&msg[5]),
+ uint16_tAt(&msg[7])};
+ if (IsUrgent(fbur)) {
+ bb_->SignalClientNeedsEntireScreen(this);
+ }
+ bb_->FrameBufferUpdateRequestReceived(this);
+ previous_update_request_ = fbur;
+}
+
+void VncClientConnection::HandleSetEncodings() {
+ auto msg = client_.Recv(kSetEncodingsLength);
+ if (msg.size() != kSetEncodingsLength) {
+ return;
+ }
+ auto count = uint16_tAt(&msg[1]);
+ auto encodings = client_.Recv(count * sizeof(int32_t));
+ if (encodings.size() % sizeof(int32_t) != 0) {
+ return;
+ }
+ {
+ std::lock_guard<std::mutex> guard(m_);
+ use_jpeg_compression_ = false;
+ }
+ for (size_t i = 0; i < encodings.size(); i += sizeof(int32_t)) {
+ auto enc = int32_tAt(&encodings[i]);
+ D("client requesting encoding: %d\n", enc);
+ if (enc == kTightEncoding) {
+ // This is a deviation from the spec which says that if a jpeg quality
+ // level is not specified, tight encoding won't use jpeg.
+ std::lock_guard<std::mutex> guard(m_);
+ use_jpeg_compression_ = true;
+ }
+ if (kJpegMinQualityEncoding <= enc && enc <= kJpegMaxQualityEncoding) {
+ D("jpeg compression level: %d", enc);
+ bb_->set_jpeg_quality_level(enc);
+ }
+ if (enc == kDesktopSizeEncoding) {
+ supports_desktop_size_encoding_ = true;
+ }
+ }
+}
+
+void VncClientConnection::HandleSetPixelFormat() {
+ std::lock_guard<std::mutex> guard(m_);
+ auto msg = client_.Recv(kSetPixelFormatLength);
+ if (msg.size() != kSetPixelFormatLength) {
+ return;
+ }
+ pixel_format_.bits_per_pixel = msg[3];
+ pixel_format_.depth = msg[4];
+ pixel_format_.big_endian = msg[5];
+ pixel_format_.true_color = msg[7];
+ pixel_format_.red_max = uint16_tAt(&msg[8]);
+ pixel_format_.green_max = uint16_tAt(&msg[10]);
+ pixel_format_.blue_max = uint16_tAt(&msg[12]);
+ pixel_format_.red_shift = msg[13];
+ pixel_format_.green_shift = msg[14];
+ pixel_format_.blue_shift = msg[15];
+}
+
+void VncClientConnection::HandlePointerEvent() {
+ auto msg = client_.Recv(kPointerEventLength);
+ if (msg.size() != kPointerEventLength) {
+ return;
+ }
+ std::uint8_t button_mask = msg[0];
+ auto x_pos = uint16_tAt(&msg[1]);
+ auto y_pos = uint16_tAt(&msg[3]);
+ {
+ std::lock_guard<std::mutex> guard(m_);
+ if (current_orientation_ == ScreenOrientation::Landscape) {
+ std::tie(x_pos, y_pos) =
+ std::make_pair(ActualScreenWidth() - y_pos, x_pos);
+ }
+ }
+ virtual_inputs_->HandlePointerEvent(button_mask, x_pos, y_pos);
+}
+
+void VncClientConnection::UpdateAccelerometer(float x, float y, float z) {
+ // Discard the event if we don't have a connection to the HAL.
+ if (!sensor_event_hal_->IsOpen()) {
+ ALOGE("sensor event client not open");
+ return;
+ }
+ timespec current_time{};
+ clock_gettime(CLOCK_MONOTONIC, ¤t_time);
+ // Construct the sensor message.
+ gce_sensors_message message{};
+ message.version = sizeof message;
+ message.sensor = avd::sensors_constants::kAccelerometerHandle;
+ message.type = SENSOR_TYPE_ACCELEROMETER;
+ message.timestamp = current_time.tv_sec * static_cast<int64_t>(1000000000) +
+ current_time.tv_nsec;
+ message.data[0] = x;
+ message.data[1] = y;
+ message.data[2] = z;
+
+ std::array<iovec, 1> msg_iov{};
+ msg_iov[0].iov_base = &message;
+ msg_iov[0].iov_len = sizeof(sensors_event_t);
+
+ msghdr msg;
+ msg.msg_name = nullptr;
+ msg.msg_namelen = 0;
+ msg.msg_iov = msg_iov.data();
+ msg.msg_iovlen = msg_iov.size();
+ msg.msg_control = nullptr;
+ msg.msg_controllen = 0;
+ msg.msg_flags = 0;
+ if (sensor_event_hal_->SendMsg(&msg, 0) == -1) {
+ ALOGE("%s: Could not send sensor data. (%s).", __FUNCTION__,
+ sensor_event_hal_->StrError());
+ }
+}
+
+VncClientConnection::Coordinates VncClientConnection::CoordinatesForOrientation(
+ ScreenOrientation orientation) const {
+ // Compute the acceleration vector that we need to send to mimic
+ // this change.
+ constexpr float g = 9.81;
+ constexpr float angle = 20.0;
+ const float cos_angle = std::cos(angle / M_PI);
+ const float sin_angle = std::sin(angle / M_PI);
+ const float z = g * sin_angle;
+ switch (orientation) {
+ case ScreenOrientation::Portrait:
+ return {0, g * cos_angle, z};
+ case ScreenOrientation::Landscape:
+ return {g * cos_angle, 0, z};
+ }
+}
+
+int VncClientConnection::ScreenWidth() const {
+ return current_orientation_ == ScreenOrientation::Portrait
+ ? ActualScreenWidth()
+ : ActualScreenHeight();
+}
+
+int VncClientConnection::ScreenHeight() const {
+ return current_orientation_ == ScreenOrientation::Portrait
+ ? ActualScreenHeight()
+ : ActualScreenWidth();
+}
+
+void VncClientConnection::SetScreenOrientation(ScreenOrientation orientation) {
+ std::lock_guard<std::mutex> guard(m_);
+ auto coords = CoordinatesForOrientation(orientation);
+ UpdateAccelerometer(coords.x, coords.y, coords.z);
+ if (supports_desktop_size_encoding_) {
+ auto previous_orientation = current_orientation_;
+ current_orientation_ = orientation;
+ if (current_orientation_ != previous_orientation &&
+ supports_desktop_size_encoding_) {
+ SendDesktopSizeUpdate();
+ bb_->SetOrientation(this, current_orientation_);
+ // TODO not sure if I should be sending a frame update along with this,
+ // or just letting the next FBUR handle it. This seems to me like it's
+ // sending one more frame buffer update than was requested, which is
+ // maybe a violation of the spec?
+ }
+ }
+}
+
+bool VncClientConnection::RotateIfIsRotationCommand(std::uint32_t key) {
+ // Due to different configurations on different platforms we're supporting
+ // a set of options for rotating the screen. These are similar to what
+ // the emulator supports and has supported.
+ // ctrl+left and ctrl+right work on windows and linux
+ // command+left and command+right work on Mac
+ // ctrl+fn+F11 and ctrl+fn+F12 work when chromoting to ubuntu from a Mac
+ if (!control_key_down_ && !meta_key_down_) {
+ return false;
+ }
+ switch (key) {
+ case avd::xk::Right:
+ case avd::xk::F12:
+ D("switching to portrait");
+ SetScreenOrientation(ScreenOrientation::Portrait);
+ break;
+ case avd::xk::Left:
+ case avd::xk::F11:
+ D("switching to landscape");
+ SetScreenOrientation(ScreenOrientation::Landscape);
+ break;
+ default:
+ return false;
+ }
+ return true;
+}
+
+void VncClientConnection::HandleKeyEvent() {
+ auto msg = client_.Recv(kKeyEventLength);
+ if (msg.size() != kKeyEventLength) {
+ return;
+ }
+
+ auto key = uint32_tAt(&msg[3]);
+ bool key_down = msg[0];
+ switch (key) {
+ case avd::xk::ControlLeft:
+ case avd::xk::ControlRight:
+ control_key_down_ = key_down;
+ break;
+ case avd::xk::MetaLeft:
+ case avd::xk::MetaRight:
+ meta_key_down_ = key_down;
+ break;
+ case avd::xk::F5:
+ key = avd::xk::Menu;
+ break;
+ case avd::xk::F7:
+ virtual_inputs_->PressPowerButton(key_down);
+ return;
+ default:
+ break;
+ }
+
+ if (RotateIfIsRotationCommand(key)) {
+ return;
+ }
+
+ virtual_inputs_->GenerateKeyPressEvent(key, key_down);
+}
+
+void VncClientConnection::HandleClientCutText() {
+ auto msg = client_.Recv(kClientCutTextLength);
+ if (msg.size() != kClientCutTextLength) {
+ return;
+ }
+ auto len = uint32_tAt(&msg[3]);
+ client_.Recv(len);
+}
+
+void VncClientConnection::NormalSession() {
+ static constexpr std::uint8_t kSetPixelFormatMessage{0};
+ static constexpr std::uint8_t kSetEncodingsMessage{2};
+ static constexpr std::uint8_t kFramebufferUpdateRequestMessage{3};
+ static constexpr std::uint8_t kKeyEventMessage{4};
+ static constexpr std::uint8_t kPointerEventMessage{5};
+ static constexpr std::uint8_t kClientCutTextMessage{6};
+ while (true) {
+ if (client_.closed()) {
+ return;
+ }
+ auto msg = client_.Recv(1);
+ if (client_.closed()) {
+ return;
+ }
+ auto msg_type = msg.front();
+ D("Received message type %d\n", static_cast<int>(msg_type));
+
+ switch (msg_type) {
+ case kSetPixelFormatMessage:
+ HandleSetPixelFormat();
+ break;
+
+ case kSetEncodingsMessage:
+ HandleSetEncodings();
+ break;
+
+ case kFramebufferUpdateRequestMessage:
+ HandleFramebufferUpdateRequest();
+ break;
+
+ case kKeyEventMessage:
+ HandleKeyEvent();
+ break;
+
+ case kPointerEventMessage:
+ HandlePointerEvent();
+ break;
+
+ case kClientCutTextMessage:
+ HandleClientCutText();
+ break;
+
+ default:
+ ALOGW("message type not handled: %d", static_cast<int>(msg_type));
+ break;
+ }
+ }
+}
diff --git a/guest/frontend/vnc_server/vnc_client_connection.h b/guest/frontend/vnc_server/vnc_client_connection.h
new file mode 100644
index 0000000..09f7c94
--- /dev/null
+++ b/guest/frontend/vnc_server/vnc_client_connection.h
@@ -0,0 +1,158 @@
+#ifndef DEVICE_GOOGLE_GCE_GCE_UTILS_GCE_VNC_SERVER_VNC_CLIENT_CONNECTION_H_
+#define DEVICE_GOOGLE_GCE_GCE_UTILS_GCE_VNC_SERVER_VNC_CLIENT_CONNECTION_H_
+
+#include "blackboard.h"
+#include "virtual_inputs.h"
+#include "vnc_utils.h"
+#include "tcp_socket.h"
+
+#include <android-base/thread_annotations.h>
+#include <SharedFD.h>
+
+#include <cstdint>
+#include <memory>
+#include <mutex>
+#include <thread>
+#include <vector>
+
+namespace avd {
+namespace vnc {
+
+class VncClientConnection {
+ public:
+ VncClientConnection(ClientSocket client, VirtualInputs* virtual_inputs,
+ BlackBoard* bb, bool aggressive);
+ VncClientConnection(const VncClientConnection&) = delete;
+ VncClientConnection& operator=(const VncClientConnection&) = delete;
+ ~VncClientConnection();
+
+ void StartSession();
+
+ private:
+ struct PixelFormat {
+ std::uint8_t bits_per_pixel;
+ std::uint8_t depth;
+ std::uint8_t big_endian;
+ std::uint8_t true_color;
+ std::uint16_t red_max;
+ std::uint16_t green_max;
+ std::uint16_t blue_max;
+ std::uint8_t red_shift;
+ std::uint8_t green_shift;
+ std::uint8_t blue_shift;
+ };
+
+ struct FrameBufferUpdateRequest {
+ bool incremental;
+ std::uint16_t x_pos;
+ std::uint16_t y_pos;
+ std::uint16_t width;
+ std::uint16_t height;
+ };
+
+ friend bool operator==(const FrameBufferUpdateRequest&,
+ const FrameBufferUpdateRequest&);
+ friend bool operator!=(const FrameBufferUpdateRequest&,
+ const FrameBufferUpdateRequest&);
+
+ bool closed();
+ void SetupProtocol();
+ void SetupSecurityType();
+
+ void GetClientInit();
+
+ void SendServerInit() EXCLUDES(m_);
+ static Message MakeFrameBufferUpdateHeader(std::uint16_t num_stripes);
+
+ static void AppendRawStripeHeader(Message* frame_buffer_update,
+ const Stripe& stripe);
+ void AppendRawStripe(Message* frame_buffer_update, const Stripe& stripe) const
+ REQUIRES(m_);
+ Message MakeRawFrameBufferUpdate(const StripePtrVec& stripes) const
+ REQUIRES(m_);
+
+ static void AppendJpegSize(Message* frame_buffer_update, size_t jpeg_size);
+ static void AppendJpegStripeHeader(Message* frame_buffer_update,
+ const Stripe& stripe);
+ static void AppendJpegStripe(Message* frame_buffer_update,
+ const Stripe& stripe);
+ static Message MakeJpegFrameBufferUpdate(const StripePtrVec& stripes);
+
+ Message MakeFrameBufferUpdate(const StripePtrVec& frame) REQUIRES(m_);
+
+ void FrameBufferUpdateRequestHandler(bool aggressive) EXCLUDES(m_);
+
+ void SendDesktopSizeUpdate() REQUIRES(m_);
+
+ bool IsUrgent(const FrameBufferUpdateRequest& update_request) const;
+ static StripeSeqNumber MostRecentStripeSeqNumber(const StripePtrVec& stripes);
+
+ void HandleFramebufferUpdateRequest() EXCLUDES(m_);
+
+ void HandleSetEncodings();
+
+ void HandleSetPixelFormat();
+
+ void HandlePointerEvent() EXCLUDES(m_);
+
+ void UpdateAccelerometer(float x, float y, float z);
+
+ struct Coordinates {
+ float x;
+ float y;
+ float z;
+ };
+
+ Coordinates CoordinatesForOrientation(ScreenOrientation orientation) const;
+
+ int ScreenWidth() const REQUIRES(m_);
+
+ int ScreenHeight() const REQUIRES(m_);
+
+ void SetScreenOrientation(ScreenOrientation orientation) EXCLUDES(m_);
+
+ // Returns true if key is special and the screen was rotated.
+ bool RotateIfIsRotationCommand(std::uint32_t key);
+
+ void HandleKeyEvent();
+
+ void HandleClientCutText();
+
+ void NormalSession();
+
+ mutable std::mutex m_;
+ ClientSocket client_;
+ avd::SharedFD sensor_event_hal_;
+ bool control_key_down_ = false;
+ bool meta_key_down_ = false;
+ VirtualInputs* virtual_inputs_{};
+
+ FrameBufferUpdateRequest previous_update_request_{};
+ BlackBoard* bb_;
+ bool use_jpeg_compression_ GUARDED_BY(m_) = false;
+
+ std::thread frame_buffer_request_handler_tid_;
+ bool closed_ GUARDED_BY(m_){};
+
+ PixelFormat pixel_format_ GUARDED_BY(m_) = {
+ std::uint8_t{32}, // bits per pixel
+ std::uint8_t{8}, // depth
+ std::uint8_t{}, // big_endian
+ std::uint8_t{}, // true_color
+ std::uint16_t{}, // red_max, (maxes not used when true color flag is 0)
+ std::uint16_t{}, // green_max
+ std::uint16_t{}, // blue_max
+ std::uint8_t{}, // red_shift (shifts not used when true color flag is 0)
+ std::uint8_t{}, // green_shift
+ std::uint8_t{}, // blue_shift
+ };
+
+ bool supports_desktop_size_encoding_ = false;
+ ScreenOrientation current_orientation_ GUARDED_BY(m_) =
+ ScreenOrientation::Portrait;
+};
+
+} // namespace vnc
+} // namespace avd
+
+#endif
diff --git a/guest/frontend/vnc_server/vnc_server.cpp b/guest/frontend/vnc_server/vnc_server.cpp
new file mode 100644
index 0000000..50d2a02
--- /dev/null
+++ b/guest/frontend/vnc_server/vnc_server.cpp
@@ -0,0 +1,36 @@
+#include "blackboard.h"
+#include "frame_buffer_watcher.h"
+#include "jpeg_compressor.h"
+#include "tcp_socket.h"
+#include "virtual_inputs.h"
+#include "vnc_client_connection.h"
+#include "vnc_server.h"
+#include "vnc_utils.h"
+
+using avd::vnc::VncServer;
+
+VncServer::VncServer(int port, bool aggressive)
+ : server_(port), frame_buffer_watcher_{&bb_}, aggressive_{aggressive} {}
+
+void VncServer::MainLoop() {
+ while (true) {
+ auto connection = server_.Accept();
+ StartClient(std::move(connection));
+ }
+}
+
+void VncServer::StartClient(ClientSocket sock) {
+ std::thread t(&VncServer::StartClientThread, this, std::move(sock));
+ t.detach();
+}
+
+void VncServer::StartClientThread(ClientSocket sock) {
+ // NOTE if VncServer is expected to be destroyed, we have a problem here.
+ // All of the client threads will be pointing to the VncServer's
+ // data members. In the current setup, if the VncServer is destroyed with
+ // clients still running, the clients will all be left with dangling
+ // pointers.
+ VncClientConnection client(std::move(sock), &virtual_inputs_, &bb_,
+ aggressive_);
+ client.StartSession();
+}
diff --git a/guest/frontend/vnc_server/vnc_server.h b/guest/frontend/vnc_server/vnc_server.h
new file mode 100644
index 0000000..f80f692
--- /dev/null
+++ b/guest/frontend/vnc_server/vnc_server.h
@@ -0,0 +1,43 @@
+#ifndef DEVICE_GOOGLE_GCE_GCE_UTILS_GCE_VNC_SERVER_VNC_SERVER_H_
+#define DEVICE_GOOGLE_GCE_GCE_UTILS_GCE_VNC_SERVER_VNC_SERVER_H_
+
+#include "blackboard.h"
+#include "frame_buffer_watcher.h"
+#include "jpeg_compressor.h"
+#include "tcp_socket.h"
+#include "virtual_inputs.h"
+#include "vnc_client_connection.h"
+#include "vnc_utils.h"
+
+#include <thread>
+#include <string>
+#include <utility>
+
+namespace avd {
+namespace vnc {
+
+class VncServer {
+ public:
+ explicit VncServer(int port, bool aggressive);
+
+ VncServer(const VncServer&) = delete;
+ VncServer& operator=(const VncServer&) = delete;
+
+ [[noreturn]] void MainLoop();
+
+ private:
+ void StartClient(ClientSocket sock);
+
+ void StartClientThread(ClientSocket sock);
+
+ ServerSocket server_;
+ VirtualInputs virtual_inputs_;
+ BlackBoard bb_;
+ FrameBufferWatcher frame_buffer_watcher_;
+ bool aggressive_{};
+};
+
+} // namespace vnc
+} // namespace avd
+
+#endif
diff --git a/guest/frontend/vnc_server/vnc_utils.h b/guest/frontend/vnc_server/vnc_utils.h
new file mode 100644
index 0000000..54cf159
--- /dev/null
+++ b/guest/frontend/vnc_server/vnc_utils.h
@@ -0,0 +1,82 @@
+#ifndef DEVICE_GOOGLE_GCE_GCE_UTILS_GCE_VNC_SERVER_VNC_UTILS_H_
+#define DEVICE_GOOGLE_GCE_GCE_UTILS_GCE_VNC_SERVER_VNC_UTILS_H_
+
+#include <GceFrameBuffer.h>
+
+#include <vector>
+#include <array>
+#include <utility>
+#include <cstdint>
+
+#undef D
+#ifdef GCE_VNC_DEBUG
+#define D(...) ALOGD(__VA_ARGS__)
+#else
+#define D(...) ((void)0)
+#endif
+
+namespace avd {
+namespace vnc {
+
+// TODO(haining) when the hwcomposer gives a sequence number type, use that
+// instead. It might just work to replace this class with a type alias
+// using StripeSeqNumber = whatever_the_hwcomposer_uses;
+class StripeSeqNumber {
+ public:
+ StripeSeqNumber() = default;
+ explicit StripeSeqNumber(std::uint64_t t) : t_{t} {}
+ bool operator<(const StripeSeqNumber& other) const {
+ return t_ < other.t_;
+ }
+
+ bool operator<=(const StripeSeqNumber& other) const {
+ return t_ <= other.t_;
+ }
+
+ private:
+ std::uint64_t t_{};
+};
+
+using Message = std::vector<std::uint8_t>;
+
+constexpr int32_t kJpegMaxQualityEncoding = -23;
+constexpr int32_t kJpegMinQualityEncoding = -32;
+
+enum class ScreenOrientation { Portrait, Landscape };
+constexpr int kNumOrientations = 2;
+
+struct Stripe {
+ int index = -1;
+ std::uint64_t frame_id{};
+ std::uint16_t x{};
+ std::uint16_t y{};
+ std::uint16_t width{};
+ std::uint16_t height{};
+ Message raw_data{};
+ Message jpeg_data{};
+ StripeSeqNumber seq_number{};
+ ScreenOrientation orientation{};
+};
+
+inline constexpr int BytesPerPixel() {
+ return sizeof(GceFrameBuffer::Pixel);
+}
+
+// The width of the screen regardless of orientation. Does not change.
+inline int ActualScreenWidth() {
+ return GceFrameBuffer::getInstance().x_res();
+}
+
+// The height of the screen regardless of orientation. Does not change.
+inline int ActualScreenHeight() {
+ return GceFrameBuffer::getInstance().y_res();
+}
+
+inline int ScreenSizeInBytes() {
+ return ActualScreenWidth() * ActualScreenHeight() * BytesPerPixel();
+}
+
+} // namespace vnc
+} // namespace avd
+
+#endif
diff --git a/guest/hals/gralloc/legacy/Android.mk b/guest/hals/gralloc/legacy/Android.mk
new file mode 100644
index 0000000..af4de7a
--- /dev/null
+++ b/guest/hals/gralloc/legacy/Android.mk
@@ -0,0 +1,58 @@
+# Copyright (C) 2016 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.
+
+LOCAL_PATH := $(call my-dir)
+
+GCE_GRALLOC_COMMON_SRC_FILES := \
+ gralloc.cpp \
+ framebuffer.cpp \
+ mapper.cpp
+
+GCE_GRALLOC_COMMON_CFLAGS:= \
+ -DLOG_TAG=\"gralloc_gce_x86\" \
+ -Wno-missing-field-initializers \
+ -Wall -Werror \
+ $(GCE_VERSION_CFLAGS)
+
+GCE_GRALLOC_COMMON_C_INCLUDES := \
+ device/google/gce/include
+
+GCE_GRALLOC_COMMON_SHARED_LIBRARIES := \
+ liblog \
+ libutils \
+ libcutils \
+ libgceframebuffer
+
+GCE_GRALLOC_COMMON_STATIC_LIBRARIES := \
+ libgcemetadata
+
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := gralloc.gce_x86
+ifeq (0, $(shell test $(PLATFORM_SDK_VERSION) -ge 21; echo $$?))
+LOCAL_MODULE_RELATIVE_PATH := hw
+else
+LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw
+endif
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := $(GCE_GRALLOC_COMMON_SRC_FILES)
+
+LOCAL_CFLAGS := $(GCE_GRALLOC_COMMON_CFLAGS)
+LOCAL_C_INCLUDES := $(GCE_GRALLOC_COMMON_C_INCLUDES)
+LOCAL_SHARED_LIBRARIES := $(GCE_GRALLOC_COMMON_SHARED_LIBRARIES)
+LOCAL_STATIC_LIBRARIES := $(GCE_GRALLOC_COMMON_STATIC_LIBRARIES)
+LOCAL_VENDOR_MODULE := true
+include $(BUILD_SHARED_LIBRARY)
+
diff --git a/guest/hals/gralloc/legacy/framebuffer.cpp b/guest/hals/gralloc/legacy/framebuffer.cpp
new file mode 100644
index 0000000..759a55a
--- /dev/null
+++ b/guest/hals/gralloc/legacy/framebuffer.cpp
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+#include <sys/mman.h>
+
+#include <dlfcn.h>
+
+#include <cutils/ashmem.h>
+#include <cutils/log.h>
+#include <cutils/properties.h>
+
+#include <sys/system_properties.h>
+
+#include <hardware/hardware.h>
+#include <hardware/gralloc.h>
+
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <cutils/atomic.h>
+#include <private/android_filesystem_config.h>
+
+#if defined(__ANDROID__)
+#include <linux/fb.h>
+#endif
+
+#include "gralloc_gce_priv.h"
+
+#include <GceFrameBuffer.h>
+#include <GceFrameBufferControl.h>
+#include <RegionRegistry.h>
+#include "AutoResources.h"
+
+/*****************************************************************************/
+
+struct fb_context_t {
+ framebuffer_device_t device;
+};
+
+/*****************************************************************************/
+
+static int fb_setSwapInterval(struct framebuffer_device_t* dev, int interval) {
+ if (interval < dev->minSwapInterval || interval > dev->maxSwapInterval) {
+ return -EINVAL;
+ }
+ // FIXME: implement fb_setSwapInterval
+ return 0;
+}
+
+/*
+ * These functions (and probably the entire framebuffer device) are most likely
+ * not used when the hardware composer device is present, however is hard to be
+ * 100% sure.
+ */
+static int fb_setUpdateRect(
+ struct framebuffer_device_t* dev __unused, int l, int t, int w, int h) {
+ if (((w|h) <= 0) || ((l|t)<0)) {
+ return -EINVAL;
+ }
+ // TODO(jemoreira): Find a way to broadcast this with the framebuffer control.
+ return 0;
+}
+
+static int fb_post(struct framebuffer_device_t* dev __unused, buffer_handle_t buffer) {
+ const int yoffset = YOffsetFromHandle(buffer);
+ if (yoffset >= 0) {
+ int retval =
+ GceFrameBufferControl::getInstance().BroadcastFrameBufferChanged(
+ yoffset);
+ if (retval) ALOGI("Failed to post framebuffer");
+
+ return retval;
+ }
+ return -1;
+}
+
+/*****************************************************************************/
+
+int initUserspaceFrameBuffer(struct private_module_t* module) {
+ avd::LockGuard<pthread_mutex_t> guard(module->lock);
+ if (module->framebuffer) {
+ return 0;
+ }
+
+ int fd;
+ if (!GceFrameBuffer::OpenFrameBuffer(&fd)) {
+ return -errno;
+ }
+
+ const GceFrameBuffer& config = GceFrameBuffer::getInstance();
+
+ /*
+ * map the framebuffer
+ */
+ module->framebuffer =
+ new private_handle_t(fd,
+ config.total_buffer_size(),
+ config.hal_format(),
+ config.x_res(),
+ config.y_res(),
+ config.line_length() / (config.bits_per_pixel() / 8),
+ private_handle_t::PRIV_FLAGS_FRAMEBUFFER);
+ reference_region("framebuffer_init", module->framebuffer);
+
+ return 0;
+}
+
+
+/*****************************************************************************/
+
+static int fb_close(struct hw_device_t *dev) {
+ fb_context_t* ctx = (fb_context_t*)dev;
+ if (ctx) {
+ free(ctx);
+ }
+ return 0;
+}
+
+int fb_device_open(
+ hw_module_t const* module, const char* name, hw_device_t** device) {
+ int status = -EINVAL;
+ if (!strcmp(name, GRALLOC_HARDWARE_FB0)) {
+ /* initialize our state here */
+ fb_context_t* dev = (fb_context_t*) malloc(sizeof(*dev));
+ LOG_FATAL_IF(!dev, "%s: malloc returned NULL.", __FUNCTION__);
+ memset(dev, 0, sizeof(*dev));
+
+ /* initialize the procs */
+ dev->device.common.tag = HARDWARE_DEVICE_TAG;
+ dev->device.common.version = 0;
+ dev->device.common.module = const_cast<hw_module_t*>(module);
+ dev->device.common.close = fb_close;
+ dev->device.setSwapInterval = fb_setSwapInterval;
+ dev->device.post = fb_post;
+ dev->device.setUpdateRect = fb_setUpdateRect;
+
+ private_module_t* m = (private_module_t*)module;
+
+ status = initUserspaceFrameBuffer(m);
+
+ const GceFrameBuffer& config = GceFrameBuffer::getInstance();
+
+ if (status >= 0) {
+ int stride = config.line_length() / (config.bits_per_pixel() / 8);
+ int format = config.hal_format();
+ const_cast<uint32_t&>(dev->device.flags) = 0;
+ const_cast<uint32_t&>(dev->device.width) = config.x_res();
+ const_cast<uint32_t&>(dev->device.height) = config.y_res();
+ const_cast<int&>(dev->device.stride) = stride;
+ const_cast<int&>(dev->device.format) = format;
+ const_cast<float&>(dev->device.xdpi) = config.dpi();
+ const_cast<float&>(dev->device.ydpi) = config.dpi();
+ // TODO (jemoreira): DRY!! Managed by the vsync thread in the hwcomposer
+ const_cast<float&>(dev->device.fps) = (60 * 1000) / 1000.0f;
+ const_cast<int&>(dev->device.minSwapInterval) = 1;
+ const_cast<int&>(dev->device.maxSwapInterval) = 1;
+ *device = &dev->device.common;
+ }
+ }
+ return status;
+}
diff --git a/guest/hals/gralloc/legacy/gralloc.cpp b/guest/hals/gralloc/legacy/gralloc.cpp
new file mode 100644
index 0000000..8b8e652
--- /dev/null
+++ b/guest/hals/gralloc/legacy/gralloc.cpp
@@ -0,0 +1,304 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+#include <limits.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+
+#include <cutils/ashmem.h>
+#include <cutils/log.h>
+#include <cutils/atomic.h>
+#include <utils/String8.h>
+
+#include <hardware/hardware.h>
+#include <hardware/gralloc.h>
+
+#include <api_level_fixes.h>
+
+#include "GceFrameBuffer.h"
+#include "GceFrameBufferControl.h"
+#include "RegionRegistry.h"
+#include "gralloc_gce_priv.h"
+#include "AutoResources.h"
+
+/*****************************************************************************/
+
+static int gralloc_alloc_buffer(
+ alloc_device_t* /*dev*/, int format, int w, int h,
+ buffer_handle_t* pHandle, int* pStrideInPixels) {
+ int err = 0;
+ int fd = -1;
+ static int sequence = 0;
+
+ int bytes_per_pixel = formatToBytesPerPixel(format);
+ int bytes_per_line;
+ int stride_in_pixels;
+ int size = 0;
+ // SwiftShader can't handle RGB_888, so fail fast and hard if we try to create
+ // a gralloc buffer in this format.
+ ALOG_ASSERT(format != HAL_PIXEL_FORMAT_RGB_888);
+ if (format == HAL_PIXEL_FORMAT_YV12) {
+ bytes_per_line = GceFrameBuffer::align(bytes_per_pixel * w, 16);
+ } else {
+ bytes_per_line = GceFrameBuffer::align(bytes_per_pixel * w);
+ }
+ size = roundUpToPageSize(size + formatToBytesPerFrame(format, w, h));
+ size += PAGE_SIZE;
+ fd = ashmem_create_region(
+ android::String8::format(
+ "gralloc-%d.%d", getpid(), sequence++).string(),
+ size);
+ if (fd < 0) {
+ ALOGE("couldn't create ashmem (%s)", strerror(-errno));
+ err = -errno;
+ }
+
+ if (err == 0) {
+ stride_in_pixels = bytes_per_line / bytes_per_pixel;
+ private_handle_t* hnd =
+ new private_handle_t(fd, size, format, w, h, stride_in_pixels, 0);
+ void* base = reference_region(__FUNCTION__, hnd);
+ if (base) {
+ *pHandle = hnd;
+ *pStrideInPixels = stride_in_pixels;
+ } else {
+ err = -EIO;
+ }
+ }
+
+ ALOGE_IF(err, "gralloc failed err=%s", strerror(-err));
+
+ return err;
+}
+
+/*****************************************************************************/
+
+static int ensure_framebuffer_allocated(private_module_t* m) {
+ // allocate the framebuffer
+ if (m->framebuffer == NULL) {
+ // The framebuffer is mapped once and forever.
+ int err = initUserspaceFrameBuffer(m);
+ if (err < 0) {
+ ALOGE("Failed to map framebuffer (%d)", errno);
+ return err;
+ }
+ }
+ return 0;
+}
+
+static int gralloc_free_framebuffer(private_handle_t const * hnd) {
+ static const GceFrameBuffer& config = GceFrameBuffer::getInstance();
+ static GceFrameBufferControl& fb_control =
+ GceFrameBufferControl::getInstance();
+
+ // free this buffer
+ const size_t bufferSize = config.line_length() * config.y_res();
+ int index = hnd->frame_offset / bufferSize;
+ return fb_control.UnsetBufferBits(1LU << index);
+}
+
+// Allocates a framebuffer taken from the set of buffers given by buffer_mask.
+static int gralloc_alloc_framebuffer(alloc_device_t* dev,
+ buffer_handle_t* pHandle,
+ int* pStrideInPixels,
+ uint32_t buffer_mask) {
+ static const GceFrameBuffer& config = GceFrameBuffer::getInstance();
+ static GceFrameBufferControl& fb_control =
+ GceFrameBufferControl::getInstance();
+
+ private_module_t* m = reinterpret_cast<private_module_t*>(
+ dev->common.module);
+
+ ensure_framebuffer_allocated(m);
+
+ const uint32_t numBuffers = GceFrameBuffer::kNumBuffers;
+ const size_t bufferSize = config.bufferSize();
+
+ // Paranoia: Force the mask to be valid
+ buffer_mask &= (1LU << numBuffers) - 1;
+
+ uint32_t bit = fb_control.GetAndSetNextAvailableBufferBit(buffer_mask);
+ if (!bit) {
+ // All buffers in the mask have been allocated already or there was another
+ // error
+ return -ENOMEM;
+ }
+
+ int frame_offset = 0;
+ while (bit != 1LU) {
+ bit >>= 1;
+ frame_offset += bufferSize;
+ }
+
+ int stride_in_pixels = config.line_length() / (config.bits_per_pixel() / 8);
+
+ // create a "fake" handle for it
+ private_handle_t* hnd = new private_handle_t(
+ dup(m->framebuffer->fd), config.total_buffer_size(),
+ config.hal_format(), config.x_res(), config.y_res(),
+ stride_in_pixels, private_handle_t::PRIV_FLAGS_FRAMEBUFFER, frame_offset);
+
+ *pHandle = hnd;
+ if (pStrideInPixels) {
+ *pStrideInPixels = stride_in_pixels;
+ }
+
+ return 0;
+}
+
+static int gralloc_alloc_sf_framebuffer(alloc_device_t* dev,
+ buffer_handle_t* pHandle,
+ int* pStrideInPixels) {
+ uint32_t mask = (1LU << GceFrameBuffer::kNumSfBuffers) - 1LU;
+ // Skip the first buffers since those are for HWC usage
+ mask <<= GceFrameBuffer::kNumHwcBuffers;
+ return gralloc_alloc_framebuffer(dev, pHandle, pStrideInPixels, mask);
+}
+
+static int gralloc_alloc_hwc_framebuffer(alloc_device_t* dev,
+ buffer_handle_t* pHandle) {
+ // Use the first kNumHwcBuffers for hwcomposer
+ uint32_t mask = (1LU << GceFrameBuffer::kNumHwcBuffers) - 1LU;
+ return gralloc_alloc_framebuffer(dev, pHandle, NULL, mask);
+}
+
+/*****************************************************************************/
+
+static int gralloc_alloc(
+ alloc_device_t* dev, int w, int h, int format, int usage,
+ buffer_handle_t* pHandle, int* pStrideInPixels) {
+ if (!pHandle || !pStrideInPixels)
+ return -EINVAL;
+
+ int err;
+ if (usage & GRALLOC_USAGE_HW_FB) {
+ err = gralloc_alloc_sf_framebuffer(dev, pHandle, pStrideInPixels);
+ } else {
+ err = gralloc_alloc_buffer(dev, format, w, h, pHandle, pStrideInPixels);
+ }
+
+ if (err < 0) {
+ return err;
+ }
+ return 0;
+}
+
+static int gralloc_free(alloc_device_t* /*dev*/, buffer_handle_t handle) {
+ if (private_handle_t::validate(handle) < 0) {
+ return -EINVAL;
+ }
+
+ int retval = 0;
+
+ private_handle_t const* hnd = reinterpret_cast<private_handle_t const*>(
+ handle);
+ if (hnd->flags & private_handle_t::PRIV_FLAGS_FRAMEBUFFER) {
+ retval = gralloc_free_framebuffer(hnd);
+ } else {
+ retval = unreference_region(__FUNCTION__, hnd);
+ }
+
+ close(hnd->fd);
+ delete hnd;
+ return retval;
+}
+
+/*****************************************************************************/
+
+static int gralloc_close(struct hw_device_t *dev) {
+ priv_alloc_device_t* ctx = reinterpret_cast<priv_alloc_device_t*>(dev);
+ if (ctx) {
+ /* TODO: keep a list of all buffer_handle_t created, and free them
+ * all here.
+ */
+ free(ctx);
+ }
+ return 0;
+}
+
+static int gralloc_device_open(
+ const hw_module_t* module, const char* name, hw_device_t** device) {
+ int status = -EINVAL;
+ if (!strcmp(name, GRALLOC_HARDWARE_GPU0)) {
+ priv_alloc_device_t *dev;
+ dev = (priv_alloc_device_t*) malloc(sizeof(*dev));
+ LOG_FATAL_IF(!dev, "%s: malloc returned NULL.", __FUNCTION__);
+
+ /* initialize our state here */
+ memset(dev, 0, sizeof(*dev));
+
+ /* initialize the procs */
+ dev->device.common.tag = HARDWARE_DEVICE_TAG;
+ dev->device.common.version = 0;
+ dev->device.common.module = const_cast<hw_module_t*>(module);
+ dev->device.common.close = gralloc_close;
+
+ dev->device.alloc = gralloc_alloc;
+ dev->device.free = gralloc_free;
+ dev->alloc_hwc_framebuffer = gralloc_alloc_hwc_framebuffer;
+
+
+ *device = &dev->device.common;
+ status = 0;
+ } else {
+ status = fb_device_open(module, name, device);
+ }
+ return status;
+}
+
+/*****************************************************************************/
+
+static struct hw_module_methods_t gralloc_module_methods = {
+ GCE_STATIC_INITIALIZER(open) gralloc_device_open
+};
+
+struct private_module_t HAL_MODULE_INFO_SYM = {
+ GCE_STATIC_INITIALIZER(base) {
+ GCE_STATIC_INITIALIZER(common) {
+ GCE_STATIC_INITIALIZER(tag) HARDWARE_MODULE_TAG,
+#ifdef GRALLOC_MODULE_API_VERSION_0_2
+ GCE_STATIC_INITIALIZER(version_major) GRALLOC_MODULE_API_VERSION_0_2,
+#else
+ GCE_STATIC_INITIALIZER(version_major) 1,
+#endif
+ GCE_STATIC_INITIALIZER(version_minor) 0,
+ GCE_STATIC_INITIALIZER(id) GRALLOC_HARDWARE_MODULE_ID,
+ GCE_STATIC_INITIALIZER(name) "GCE X86 Graphics Memory Allocator Module",
+ GCE_STATIC_INITIALIZER(author) "The Android Open Source Project",
+ GCE_STATIC_INITIALIZER(methods) &gralloc_module_methods,
+ GCE_STATIC_INITIALIZER(dso) NULL,
+ GCE_STATIC_INITIALIZER(reserved) {0},
+ },
+ GCE_STATIC_INITIALIZER(registerBuffer) gralloc_register_buffer,
+ GCE_STATIC_INITIALIZER(unregisterBuffer) gralloc_unregister_buffer,
+ GCE_STATIC_INITIALIZER(lock) gralloc_lock,
+ GCE_STATIC_INITIALIZER(unlock) gralloc_unlock,
+#ifdef GRALLOC_MODULE_API_VERSION_0_2
+ GCE_STATIC_INITIALIZER(perform) NULL,
+ GCE_STATIC_INITIALIZER(lock_ycbcr) gralloc_lock_ycbcr,
+#endif
+ },
+ GCE_STATIC_INITIALIZER(framebuffer) 0,
+ GCE_STATIC_INITIALIZER(lock) PTHREAD_MUTEX_INITIALIZER,
+};
diff --git a/guest/hals/gralloc/legacy/gralloc_gce_priv.h b/guest/hals/gralloc/legacy/gralloc_gce_priv.h
new file mode 100644
index 0000000..a194b3e
--- /dev/null
+++ b/guest/hals/gralloc/legacy/gralloc_gce_priv.h
@@ -0,0 +1,268 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+#ifndef GRALLOC_GCE_PRIV_H_
+#define GRALLOC_GCE_PRIV_H_
+
+#include <stdint.h>
+#include <limits.h>
+#include <sys/cdefs.h>
+#include <sys/mman.h>
+#include <hardware/gralloc.h>
+#include <Pthread.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include <cutils/native_handle.h>
+#include <cutils/log.h>
+
+#include <linux/fb.h>
+
+#include "GceFrameBuffer.h"
+
+#ifndef GRALLOC_MODULE_API_VERSION_0_2
+// This structure will be defined in later releases of Android. Declare it
+// here to allow us to structure the code well.
+struct android_ycbcr {
+ void* y;
+ void* cb;
+ void* cr;
+ size_t ystride;
+ size_t cstride;
+ size_t chroma_step;
+ uint32_t reserved[8];
+};
+#endif
+
+/*****************************************************************************/
+
+struct private_handle_t;
+
+struct private_module_t {
+ gralloc_module_t base;
+
+ private_handle_t* framebuffer;
+ pthread_mutex_t lock;
+};
+
+int initUserspaceFrameBuffer(struct private_module_t* module);
+
+/*****************************************************************************/
+
+struct priv_alloc_device_t {
+ alloc_device_t device;
+ // Creates handles for the hwcomposer-specific framebuffers
+ int (*alloc_hwc_framebuffer)(alloc_device_t* m,
+ buffer_handle_t* handle);
+};
+
+/*****************************************************************************/
+
+struct private_handle_t : public native_handle {
+ enum {
+ PRIV_FLAGS_FRAMEBUFFER = 0x00000001
+ };
+
+ // file-descriptors
+ int fd;
+ // ints
+ int magic;
+ int flags;
+ int format;
+ int x_res;
+ int y_res;
+ int stride_in_pixels;
+ // Use to indicate which frame we're using.
+ int frame_offset;
+ int total_size;
+ int lock_level;
+
+ static inline int sNumInts() {
+ return (((sizeof(private_handle_t) - sizeof(native_handle_t))/sizeof(int)) - sNumFds);
+ }
+ static const int sNumFds = 1;
+ static const int sMagic = 0x3141592;
+
+ private_handle_t(int fd, int size, int format, int x_res, int y_res,
+ int stride_in_pixels, int flags, int frame_offset = 0)
+ : fd(fd),
+ magic(sMagic),
+ flags(flags),
+ format(format),
+ x_res(x_res),
+ y_res(y_res),
+ stride_in_pixels(stride_in_pixels),
+ frame_offset(frame_offset),
+ total_size(size),
+ lock_level(0) {
+ version = sizeof(native_handle);
+ numInts = sNumInts();
+ numFds = sNumFds;
+ }
+
+ ~private_handle_t() {
+ magic = 0;
+ }
+
+ static int validate(const native_handle* h) {
+ const private_handle_t* hnd = (const private_handle_t*)h;
+ if (!h || h->version != sizeof(native_handle) ||
+ h->numInts != sNumInts() || h->numFds != sNumFds ||
+ hnd->magic != sMagic) {
+ ALOGE("invalid gralloc handle (at %p)", h);
+ return -EINVAL;
+ }
+ return 0;
+ }
+};
+
+
+static inline int formatToBytesPerPixel(int format) {
+ switch (format) {
+ case HAL_PIXEL_FORMAT_RGBA_8888:
+ case HAL_PIXEL_FORMAT_RGBX_8888:
+ case HAL_PIXEL_FORMAT_BGRA_8888:
+#if GCE_PLATFORM_SDK_AFTER(J)
+ // The camera 3.0 implementation assumes that IMPLEMENTATION_DEFINED
+ // means HAL_PIXEL_FORMAT_RGBA_8888
+ case HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED:
+#endif
+ return 4;
+ case HAL_PIXEL_FORMAT_RGB_888:
+ return 3;
+ case HAL_PIXEL_FORMAT_RGB_565:
+ case HAL_PIXEL_FORMAT_YV12:
+#ifdef GRALLOC_MODULE_API_VERSION_0_2
+ case HAL_PIXEL_FORMAT_YCbCr_420_888:
+#endif
+ return 2;
+#if GCE_PLATFORM_SDK_AFTER(J)
+ case HAL_PIXEL_FORMAT_BLOB:
+ return 1;
+#endif
+ default:
+ ALOGE("%s: unknown format=%d", __FUNCTION__, format);
+ return 4;
+ }
+}
+
+static inline void formatToYcbcr(
+ int format, int width, int height, void* base_v, android_ycbcr* out) {
+ char* it = static_cast<char*>(base_v);
+ // Clear reserved fields;
+ memset(out, 0, sizeof(*out));
+ switch (format) {
+ case HAL_PIXEL_FORMAT_YV12:
+#ifdef GRALLOC_MODULE_API_VERSION_0_2
+ case HAL_PIXEL_FORMAT_YCbCr_420_888:
+#endif
+ out->ystride = GceFrameBuffer::align(width, 16);
+ out->cstride = GceFrameBuffer::align(out->ystride / 2, 16);
+ out->chroma_step = 1;
+ out->y = it;
+ it += out->ystride * height;
+ out->cr = it;
+ it += out->cstride * height / 2;
+ out->cb = it;
+ break;
+ default:
+ ALOGE("%s: can't deal with format=0x%x (%s)",
+ __FUNCTION__, format, pixel_format_to_string(format));
+ }
+}
+
+static inline int formatToBytesPerFrame(int format, int w, int h) {
+ int bytes_per_pixel = formatToBytesPerPixel(format);
+ int w16, h16;
+ int y_size, c_size;
+
+ switch (format) {
+#if GCE_PLATFORM_SDK_AFTER(J)
+ // BLOB is used to allocate buffers for JPEG formatted data. Bytes per pixel
+ // is 1, the desired buffer size is in w, and h should be 1. We refrain from
+ // adding additional padding, although the caller is likely to round
+ // up to a page size.
+ case HAL_PIXEL_FORMAT_BLOB:
+ return bytes_per_pixel * w * h;
+#endif
+ case HAL_PIXEL_FORMAT_YV12:
+#ifdef GRALLOC_MODULE_API_VERSION_0_2
+ case HAL_PIXEL_FORMAT_YCbCr_420_888:
+#endif
+ android_ycbcr strides;
+ formatToYcbcr(format, w, h, NULL, &strides);
+ y_size = strides.ystride * h;
+ c_size = strides.cstride * h / 2;
+ return (y_size + 2 * c_size + GceFrameBuffer::kSwiftShaderPadding);
+ /*case HAL_PIXEL_FORMAT_RGBA_8888:
+ case HAL_PIXEL_FORMAT_RGBX_8888:
+ case HAL_PIXEL_FORMAT_BGRA_8888:
+ case HAL_PIXEL_FORMAT_RGB_888:
+ case HAL_PIXEL_FORMAT_RGB_565:*/
+ default:
+ w16 = GceFrameBuffer::align(w, 16);
+ h16 = GceFrameBuffer::align(h, 16);
+ return bytes_per_pixel * w16 * h16 + GceFrameBuffer::kSwiftShaderPadding;
+ }
+}
+
+// Calculates the yoffset from a framebuffer handle. I checks the given handle
+// for errors first. Returns the yoffset (non negative integer) or -1 if there
+// is an error.
+static inline int YOffsetFromHandle(buffer_handle_t buffer_hnd) {
+ if (!buffer_hnd) {
+ ALOGE("Attempt to post null buffer");
+ return -1;
+ }
+ if (private_handle_t::validate(buffer_hnd) < 0) {
+ ALOGE("Attempt to post non-gce handle");
+ return -1;
+ }
+ const private_handle_t* hnd =
+ reinterpret_cast<private_handle_t const*>(buffer_hnd);
+ if (!(hnd->flags & private_handle_t::PRIV_FLAGS_FRAMEBUFFER)) {
+ ALOGE("Attempt to post non-framebuffer");
+ return -1;
+ }
+
+ const GceFrameBuffer& config = GceFrameBuffer::getInstance();
+ return hnd->frame_offset / config.line_length();
+}
+
+int fb_device_open(
+ const hw_module_t* module, const char* name, hw_device_t** device);
+
+int gralloc_lock(
+ gralloc_module_t const* module,
+ buffer_handle_t handle, int usage,
+ int l, int t, int w, int h,
+ void** vaddr);
+
+int gralloc_unlock(
+ gralloc_module_t const* module, buffer_handle_t handle);
+
+int gralloc_register_buffer(
+ gralloc_module_t const* module, buffer_handle_t handle);
+
+int gralloc_unregister_buffer(
+ gralloc_module_t const* module, buffer_handle_t handle);
+
+int gralloc_lock_ycbcr(
+ struct gralloc_module_t const* module,
+ buffer_handle_t handle, int usage,
+ int l, int t, int w, int h,
+ struct android_ycbcr *ycbcr);
+
+#endif /* GRALLOC_GCE_PRIV_H_ */
diff --git a/guest/hals/gralloc/legacy/mapper.cpp b/guest/hals/gralloc/legacy/mapper.cpp
new file mode 100644
index 0000000..a75cdc9
--- /dev/null
+++ b/guest/hals/gralloc/legacy/mapper.cpp
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+#include <limits.h>
+#include <errno.h>
+#include <pthread.h>
+#include <unistd.h>
+#include <string.h>
+
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <cutils/hashmap.h>
+#include <cutils/log.h>
+#include <cutils/atomic.h>
+
+#include <hardware/hardware.h>
+#include <hardware/gralloc.h>
+#include <system/graphics.h>
+
+#include "RegionRegistry.h"
+
+#include "gralloc_gce_priv.h"
+#include "remoter_framework_pkt.h"
+
+#define DEBUG_REFERENCES 1
+#define DEBUG_MAX_LOCK_LEVEL 20
+
+/*****************************************************************************/
+
+int gralloc_register_buffer(gralloc_module_t const* /*module*/,
+ buffer_handle_t handle) {
+ if (private_handle_t::validate(handle) < 0) {
+ return -EINVAL;
+ }
+
+ private_handle_t* hnd = (private_handle_t*)handle;
+ if (reference_region(__FUNCTION__, hnd)) {
+ return 0;
+ } else {
+ return -EIO;
+ }
+}
+
+int gralloc_unregister_buffer(gralloc_module_t const* /*module*/,
+ buffer_handle_t handle) {
+ if (private_handle_t::validate(handle) < 0) {
+ return -EINVAL;
+ }
+ private_handle_t* hnd = (private_handle_t*)handle;
+ return unreference_region("gralloc_unregister_buffer", hnd);
+}
+
+int gralloc_lock(
+ gralloc_module_t const* /*module*/, buffer_handle_t handle, int /*usage*/,
+ int /*l*/, int /*t*/, int /*w*/, int /*h*/,
+ void** vaddr) {
+ if (private_handle_t::validate(handle) < 0) {
+ return -EINVAL;
+ }
+ if (!vaddr) {
+ return -EINVAL;
+ }
+ private_handle_t* hnd = (private_handle_t*)handle;
+#if DEBUG_REFERENCES
+ if (hnd->lock_level > DEBUG_MAX_LOCK_LEVEL) {
+ LOG_FATAL("%s: unbalanced lock detected. lock level = %d",
+ __FUNCTION__, hnd->lock_level);
+ }
+ ++hnd->lock_level;
+#endif
+ void* base = reference_region("gralloc_lock", hnd);
+ *vaddr = reinterpret_cast<unsigned char*>(base)
+ + hnd->frame_offset;
+ return 0;
+}
+
+int gralloc_unlock(
+ gralloc_module_t const* /*module*/, buffer_handle_t handle) {
+ if (private_handle_t::validate(handle) < 0) {
+ return -EINVAL;
+ }
+ private_handle_t* hnd = (private_handle_t*) handle;
+#if DEBUG_REFERENCES
+ if (hnd->lock_level <= 0) {
+ LOG_FATAL("%s unbalanced unlock detected. lock level = %d",
+ __FUNCTION__, hnd->lock_level);
+ }
+ --hnd->lock_level;
+#endif
+ unreference_region("gralloc_unlock", hnd);
+ return 0;
+}
+
+int gralloc_lock_ycbcr(
+ gralloc_module_t const* /*module*/, buffer_handle_t handle, int /*usage*/,
+ int /*l*/, int /*t*/, int /*w*/, int /*h*/,
+ struct android_ycbcr* ycbcr) {
+ if (private_handle_t::validate(handle) < 0) {
+ return -EINVAL;
+ }
+ private_handle_t* hnd = (private_handle_t*)handle;
+#if DEBUG_REFERENCES
+ if (hnd->lock_level > DEBUG_MAX_LOCK_LEVEL) {
+ LOG_FATAL("%s: unbalanced lock detected. lock level = %d",
+ __FUNCTION__, hnd->lock_level);
+ }
+ ++hnd->lock_level;
+#endif
+ void* base = reference_region("gralloc_lock_ycbcr", hnd);
+ formatToYcbcr(hnd->format, hnd->x_res, hnd->y_res, base, ycbcr);
+ return 0;
+}
diff --git a/guest/hals/hwcomposer/legacy/Android.mk b/guest/hals/hwcomposer/legacy/Android.mk
new file mode 100644
index 0000000..713d055
--- /dev/null
+++ b/guest/hals/hwcomposer/legacy/Android.mk
@@ -0,0 +1,75 @@
+# Copyright (C) 2016 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.
+
+LOCAL_PATH := $(call my-dir)
+
+# HAL module implemenation stored in
+# hw/<OVERLAY_HARDWARE_MODULE_ID>.<ro.product.board>.so
+
+# Old hwcomposer, relies on GLES composition
+include $(CLEAR_VARS)
+include $(LOCAL_PATH)/hwcomposer.mk
+LOCAL_CFLAGS += -DUSE_OLD_HWCOMPOSER
+LOCAL_MODULE := hwcomposer.gce_x86-deprecated
+include $(BUILD_SHARED_LIBRARY)
+
+# New hwcomposer, performs software composition
+include $(CLEAR_VARS)
+include $(LOCAL_PATH)/hwcomposer.mk
+LOCAL_MODULE := hwcomposer.gce_x86
+LOCAL_VENDOR_MODULE := true
+include $(BUILD_SHARED_LIBRARY)
+
+# An executable to run some tests
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := hwc_tests.gce_x86
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SHARED_LIBRARIES := \
+ libgceframebuffer \
+ liblog \
+ libcutils \
+ libutils \
+ libsync \
+ libhardware \
+ libjpeg \
+ $(GCE_STLPORT_LIBS)
+
+LOCAL_STATIC_LIBRARIES := \
+ libgcemetadata \
+ libyuv_static
+
+LOCAL_SRC_FILES := \
+ hwc_tests.cpp \
+ gce_composer.cpp \
+ base_composer.cpp \
+ geometry_utils.cpp
+
+
+LOCAL_CFLAGS += \
+ -DLOG_TAG=\"hwc_tests\" \
+ $(GCE_VERSION_CFLAGS)
+
+LOCAL_C_INCLUDES := \
+ device/google/gce/hwcomposer \
+ device/google/gce/include \
+ bionic \
+ $(GCE_STLPORT_INCLUDES)
+
+include device/google/gce/libs/base/libbase.mk
+LOCAL_SHARED_LIBRARIES += $(GCE_LIBBASE_LIB_NAME)
+LOCAL_C_INCLUDES += $(GCE_LIBBASE_INCLUDE_DIR)
+
+include $(BUILD_EXECUTABLE)
diff --git a/guest/hals/hwcomposer/legacy/base_composer.cpp b/guest/hals/hwcomposer/legacy/base_composer.cpp
new file mode 100644
index 0000000..dd23076
--- /dev/null
+++ b/guest/hals/hwcomposer/legacy/base_composer.cpp
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#include "base_composer.h"
+
+#include <cutils/log.h>
+#include "GceFrameBufferControl.h"
+#include "gralloc_gce_priv.h"
+
+namespace avd {
+
+namespace {
+
+int BroadcastFrameBufferChanged (int yoffset) {
+ return GceFrameBufferControl::getInstance().BroadcastFrameBufferChanged(
+ yoffset);
+}
+
+}
+
+BaseComposer::BaseComposer(int64_t vsync_base_timestamp, int32_t vsync_period_ns)
+ : fb_broadcaster_(BroadcastFrameBufferChanged),
+ vsync_base_timestamp_(vsync_base_timestamp),
+ vsync_period_ns_(vsync_period_ns) {
+ const gralloc_module_t* gralloc_module;
+ hw_get_module(GRALLOC_HARDWARE_MODULE_ID,
+ reinterpret_cast<const hw_module_t**>(&gralloc_module));
+}
+
+BaseComposer::~BaseComposer() {}
+
+FbBroadcaster BaseComposer::ReplaceFbBroadcaster(
+ FbBroadcaster fb_broadcaster) {
+ FbBroadcaster tmp = fb_broadcaster_;
+ fb_broadcaster_ = fb_broadcaster;
+ return tmp;
+}
+
+void BaseComposer::Dump(char* buff __unused, int buff_len __unused) {}
+
+
+int BaseComposer::PostFrameBuffer(buffer_handle_t buffer) {
+ const int yoffset = YOffsetFromHandle(buffer);
+ // If the broadcaster is NULL or could not get a good yoffset just ignore it.
+ if (fb_broadcaster_ && yoffset >= 0) {
+ int retval = fb_broadcaster_(yoffset);
+ if (retval){
+ ALOGI("Failed to post framebuffer");
+ return -1;
+ }
+ }
+
+ return yoffset;
+}
+
+int BaseComposer::PrepareLayers(size_t num_layers, gce_hwc_layer* layers) {
+ // find unsupported overlays
+ for (size_t i = 0; i < num_layers; i++) {
+ if (IS_TARGET_FRAMEBUFFER(layers[i].compositionType)) {
+ continue;
+ }
+ layers[i].compositionType = HWC_FRAMEBUFFER;
+ }
+ return 0;
+}
+
+int BaseComposer::SetLayers(size_t num_layers, gce_hwc_layer* layers) {
+ for (size_t idx = 0; idx < num_layers; idx++) {
+ if (IS_TARGET_FRAMEBUFFER(layers[idx].compositionType)) {
+ return PostFrameBuffer(layers[idx].handle);
+ }
+ }
+ return -1;
+}
+
+} // namespace avd
diff --git a/guest/hals/hwcomposer/legacy/base_composer.h b/guest/hals/hwcomposer/legacy/base_composer.h
new file mode 100644
index 0000000..8a17b6a
--- /dev/null
+++ b/guest/hals/hwcomposer/legacy/base_composer.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#ifndef GCE_OLD_HWCOMPOSER_GCE_COMPOSER_H
+#define GCE_OLD_HWCOMPOSER_GCE_COMPOSER_H
+
+#include <hardware/gralloc.h>
+#include "hwcomposer_common.h"
+
+namespace avd {
+
+typedef int (*FbBroadcaster)(int);
+
+class BaseComposer {
+ public:
+ BaseComposer(int64_t vsync_base_timestamp, int32_t vsync_period_ns);
+ ~BaseComposer();
+
+ // Sets the composition type of each layer and returns the number of layers
+ // to be composited by the hwcomposer.
+ int PrepareLayers(size_t num_layers, gce_hwc_layer* layers);
+ // Returns the yoffset that was broadcasted or a negative number if there was
+ // an error.
+ int SetLayers(size_t num_layers, gce_hwc_layer* layers);
+ // Returns yoffset of the handle or negative on error.
+ int PostFrameBuffer(buffer_handle_t handle);
+ // Changes the broadcaster, gives the ability to report more than just the
+ // yoffset by using a wrapper like the StatsKeepingComposer. Returns the old
+ // broadcaster. Passing a NULL pointer will cause the composer to not
+ // broadcast at all.
+ FbBroadcaster ReplaceFbBroadcaster(FbBroadcaster);
+ void Dump(char* buff, int buff_len);
+ protected:
+ int64_t vsync_base_timestamp_;
+ int32_t vsync_period_ns_;
+ private:
+ FbBroadcaster fb_broadcaster_;
+};
+
+} // namespace avd
+
+#endif
diff --git a/guest/hals/hwcomposer/legacy/gce_composer.cpp b/guest/hals/hwcomposer/legacy/gce_composer.cpp
new file mode 100644
index 0000000..412e818
--- /dev/null
+++ b/guest/hals/hwcomposer/legacy/gce_composer.cpp
@@ -0,0 +1,704 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#include "gce_composer.h"
+#include <GceFrameBuffer.h>
+#include <GceFrameBufferControl.h>
+#include <cutils/log.h>
+#include <hardware/hwcomposer.h>
+#include <hardware/hwcomposer_defs.h>
+#include <libyuv.h>
+#include <system/graphics.h>
+#include <algorithm>
+#include <cstdlib>
+#include <utility>
+#include <vector>
+#include "geometry_utils.h"
+#include "hwcomposer_common.h"
+
+namespace avd {
+
+namespace {
+
+// Ensures that the layer does not include any inconsistencies
+int SanityCheckLayer(const gce_hwc_layer& layer) {
+ // Check displayFrame
+ if (layer.displayFrame.left > layer.displayFrame.right ||
+ layer.displayFrame.top > layer.displayFrame.bottom) {
+ ALOGE(
+ "%s: Malformed rectangle (displayFrame): [left = %d, right = %d, top = "
+ "%d, bottom = %d]",
+ __FUNCTION__,
+ layer.displayFrame.left,
+ layer.displayFrame.right,
+ layer.displayFrame.top,
+ layer.displayFrame.bottom);
+ return -EINVAL;
+ }
+ // Check sourceCrop
+ if (layer.sourceCrop.left > layer.sourceCrop.right ||
+ layer.sourceCrop.top > layer.sourceCrop.bottom) {
+ ALOGE(
+ "%s: Malformed rectangle (sourceCrop): [left = %d, right = %d, top = "
+ "%d, bottom = %d]",
+ __FUNCTION__,
+ layer.sourceCrop.left,
+ layer.sourceCrop.right,
+ layer.sourceCrop.top,
+ layer.sourceCrop.bottom);
+ return -EINVAL;
+ }
+ const private_handle_t* p_handle =
+ reinterpret_cast<const private_handle_t*>(layer.handle);
+ if (!p_handle) {
+ ALOGE("Layer has a NULL buffer handle");
+ return -EINVAL;
+ }
+ if (layer.sourceCrop.left < 0 || layer.sourceCrop.top < 0 ||
+ layer.sourceCrop.right > p_handle->x_res ||
+ layer.sourceCrop.bottom > p_handle->y_res) {
+ ALOGE(
+ "%s: Invalid sourceCrop for buffer handle: sourceCrop = [left = %d, "
+ "right = %d, top = %d, bottom = %d], handle = [width = %d, height = "
+ "%d]",
+ __FUNCTION__,
+ layer.sourceCrop.left,
+ layer.sourceCrop.right,
+ layer.sourceCrop.top,
+ layer.sourceCrop.bottom,
+ p_handle->x_res,
+ p_handle->y_res);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+bool LayerNeedsScaling(const gce_hwc_layer& layer) {
+ int from_w = layer.sourceCrop.right - layer.sourceCrop.left;
+ int from_h = layer.sourceCrop.bottom - layer.sourceCrop.top;
+ int to_w = layer.displayFrame.right - layer.displayFrame.left;
+ int to_h = layer.displayFrame.bottom - layer.displayFrame.top;
+
+ bool not_rot_scale = from_w != to_w || from_h != to_h;
+ bool rot_scale = from_w != to_h || from_h != to_w;
+
+ bool needs_rot = layer.transform & HAL_TRANSFORM_ROT_90;
+
+ return needs_rot ? rot_scale : not_rot_scale;
+}
+
+bool LayerNeedsBlending(const gce_hwc_layer& layer) {
+ return layer.blending != HWC_BLENDING_NONE;
+}
+
+bool LayerNeedsAttenuation(const gce_hwc_layer& layer) {
+ return layer.blending == HWC_BLENDING_COVERAGE;
+}
+
+struct BufferSpec;
+typedef int (*ConverterFunction)(const BufferSpec& src, const BufferSpec& dst,
+ bool v_flip);
+int DoCopy(const BufferSpec& src, const BufferSpec& dst, bool v_flip);
+int ConvertFromYV12(const BufferSpec& src, const BufferSpec& dst, bool v_flip);
+ConverterFunction GetConverter(uint32_t format) {
+ switch (format) {
+ case HAL_PIXEL_FORMAT_RGBA_8888:
+ case HAL_PIXEL_FORMAT_RGBX_8888:
+ case HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED:
+ return &DoCopy;
+
+ case HAL_PIXEL_FORMAT_YV12:
+ return &ConvertFromYV12;
+
+ // Unsupported formats
+ // TODO(jemoreira): Conversion from these formats should be implemented as
+ // we find evidence of its usage.
+ // case HAL_PIXEL_FORMAT_BGRA_8888:
+
+ // case HAL_PIXEL_FORMAT_RGB_888:
+ // case HAL_PIXEL_FORMAT_RGB_565:
+
+ // case HAL_PIXEL_FORMAT_sRGB_A_8888:
+ // case HAL_PIXEL_FORMAT_sRGB_X_8888:
+
+ // case HAL_PIXEL_FORMAT_Y8:
+ // case HAL_PIXEL_FORMAT_Y16:
+
+ // case HAL_PIXEL_FORMAT_RAW_SENSOR:
+ // case HAL_PIXEL_FORMAT_BLOB:
+
+ // case HAL_PIXEL_FORMAT_YCbCr_420_888:
+ // case HAL_PIXEL_FORMAT_YCbCr_422_SP:
+ // case HAL_PIXEL_FORMAT_YCrCb_420_SP:
+ // case HAL_PIXEL_FORMAT_YCbCr_422_I:
+ default:
+ ALOGW("Unsupported format: 0x%04x, returning null converter function",
+ format);
+ }
+ return NULL;
+}
+
+// Whether we support a given format
+bool IsFormatSupported(uint32_t format) {
+ return GetConverter(format) != NULL;
+}
+
+bool CanCompositeLayer(const gce_hwc_layer& layer) {
+ if (layer.handle == NULL) {
+ ALOGW("%s received a layer with a null handler", __FUNCTION__);
+ return false;
+ }
+ int format = reinterpret_cast<const private_handle_t*>(layer.handle)->format;
+ if (!IsFormatSupported(format)) {
+ ALOGD("Unsupported pixel format: 0x%x, doing software composition instead",
+ format);
+ return false;
+ }
+ return true;
+}
+
+/*******************************************************************************
+Libyuv's convert functions only allow the combination of any rotation (multiple
+of 90 degrees) and a vertical flip, but not horizontal flips.
+Surfaceflinger's transformations are expressed in terms of a vertical flip, a
+horizontal flip and/or a single 90 degrees clockwise rotation (see
+NATIVE_WINDOW_TRANSFORM_HINT documentation on system/window.h for more insight).
+The following code allows to turn a horizontal flip into a 180 degrees rotation
+and a vertical flip.
+*******************************************************************************/
+libyuv::RotationMode GetRotationFromTransform(uint32_t transform) {
+ uint32_t rotation =
+ (transform & HAL_TRANSFORM_ROT_90) ? 1 : 0; // 1 * ROT90 bit
+ rotation += (transform & HAL_TRANSFORM_FLIP_H) ? 2 : 0; // 2 * VFLIP bit
+ return static_cast<libyuv::RotationMode>(90 * rotation);
+}
+
+bool GetVFlipFromTransform(uint32_t transform) {
+ // vertical flip xor horizontal flip
+ return ((transform & HAL_TRANSFORM_FLIP_V) >> 1) ^
+ (transform & HAL_TRANSFORM_FLIP_H);
+}
+
+struct BufferSpec {
+ uint8_t* buffer;
+ size_t size;
+ int width;
+ int height;
+ int stride;
+ int crop_x;
+ int crop_y;
+ int crop_width;
+ int crop_height;
+ uint32_t format;
+
+ BufferSpec(uint8_t* buffer, size_t size, int width, int height, int stride)
+ : buffer(buffer),
+ size(size),
+ width(width),
+ height(height),
+ stride(stride),
+ crop_x(0),
+ crop_y(0),
+ crop_width(width),
+ crop_height(height),
+ format(HAL_PIXEL_FORMAT_RGBA_8888) {}
+};
+
+int ConvertFromYV12(const BufferSpec& src, const BufferSpec& dst, bool v_flip) {
+ // use the stride in pixels as the source width
+ int stride_in_pixels = src.stride / formatToBytesPerPixel(src.format);
+
+ // The following calculation of plane offsets and alignments are based on
+ // swiftshader's Sampler::setTextureLevel() implementation
+ // (Renderer/Sampler.cpp:225)
+ uint8_t* src_y = src.buffer;
+ int stride_y = stride_in_pixels;
+ uint8_t* src_v = src_y + stride_y * src.height;
+ int stride_v = GceFrameBuffer::align(stride_y / 2, 16);
+ uint8_t* src_u = src_v + stride_v * src.height / 2;
+ int stride_u = GceFrameBuffer::align(stride_y / 2, 16);
+
+ // Adjust for crop
+ src_y += src.crop_y * stride_y + src.crop_x;
+ src_v += (src.crop_y / 2) * stride_v + (src.crop_x / 2);
+ src_u += (src.crop_y / 2) * stride_u + (src.crop_x / 2);
+ uint8_t* dst_buffer = dst.buffer + dst.crop_y * dst.stride +
+ dst.crop_x * formatToBytesPerPixel(dst.format);
+
+ // YV12 is the same as I420, with the U and V planes swapped
+ return libyuv::I420ToARGB(src_y, stride_y, src_v, stride_v, src_u, stride_u,
+ dst_buffer, dst.stride, dst.crop_width,
+ v_flip ? -dst.crop_height : dst.crop_height);
+}
+
+int DoConversion(const BufferSpec& src, const BufferSpec& dst, bool v_flip) {
+ return (*GetConverter(src.format))(src, dst, v_flip);
+}
+
+int DoCopy(const BufferSpec& src, const BufferSpec& dst, bool v_flip) {
+ // Point to the upper left corner of the crop rectangle
+ uint8_t* src_buffer = src.buffer + src.crop_y * src.stride +
+ src.crop_x * formatToBytesPerPixel(src.format);
+ uint8_t* dst_buffer = dst.buffer + dst.crop_y * dst.stride +
+ dst.crop_x * formatToBytesPerPixel(dst.format);
+ int width = src.crop_width;
+ int height = src.crop_height;
+
+ if (v_flip) {
+ height = -height;
+ }
+
+ // HAL formats are named based on the order of the pixel componets on the
+ // byte stream, while libyuv formats are named based on the order of those
+ // pixel components in an integer written from left to right. So
+ // libyuv::FOURCC_ARGB is equivalent to HAL_PIXEL_FORMAT_BGRA_8888.
+ return libyuv::ARGBCopy(src_buffer, src.stride, dst_buffer, dst.stride, width,
+ height);
+}
+
+int DoRotation(const BufferSpec& src, const BufferSpec& dst,
+ libyuv::RotationMode rotation, bool v_flip) {
+ // Point to the upper left corner of the crop rectangles
+ uint8_t* src_buffer = src.buffer + src.crop_y * src.stride +
+ src.crop_x * formatToBytesPerPixel(src.format);
+ uint8_t* dst_buffer = dst.buffer + dst.crop_y * dst.stride +
+ dst.crop_x * formatToBytesPerPixel(dst.format);
+ int width = src.crop_width;
+ int height = src.crop_height;
+
+ if (v_flip) {
+ height = -height;
+ }
+
+ return libyuv::ARGBRotate(src_buffer, src.stride, dst_buffer, dst.stride,
+ width, height, rotation);
+}
+
+int DoScaling(const BufferSpec& src, const BufferSpec& dst, bool v_flip) {
+ // Point to the upper left corner of the crop rectangles
+ uint8_t* src_buffer = src.buffer + src.crop_y * src.stride +
+ src.crop_x * formatToBytesPerPixel(src.format);
+ uint8_t* dst_buffer = dst.buffer + dst.crop_y * dst.stride +
+ dst.crop_x * formatToBytesPerPixel(dst.format);
+ int src_width = src.crop_width;
+ int src_height = src.crop_height;
+ int dst_width = dst.crop_width;
+ int dst_height = dst.crop_height;
+
+ if (v_flip) {
+ src_height = -src_height;
+ }
+
+ return libyuv::ARGBScale(src_buffer, src.stride, src_width, src_height,
+ dst_buffer, dst.stride, dst_width, dst_height,
+ libyuv::kFilterBilinear);
+}
+
+int DoAttenuation(const BufferSpec& src, const BufferSpec& dest, bool v_flip) {
+ // Point to the upper left corner of the crop rectangles
+ uint8_t* src_buffer = src.buffer + src.crop_y * src.stride +
+ src.crop_x * formatToBytesPerPixel(src.format);
+ uint8_t* dst_buffer = dest.buffer + dest.crop_y * dest.stride +
+ dest.crop_x * formatToBytesPerPixel(dest.format);
+ int width = dest.crop_width;
+ int height = dest.crop_height;
+
+ if (v_flip) {
+ height = -height;
+ }
+
+ return libyuv::ARGBAttenuate(src_buffer, src.stride, dst_buffer, dest.stride,
+ width, height);
+}
+
+int DoBlending(const BufferSpec& src, const BufferSpec& dest, bool v_flip) {
+ // Point to the upper left corner of the crop rectangles
+ uint8_t* src_buffer = src.buffer + src.crop_y * src.stride +
+ src.crop_x * formatToBytesPerPixel(src.format);
+ uint8_t* dst_buffer = dest.buffer + dest.crop_y * dest.stride +
+ dest.crop_x * formatToBytesPerPixel(dest.format);
+ int width = dest.crop_width;
+ int height = dest.crop_height;
+
+ if (v_flip) {
+ height = -height;
+ }
+
+ // libyuv's ARGB format is hwcomposer's BGRA format, since blending only cares
+ // for the position of alpha in the pixel and not the position of the colors
+ // this function is perfectly usable.
+ return libyuv::ARGBBlend(src_buffer, src.stride, dst_buffer, dest.stride,
+ dst_buffer, dest.stride, width, height);
+}
+
+} // namespace
+
+// Returns a handle to the appropriate framebuffer to use:
+// - the one provided by surfaceflinger if it is doing any GLES composition
+// - the next hwc-only framebuffer otherwise
+// Takes care of rotating the hwc-only framebuffers
+buffer_handle_t GceComposer::FindFrameBuffer(int num_layers,
+ gce_hwc_layer* layers) {
+ buffer_handle_t* fb_handle = NULL;
+ bool use_hwc_fb = true;
+ // The framebuffer target is usually the last layer in the list, so iterate in
+ // reverse
+ for (int idx = num_layers - 1; idx >= 0; --idx) {
+ if (IS_TARGET_FRAMEBUFFER(layers[idx].compositionType)) {
+ fb_handle = &layers[idx].handle;
+ } else if (layers[idx].compositionType != HWC_OVERLAY) {
+ use_hwc_fb = false;
+ // Just in case the FB target was not found yet
+ if (fb_handle) break;
+ }
+ }
+ if (use_hwc_fb && !hwc_framebuffers_.empty()) {
+ fb_handle = &hwc_framebuffers_[next_hwc_framebuffer_];
+ next_hwc_framebuffer_ =
+ (next_hwc_framebuffer_ + 1) % hwc_framebuffers_.size();
+ }
+
+ return *fb_handle;
+}
+
+void GceComposer::CompositeLayer(gce_hwc_layer* src_layer,
+ buffer_handle_t dst_handle) {
+ libyuv::RotationMode rotation =
+ GetRotationFromTransform(src_layer->transform);
+
+ const private_handle_t* src_priv_handle =
+ reinterpret_cast<const private_handle_t*>(src_layer->handle);
+ const private_handle_t* dst_priv_handle =
+ reinterpret_cast<const private_handle_t*>(dst_handle);
+
+ bool needs_conversion = src_priv_handle->format != dst_priv_handle->format;
+ bool needs_scaling = LayerNeedsScaling(*src_layer);
+ bool needs_rotation = rotation != libyuv::kRotate0;
+ bool needs_transpose = needs_rotation && rotation != libyuv::kRotate180;
+ bool needs_vflip = GetVFlipFromTransform(src_layer->transform);
+ bool needs_attenuation = LayerNeedsAttenuation(*src_layer);
+ bool needs_blending = LayerNeedsBlending(*src_layer);
+ bool needs_copy = !(needs_conversion || needs_scaling || needs_rotation ||
+ needs_vflip || needs_attenuation || needs_blending);
+
+ uint8_t* src_buffer;
+ uint8_t* dst_buffer;
+ int retval = gralloc_module_->lock(
+ gralloc_module_, src_layer->handle, GRALLOC_USAGE_SW_READ_OFTEN, 0, 0,
+ src_priv_handle->x_res, src_priv_handle->y_res,
+ reinterpret_cast<void**>(&src_buffer));
+ if (retval) {
+ ALOGE("Got error code %d from lock function", retval);
+ return;
+ }
+ retval = gralloc_module_->lock(gralloc_module_, dst_handle,
+ GRALLOC_USAGE_SW_WRITE_OFTEN, 0, 0,
+ dst_priv_handle->x_res, dst_priv_handle->y_res,
+ reinterpret_cast<void**>(&dst_buffer));
+ if (retval) {
+ ALOGE("Got error code %d from lock function", retval);
+ // TODO(jemoreira): Use a lock_guard-like object.
+ gralloc_module_->unlock(gralloc_module_, src_priv_handle);
+ return;
+ }
+
+ BufferSpec src_layer_spec(src_buffer, src_priv_handle->total_size,
+ src_priv_handle->x_res, src_priv_handle->y_res,
+ src_priv_handle->stride_in_pixels *
+ formatToBytesPerPixel(src_priv_handle->format));
+ src_layer_spec.crop_x = src_layer->sourceCrop.left;
+ src_layer_spec.crop_y = src_layer->sourceCrop.top;
+ src_layer_spec.crop_width =
+ src_layer->sourceCrop.right - src_layer->sourceCrop.left;
+ src_layer_spec.crop_height =
+ src_layer->sourceCrop.bottom - src_layer->sourceCrop.top;
+ src_layer_spec.format = src_priv_handle->format;
+
+ BufferSpec dst_layer_spec(dst_buffer, dst_priv_handle->total_size,
+ dst_priv_handle->x_res, dst_priv_handle->y_res,
+ dst_priv_handle->stride_in_pixels *
+ formatToBytesPerPixel(dst_priv_handle->format));
+ dst_layer_spec.crop_x = src_layer->displayFrame.left;
+ dst_layer_spec.crop_y = src_layer->displayFrame.top;
+ dst_layer_spec.crop_width =
+ src_layer->displayFrame.right - src_layer->displayFrame.left;
+ dst_layer_spec.crop_height =
+ src_layer->displayFrame.bottom - src_layer->displayFrame.top;
+ dst_layer_spec.format = dst_priv_handle->format;
+
+ // Add the destination layer to the bottom of the buffer stack
+ std::vector<BufferSpec> dest_buffer_stack(1, dst_layer_spec);
+
+ // If more than operation is to be performed, a temporary buffer is needed for
+ // each additional operation
+
+ // N operations need N destination buffers, the destination layer (the
+ // framebuffer) is one of them, so only N-1 temporary buffers are needed.
+ // Vertical flip is not taken into account because it can be done together
+ // with any other operation.
+ int needed_tmp_buffers = (needs_conversion ? 1 : 0) +
+ (needs_scaling ? 1 : 0) + (needs_rotation ? 1 : 0) +
+ (needs_attenuation ? 1 : 0) +
+ (needs_blending ? 1 : 0) + (needs_copy ? 1 : 0) - 1;
+
+ int x_res = src_layer->displayFrame.right - src_layer->displayFrame.left;
+ int y_res = src_layer->displayFrame.bottom - src_layer->displayFrame.top;
+ size_t output_frame_size =
+ x_res * y_res * formatToBytesPerPixel(dst_priv_handle->format);
+ while (needed_tmp_buffers > 0) {
+ BufferSpec tmp(
+ RotateTmpBuffer(needed_tmp_buffers), output_frame_size, x_res, y_res,
+ // There should be no danger of overflow aligning the stride because
+ // these sizes are taken from the displayFrame rectangle which is always
+ // smaller than the framebuffer, the framebuffer in turn has aligned
+ // stride and these buffers are the size of the framebuffer.
+ GceFrameBuffer::align(
+ x_res * formatToBytesPerPixel(dst_priv_handle->format), 16));
+ dest_buffer_stack.push_back(tmp);
+ needed_tmp_buffers--;
+ }
+
+ // Conversion and scaling should always be the first operations, so that every
+ // other operation works on equally sized frames (garanteed to fit in the tmp
+ // buffers)
+
+ // TODO(jemoreira): We are converting to ARGB as the first step under the
+ // assumption that scaling ARGB is faster than scaling I420 (the most common).
+ // This should be confirmed with testing.
+ if (needs_conversion) {
+ BufferSpec& dst_buffer_spec = dest_buffer_stack.back();
+ if (needs_scaling || needs_transpose) {
+ // If a rotation or a scaling operation are needed the dimensions at the
+ // top of the buffer stack are wrong (wrong sizes for scaling, swapped
+ // width and height for 90 and 270 rotations).
+ // Make width and height match the crop sizes on the source
+ int src_width = src_layer_spec.crop_width;
+ int src_height = src_layer_spec.crop_height;
+ int dst_stride = GceFrameBuffer::align(
+ src_width * formatToBytesPerPixel(dst_priv_handle->format), 16);
+ size_t needed_size = dst_stride * src_height;
+ dst_buffer_spec.width = src_width;
+ dst_buffer_spec.height = src_height;
+ // Ajust the stride accordingly
+ dst_buffer_spec.stride = dst_stride;
+ // Crop sizes also need to be adjusted
+ dst_buffer_spec.crop_width = src_width;
+ dst_buffer_spec.crop_height = src_height;
+ dst_buffer_spec.size = needed_size;
+ // crop_x and y are fine at 0, format is already set to match destination
+
+ // In case of a scale, the source frame may be bigger than the default tmp
+ // buffer size
+ if (needed_size > tmp_buffer_.size() / kNumTmpBufferPieces) {
+ dst_buffer_spec.buffer = GetSpecialTmpBuffer(needed_size);
+ }
+ }
+ retval = DoConversion(src_layer_spec, dst_buffer_spec, needs_vflip);
+ if (retval) {
+ ALOGE("Got error code %d from DoConversion function", retval);
+ }
+ needs_vflip = false;
+ src_layer_spec = dst_buffer_spec;
+ dest_buffer_stack.pop_back();
+ }
+
+ if (needs_scaling) {
+ BufferSpec& dst_buffer_spec = dest_buffer_stack.back();
+ if (needs_transpose) {
+ // If a rotation is needed, the temporary buffer has the correct size but
+ // needs to be transposed and have its stride updated accordingly. The
+ // crop sizes also needs to be transposed, but not the x and y since they
+ // are both zero in a temporary buffer (and it is a temporary buffer
+ // because a rotation will be performed next).
+ std::swap(dst_buffer_spec.width, dst_buffer_spec.height);
+ std::swap(dst_buffer_spec.crop_width, dst_buffer_spec.crop_height);
+ // TODO (jemoreira): Aligment (To align here may cause the needed size to
+ // be bigger than the buffer, so care should be taken)
+ dst_buffer_spec.stride = dst_buffer_spec.width *
+ formatToBytesPerPixel(dst_priv_handle->format);
+ }
+ retval = DoScaling(src_layer_spec, dst_buffer_spec, needs_vflip);
+ needs_vflip = false;
+ if (retval) {
+ ALOGE("Got error code %d from DoScaling function", retval);
+ }
+ src_layer_spec = dst_buffer_spec;
+ dest_buffer_stack.pop_back();
+ }
+
+ if (needs_rotation) {
+ retval = DoRotation(src_layer_spec, dest_buffer_stack.back(), rotation,
+ needs_vflip);
+ needs_vflip = false;
+ if (retval) {
+ ALOGE("Got error code %d from DoTransform function", retval);
+ }
+ src_layer_spec = dest_buffer_stack.back();
+ dest_buffer_stack.pop_back();
+ }
+
+ if (needs_attenuation) {
+ retval =
+ DoAttenuation(src_layer_spec, dest_buffer_stack.back(), needs_vflip);
+ needs_vflip = false;
+ if (retval) {
+ ALOGE("Got error code %d from DoBlending function", retval);
+ }
+ src_layer_spec = dest_buffer_stack.back();
+ dest_buffer_stack.pop_back();
+ }
+
+ if (needs_copy) {
+ retval = DoCopy(src_layer_spec, dest_buffer_stack.back(), needs_vflip);
+ needs_vflip = false;
+ if (retval) {
+ ALOGE("Got error code %d from DoBlending function", retval);
+ }
+ src_layer_spec = dest_buffer_stack.back();
+ dest_buffer_stack.pop_back();
+ }
+
+ // Blending (if needed) should always be the last operation, so that it reads
+ // and writes in the destination layer and not some temporary buffer.
+ if (needs_blending) {
+ retval = DoBlending(src_layer_spec, dest_buffer_stack.back(), needs_vflip);
+ needs_vflip = false;
+ if (retval) {
+ ALOGE("Got error code %d from DoBlending function", retval);
+ }
+ // Don't need to assign destination to source in the last one
+ dest_buffer_stack.pop_back();
+ }
+
+ gralloc_module_->unlock(gralloc_module_, src_priv_handle);
+ gralloc_module_->unlock(gralloc_module_, dst_priv_handle);
+}
+
+/* static */ const int GceComposer::kNumTmpBufferPieces = 2;
+
+GceComposer::GceComposer(int64_t vsync_base_timestamp, int32_t vsync_period_ns)
+ : BaseComposer(vsync_base_timestamp, vsync_period_ns),
+ tmp_buffer_(kNumTmpBufferPieces *
+ GceFrameBuffer::getInstance().bufferSize()),
+ next_hwc_framebuffer_(0) {
+ hw_get_module(GRALLOC_HARDWARE_MODULE_ID,
+ reinterpret_cast<const hw_module_t**>(&gralloc_module_));
+ gralloc_module_->common.methods->open(
+ reinterpret_cast<const hw_module_t*>(gralloc_module_),
+ GRALLOC_HARDWARE_GPU0,
+ reinterpret_cast<hw_device_t**>(&gralloc_dev_));
+ for (int i = 0; i < GceFrameBuffer::kNumHwcBuffers; ++i) {
+ buffer_handle_t tmp;
+ gralloc_dev_->alloc_hwc_framebuffer(
+ reinterpret_cast<alloc_device_t*>(gralloc_dev_), &tmp);
+ hwc_framebuffers_.push_back(tmp);
+ }
+}
+
+GceComposer::~GceComposer() {
+ // Free the hwc fb handles
+ for (int idx = 0; idx < hwc_framebuffers_.size(); ++idx) {
+ gralloc_dev_->device.free(reinterpret_cast<alloc_device_t*>(gralloc_dev_),
+ hwc_framebuffers_[idx]);
+ }
+
+ // close devices
+ gralloc_dev_->device.common.close(reinterpret_cast<hw_device_t*>(gralloc_dev_));
+}
+
+int GceComposer::PrepareLayers(size_t num_layers, gce_hwc_layer* layers) {
+ int composited_layers_count = 0;
+
+ // Loop over layers in inverse order of z-index
+ for (size_t layer_index = num_layers; layer_index > 0;) {
+ // Decrement here to be able to compare unsigned integer with 0 in the
+ // loop condition
+ --layer_index;
+ if (IS_TARGET_FRAMEBUFFER(layers[layer_index].compositionType)) {
+ continue;
+ }
+ if (layers[layer_index].flags & HWC_SKIP_LAYER) {
+ continue;
+ }
+ if (layers[layer_index].compositionType == HWC_BACKGROUND) {
+ layers[layer_index].compositionType = HWC_FRAMEBUFFER;
+ continue;
+ }
+ layers[layer_index].compositionType = HWC_OVERLAY;
+ // Hwcomposer cannot draw below software-composed layers, so we need
+ // to mark those HWC_FRAMEBUFFER as well.
+ for (size_t top_idx = layer_index + 1; top_idx < num_layers; ++top_idx) {
+ // layers marked as skip are in a state that makes them unreliable to
+ // read, so it's best to assume they cover the whole screen
+ if (layers[top_idx].flags & HWC_SKIP_LAYER ||
+ (layers[top_idx].compositionType == HWC_FRAMEBUFFER &&
+ LayersOverlap(layers[layer_index], layers[top_idx]))) {
+ layers[layer_index].compositionType = HWC_FRAMEBUFFER;
+ break;
+ }
+ }
+ if (layers[layer_index].compositionType == HWC_OVERLAY &&
+ !CanCompositeLayer(layers[layer_index])) {
+ layers[layer_index].compositionType = HWC_FRAMEBUFFER;
+ }
+ if (layers[layer_index].compositionType == HWC_OVERLAY) {
+ ++composited_layers_count;
+ }
+ }
+ return composited_layers_count;
+}
+
+int GceComposer::SetLayers(size_t num_layers, gce_hwc_layer* layers) {
+ int targetFbs = 0;
+ buffer_handle_t fb_handle = FindFrameBuffer(num_layers, layers);
+ if (!fb_handle) {
+ ALOGE("%s: framebuffer handle is null", __FUNCTION__);
+ return -1;
+ }
+ // TODO(jemoreira): Lock all HWC_OVERLAY layers and the framebuffer before
+ // this loop and unlock them after. The way it's done now causes the target
+ // framebuffer to be locked and unlocked many times, if regions are
+ // implemented it will also be true for every layer that covers more than one
+ // region.
+ for (size_t idx = 0; idx < num_layers; idx++) {
+ if (IS_TARGET_FRAMEBUFFER(layers[idx].compositionType)) {
+ ++targetFbs;
+ } else if (layers[idx].compositionType == HWC_OVERLAY &&
+ !(layers[idx].flags & HWC_SKIP_LAYER)) {
+ if (SanityCheckLayer(layers[idx])) {
+ ALOGE("Layer (%d) failed sanity check", idx);
+ return -EINVAL;
+ }
+ CompositeLayer(&layers[idx], fb_handle);
+ }
+ }
+ if (targetFbs != 1) {
+ ALOGW("Saw %d layers, posted=%d", num_layers, targetFbs);
+ }
+ return PostFrameBuffer(fb_handle);
+}
+
+uint8_t* GceComposer::RotateTmpBuffer(unsigned int order) {
+ return &tmp_buffer_[(order % kNumTmpBufferPieces) * tmp_buffer_.size() /
+ kNumTmpBufferPieces];
+}
+
+uint8_t* GceComposer::GetSpecialTmpBuffer(size_t needed_size) {
+ special_tmp_buffer_.resize(needed_size);
+ return &special_tmp_buffer_[0];
+}
+
+} // namespace avd
diff --git a/guest/hals/hwcomposer/legacy/gce_composer.h b/guest/hals/hwcomposer/legacy/gce_composer.h
new file mode 100644
index 0000000..aa5171d
--- /dev/null
+++ b/guest/hals/hwcomposer/legacy/gce_composer.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#ifndef GCE_HWCOMPOSER_GCE_COMPOSER_H
+#define GCE_HWCOMPOSER_GCE_COMPOSER_H
+
+#include <hardware/gralloc.h>
+#include "gralloc_gce_priv.h"
+#include "hwcomposer_common.h"
+#include "base_composer.h"
+
+#include <vector>
+
+namespace avd {
+
+class GceComposer : public BaseComposer {
+ public:
+ GceComposer(int64_t vsync_base_timestamp, int32_t vsync_period_ns);
+ ~GceComposer();
+
+ // override
+ int PrepareLayers(size_t num_layers, gce_hwc_layer* layers);
+ // override
+ int SetLayers(size_t num_layers, gce_hwc_layer* layers);
+
+ protected:
+ static const int kNumTmpBufferPieces;
+ uint8_t* RotateTmpBuffer(unsigned int order);
+ uint8_t* GetSpecialTmpBuffer(size_t needed_size);
+ buffer_handle_t FindFrameBuffer(int num_layers, gce_hwc_layer* layers);
+ void CompositeLayer(gce_hwc_layer* src_layer, buffer_handle_t dst_layer);
+ std::vector<uint8_t> tmp_buffer_;
+ std::vector<uint8_t> special_tmp_buffer_;
+ const gralloc_module_t* gralloc_module_;
+ priv_alloc_device_t* gralloc_dev_;
+ std::vector<buffer_handle_t> hwc_framebuffers_;
+ int next_hwc_framebuffer_;
+};
+
+} // namespace avd
+
+#endif
diff --git a/guest/hals/hwcomposer/legacy/geometry_utils.cpp b/guest/hals/hwcomposer/legacy/geometry_utils.cpp
new file mode 100644
index 0000000..6102d81
--- /dev/null
+++ b/guest/hals/hwcomposer/legacy/geometry_utils.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#include "geometry_utils.h"
+#include <algorithm>
+#include <utility>
+
+namespace avd {
+
+bool LayersOverlap(const gce_hwc_layer& layer1, const gce_hwc_layer& layer2) {
+ int left1 = layer1.displayFrame.left;
+ int right1 = layer1.displayFrame.right;
+ int top1 = layer1.displayFrame.top;
+ int bottom1 = layer1.displayFrame.bottom;
+
+ int left2 = layer2.displayFrame.left;
+ int right2 = layer2.displayFrame.right;
+ int top2 = layer2.displayFrame.top;
+ int bottom2 = layer2.displayFrame.bottom;
+
+ bool overlap_x = left1 < right2 && left2 < right1;
+ bool overlap_y = top1 < bottom2 && top2 < bottom1;
+
+ return overlap_x && overlap_y;
+}
+
+} // namespace avd
diff --git a/guest/hals/hwcomposer/legacy/geometry_utils.h b/guest/hals/hwcomposer/legacy/geometry_utils.h
new file mode 100644
index 0000000..a575d2f
--- /dev/null
+++ b/guest/hals/hwcomposer/legacy/geometry_utils.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#ifndef GCE_HWCOMPOSER_GEOMETRY_UTILS_H
+#define GCE_HWCOMPOSER_GEOMETRY_UTILS_H
+
+#include "hwcomposer_common.h"
+
+namespace avd {
+
+bool LayersOverlap(const gce_hwc_layer& layer1, const gce_hwc_layer& layer2);
+
+} // namespace avd
+
+#endif
diff --git a/guest/hals/hwcomposer/legacy/hwc_tests.cpp b/guest/hals/hwcomposer/legacy/hwc_tests.cpp
new file mode 100644
index 0000000..86a3c6e
--- /dev/null
+++ b/guest/hals/hwcomposer/legacy/hwc_tests.cpp
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+#include "gce_composer.h"
+#include <stdio.h>
+
+// This executable is only intended to perform simple tests on the hwcomposer
+// functionality. It should not be part of the images, but rather be included
+// (via scp) when needed to test specific scenarios that are hard to reproduce
+// in the normal operation of the device.
+
+class HWC_Tester : public avd::GceComposer {
+ public:
+ HWC_Tester() : avd::GceComposer(int64_t(0), int32_t(16000000)) {}
+ int RunTest() {
+ // Allocate two buffers (1x1 and 800x1280)
+ buffer_handle_t src_handle;
+ int src_stride;
+ int res = gralloc_dev_->device.alloc(&gralloc_dev_->device,
+ 1,
+ 1,
+ HAL_PIXEL_FORMAT_RGBA_8888,
+ GRALLOC_USAGE_SW_READ_OFTEN,
+ &src_handle,
+ &src_stride);
+ if (res) {
+ fprintf(stderr, "Error allocating source buffer, see logs for details\n");
+ return -1;
+ }
+ buffer_handle_t dst_handle;
+ int dst_stride;
+ res = gralloc_dev_->device.alloc(&gralloc_dev_->device,
+ 800,
+ 1280,
+ HAL_PIXEL_FORMAT_RGBA_8888,
+ GRALLOC_USAGE_SW_WRITE_OFTEN,
+ &dst_handle,
+ &dst_stride);
+ if (res) {
+ fprintf(stderr,
+ "Error allocating destination buffer, see logs for details\n");
+ return -1;
+ }
+ // Create a mock layer requesting a sinple copy of the pixels so that DoCopy gets called
+ gce_hwc_layer src_layer;
+ src_layer.compositionType = HWC_OVERLAY;
+ src_layer.hints = 0;
+ src_layer.flags = 0;
+ src_layer.handle = src_handle;
+
+ // No transformation, just a copy
+ src_layer.transform = 0;
+ src_layer.blending = HWC_BLENDING_NONE;
+
+ src_layer.sourceCrop.top = 0;
+ src_layer.sourceCrop.left = 0;
+ src_layer.sourceCrop.bottom = 1;
+ src_layer.sourceCrop.right = 1;
+
+ src_layer.displayFrame.top = 0;
+ src_layer.displayFrame.left = 0;
+ src_layer.displayFrame.bottom = 1;
+ src_layer.displayFrame.right = 1;
+
+ src_layer.visibleRegionScreen.numRects = 0;
+ src_layer.visibleRegionScreen.rects = NULL;
+
+ src_layer.acquireFenceFd = -1;
+ src_layer.releaseFenceFd = -1;
+ // Call CompositeLayer
+ CompositeLayer(&src_layer, dst_handle);
+ // If we got this far without a SEGFAULT we call it success
+ printf("OK\n");
+ gralloc_dev_->device.free(&gralloc_dev_->device, src_handle);
+ gralloc_dev_->device.free(&gralloc_dev_->device, dst_handle);
+ return 0;
+ }
+};
+
+int main() {
+ HWC_Tester t;
+ return t.RunTest();
+}
diff --git a/guest/hals/hwcomposer/legacy/hwcomposer.cpp b/guest/hals/hwcomposer/legacy/hwcomposer.cpp
new file mode 100644
index 0000000..670186b
--- /dev/null
+++ b/guest/hals/hwcomposer/legacy/hwcomposer.cpp
@@ -0,0 +1,389 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+// Versions of hwcomposer we implement:
+// JB: 0.3
+// JB-MR1 to N : 1.1
+// N-MR1 to ... : We report 1.1 but SurfaceFlinger has the option to use an
+// adapter to treat our 1.1 hwcomposer as a 2.0. If SF stops using that adapter
+// to support 1.1 implementations it can be copied into gce from
+// frameworks/native/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.*
+
+#include <api_level_fixes.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <math.h>
+#include <poll.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/resource.h>
+#include <sys/time.h>
+
+#define HWC_REMOVE_DEPRECATED_VERSIONS 1
+
+#include <cutils/compiler.h>
+#include <cutils/log.h>
+#include <cutils/properties.h>
+#include <hardware/gralloc.h>
+#include <hardware/hardware.h>
+#include <hardware/hwcomposer.h>
+#include <hardware/hwcomposer_defs.h>
+#include <utils/String8.h>
+#include <utils/Vector.h>
+
+#include <GceFrameBuffer.h>
+#include <GceFrameBufferControl.h>
+#include <gralloc_gce_priv.h>
+#include <remoter_framework_pkt.h>
+#include <sync/sync.h>
+
+#include "gce_composer.h"
+#include "geometry_utils.h"
+#include "hwcomposer_common.h"
+#include "base_composer.h"
+#include "stats_keeper.h"
+
+
+#ifdef USE_OLD_HWCOMPOSER
+typedef avd::BaseComposer InnerComposerType;
+#else
+typedef avd::GceComposer InnerComposerType;
+#endif
+
+
+#ifdef GATHER_STATS
+typedef avd::StatsKeepingComposer<InnerComposerType> ComposerType;
+#else
+typedef InnerComposerType ComposerType;
+#endif
+
+struct gce_hwc_composer_device_1_t {
+ gce_hwc_device base;
+ const hwc_procs_t* procs;
+ pthread_t vsync_thread;
+ int64_t vsync_base_timestamp;
+ int32_t vsync_period_ns;
+ ComposerType* composer;
+};
+
+static void dump_layer(gce_hwc_layer const* l) {
+ ALOGI(
+ "\ttype=%d, flags=%08x, handle=%p, tr=%02x, blend=%04x, "
+ "{%d,%d,%d,%d}, {%d,%d,%d,%d}",
+ l->compositionType, l->flags, l->handle, l->transform, l->blending,
+ l->sourceCrop.left, l->sourceCrop.top, l->sourceCrop.right,
+ l->sourceCrop.bottom, l->displayFrame.left, l->displayFrame.top,
+ l->displayFrame.right, l->displayFrame.bottom);
+}
+
+#if GCE_PLATFORM_SDK_BEFORE(J_MR1)
+static int gce_hwc_prepare(gce_hwc_device* dev, hwc_layer_list_t* list) {
+#else
+static int gce_hwc_prepare(gce_hwc_device* dev, size_t numDisplays,
+ hwc_display_contents_1_t** displays) {
+ if (!numDisplays || !displays) return 0;
+
+ hwc_display_contents_1_t* list = displays[HWC_DISPLAY_PRIMARY];
+
+ if (!list) return 0;
+#endif
+ int composited_layers_count =
+ reinterpret_cast<gce_hwc_composer_device_1_t*>(dev)
+ ->composer->PrepareLayers(list->numHwLayers, &list->hwLayers[0]);
+ return 0;
+}
+
+#if GCE_PLATFORM_SDK_BEFORE(J_MR1)
+int gce_hwc_set(struct hwc_composer_device* dev, hwc_display_t dpy,
+ hwc_surface_t sur, hwc_layer_list_t* list) {
+ reinterpret_cast<gce_hwc_composer_device_1_t*>(dev)->composer->SetLayers(
+ list->numHwLayers, &list->hwLayers[0]);
+ return 0;
+}
+#else
+static int gce_hwc_set(gce_hwc_device* dev, size_t numDisplays,
+ hwc_display_contents_1_t** displays) {
+ if (!numDisplays || !displays) return 0;
+
+ hwc_display_contents_1_t* contents = displays[HWC_DISPLAY_PRIMARY];
+ if (!contents) return 0;
+
+ gce_hwc_layer* layers = &contents->hwLayers[0];
+ reinterpret_cast<gce_hwc_composer_device_1_t*>(dev)->composer->SetLayers(
+ contents->numHwLayers, layers);
+
+ int closedFds = 0;
+ for (size_t index = 0; index < contents->numHwLayers; ++index) {
+ if (layers[index].acquireFenceFd != -1) {
+ close(layers[index].acquireFenceFd);
+ layers[index].acquireFenceFd = -1;
+ ++closedFds;
+ }
+ }
+ if (closedFds) {
+ ALOGI("Saw %d layers, closed=%d", contents->numHwLayers, closedFds);
+ }
+
+ // TODO(ghartman): This should be set before returning. On the next set it
+ // should be signalled when we load the new frame.
+ contents->retireFenceFd = -1;
+ return 0;
+}
+#endif
+
+static void gce_hwc_register_procs(gce_hwc_device* dev,
+ const hwc_procs_t* procs) {
+ struct gce_hwc_composer_device_1_t* pdev =
+ (struct gce_hwc_composer_device_1_t*)dev;
+ pdev->procs = procs;
+}
+
+static int gce_hwc_query(gce_hwc_device* dev, int what, int* value) {
+ struct gce_hwc_composer_device_1_t* pdev =
+ (struct gce_hwc_composer_device_1_t*)dev;
+
+ switch (what) {
+ case HWC_BACKGROUND_LAYER_SUPPORTED:
+ // we support the background layer
+ value[0] = 0;
+ break;
+ case HWC_VSYNC_PERIOD:
+ value[0] = pdev->vsync_period_ns;
+ break;
+ default:
+ // unsupported query
+ ALOGE("%s badness unsupported query what=%d", __FUNCTION__, what);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int gce_hwc_event_control(
+#if GCE_PLATFORM_SDK_BEFORE(J_MR1)
+ gce_hwc_device* dev, int event, int /*enabled*/) {
+#else
+ gce_hwc_device* dev, int /*dpy*/, int event, int /*enabled*/) {
+#endif
+ struct gce_hwc_composer_device_1_t* pdev =
+ (struct gce_hwc_composer_device_1_t*)dev;
+
+ if (event == HWC_EVENT_VSYNC) {
+ return 0;
+ }
+ return -EINVAL;
+}
+
+static void* hwc_vsync_thread(void* data) {
+ struct gce_hwc_composer_device_1_t* pdev =
+ (struct gce_hwc_composer_device_1_t*)data;
+ setpriority(PRIO_PROCESS, 0, HAL_PRIORITY_URGENT_DISPLAY);
+
+ int64_t base_timestamp = pdev->vsync_base_timestamp;
+ int64_t last_logged = base_timestamp / 1e9;
+ int sent = 0;
+ int last_sent = 0;
+ static const int log_interval = 60;
+ while (true) {
+ struct timespec rt;
+ if (clock_gettime(CLOCK_MONOTONIC, &rt) == -1) {
+ ALOGE("%s:%d error in vsync thread clock_gettime: %s", __FILE__, __LINE__,
+ strerror(errno));
+ }
+ int64_t timestamp = int64_t(rt.tv_sec) * 1e9 + rt.tv_nsec;
+ // Given now's timestamp calculate the time of the next timestamp.
+ timestamp += pdev->vsync_period_ns -
+ (timestamp - base_timestamp) % pdev->vsync_period_ns;
+
+ rt.tv_sec = timestamp / 1e9;
+ rt.tv_nsec = timestamp % static_cast<int32_t>(1e9);
+ int err = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &rt, NULL);
+ if ( err == -1) {
+ ALOGE("error in vsync thread: %s", strerror(errno));
+ if (errno == EINTR) {
+ continue;
+ }
+ }
+
+ pdev->procs->vsync(const_cast<hwc_procs_t*>(pdev->procs), 0, timestamp);
+ if (rt.tv_sec - last_logged > log_interval) {
+ ALOGI("Sent %d syncs in %ds", sent - last_sent, log_interval);
+ last_logged = rt.tv_sec;
+ last_sent = sent;
+ }
+ ++sent;
+ }
+
+ return NULL;
+}
+
+static int gce_hwc_blank(gce_hwc_device* dev, int disp, int /*blank*/) {
+ struct gce_hwc_composer_device_1_t* pdev =
+ (struct gce_hwc_composer_device_1_t*)dev;
+ if (!IS_PRIMARY_DISPLAY(disp)) return -EINVAL;
+ return 0;
+}
+
+static void gce_hwc_dump(gce_hwc_device* dev, char* buff, int buff_len) {
+ reinterpret_cast<gce_hwc_composer_device_1_t*>(dev)->composer->Dump(buff,
+ buff_len);
+}
+
+static int gce_hwc_get_display_configs(gce_hwc_device* dev, int disp,
+ uint32_t* configs, size_t* numConfigs) {
+ struct gce_hwc_composer_device_1_t* pdev =
+ (struct gce_hwc_composer_device_1_t*)dev;
+
+ if (*numConfigs == 0) return 0;
+
+ if (IS_PRIMARY_DISPLAY(disp)) {
+ configs[0] = 0;
+ *numConfigs = 1;
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+#if GCE_PLATFORM_SDK_AFTER(J)
+static int32_t gce_hwc_attribute(struct gce_hwc_composer_device_1_t* pdev,
+ const uint32_t attribute) {
+ const GceFrameBuffer& config = GceFrameBuffer::getInstance();
+ switch (attribute) {
+ case HWC_DISPLAY_VSYNC_PERIOD:
+ return pdev->vsync_period_ns;
+ case HWC_DISPLAY_WIDTH:
+ return config.x_res();
+ case HWC_DISPLAY_HEIGHT:
+ return config.y_res();
+ case HWC_DISPLAY_DPI_X:
+ ALOGI("Reporting DPI_X of %d", config.dpi());
+ return config.dpi() * 1000; // The number of pixels per thousand inches
+ case HWC_DISPLAY_DPI_Y:
+ ALOGI("Reporting DPI_Y of %d", config.dpi());
+ return config.dpi() * 1000; // The number of pixels per thousand inches
+ default:
+ ALOGE("unknown display attribute %u", attribute);
+ return -EINVAL;
+ }
+}
+
+static int gce_hwc_get_display_attributes(gce_hwc_device* dev, int disp,
+ uint32_t config __unused,
+ const uint32_t* attributes,
+ int32_t* values) {
+ struct gce_hwc_composer_device_1_t* pdev =
+ (struct gce_hwc_composer_device_1_t*)dev;
+
+ if (!IS_PRIMARY_DISPLAY(disp)) {
+ ALOGE("unknown display type %u", disp);
+ return -EINVAL;
+ }
+
+ for (int i = 0; attributes[i] != HWC_DISPLAY_NO_ATTRIBUTE; i++) {
+ values[i] = gce_hwc_attribute(pdev, attributes[i]);
+ }
+
+ return 0;
+}
+#endif
+
+static int gce_hwc_close(hw_device_t* device) {
+ struct gce_hwc_composer_device_1_t* dev =
+ (struct gce_hwc_composer_device_1_t*)device;
+ ALOGE("gce_hwc_close");
+ pthread_kill(dev->vsync_thread, SIGTERM);
+ pthread_join(dev->vsync_thread, NULL);
+ delete dev->composer;
+ delete dev;
+ return 0;
+}
+
+static int gce_hwc_open(const struct hw_module_t* module, const char* name,
+ struct hw_device_t** device) {
+ ALOGI("%s", __FUNCTION__);
+ if (strcmp(name, HWC_HARDWARE_COMPOSER)) {
+ ALOGE("%s called with bad name %s", __FUNCTION__, name);
+ return -EINVAL;
+ }
+
+ gce_hwc_composer_device_1_t* dev = new gce_hwc_composer_device_1_t();
+ if (!dev) {
+ ALOGE("%s failed to allocate dev", __FUNCTION__);
+ return -ENOMEM;
+ }
+
+ // TODO(ghartman): Read metadata here
+ int refreshRate = 60;
+ dev->vsync_period_ns = 1000000000 / refreshRate;
+ struct timespec rt;
+ if (clock_gettime(CLOCK_MONOTONIC, &rt) == -1) {
+ ALOGE("%s:%d error in vsync thread clock_gettime: %s", __FILE__, __LINE__,
+ strerror(errno));
+ }
+ dev->vsync_base_timestamp = int64_t(rt.tv_sec) * 1e9 + rt.tv_nsec;
+
+ dev->base.common.tag = HARDWARE_DEVICE_TAG;
+ dev->base.common.version = GCE_HWC_DEVICE_API_VERSION;
+ dev->base.common.module = const_cast<hw_module_t*>(module);
+ dev->base.common.close = gce_hwc_close;
+
+ dev->base.prepare = gce_hwc_prepare;
+ dev->base.set = gce_hwc_set;
+ dev->base.query = gce_hwc_query;
+ dev->base.registerProcs = gce_hwc_register_procs;
+ dev->base.dump = gce_hwc_dump;
+#if GCE_PLATFORM_SDK_BEFORE(J_MR1)
+ static hwc_methods_t hwc_methods = {gce_hwc_event_control};
+ dev->base.methods = &hwc_methods;
+#else
+ dev->base.blank = gce_hwc_blank;
+ dev->base.eventControl = gce_hwc_event_control;
+ dev->base.getDisplayConfigs = gce_hwc_get_display_configs;
+ dev->base.getDisplayAttributes = gce_hwc_get_display_attributes;
+#endif
+ dev->composer =
+ new ComposerType(dev->vsync_base_timestamp, dev->vsync_period_ns);
+
+ int ret = pthread_create(&dev->vsync_thread, NULL, hwc_vsync_thread, dev);
+ if (ret) {
+ ALOGE("failed to start vsync thread: %s", strerror(ret));
+ ret = -ret;
+ delete dev;
+ } else {
+ *device = &dev->base.common;
+ }
+
+ return ret;
+}
+
+static struct hw_module_methods_t gce_hwc_module_methods = {
+ gce_hwc_open,
+};
+
+hwc_module_t HAL_MODULE_INFO_SYM = {{HARDWARE_MODULE_TAG,
+ HWC_MODULE_API_VERSION_0_1,
+ HARDWARE_HAL_API_VERSION,
+ HWC_HARDWARE_MODULE_ID,
+ "GCE hwcomposer module",
+ "Google",
+ &gce_hwc_module_methods,
+ NULL,
+ {0}}};
diff --git a/guest/hals/hwcomposer/legacy/hwcomposer.mk b/guest/hals/hwcomposer/legacy/hwcomposer.mk
new file mode 100644
index 0000000..eb04e2b
--- /dev/null
+++ b/guest/hals/hwcomposer/legacy/hwcomposer.mk
@@ -0,0 +1,54 @@
+# Copyright (C) 2016 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.
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw
+LOCAL_MULTILIB := first
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SHARED_LIBRARIES := \
+ libgceframebuffer \
+ liblog \
+ libcutils \
+ libutils \
+ libsync \
+ libhardware \
+ libjpeg \
+ $(GCE_STLPORT_LIBS)
+
+LOCAL_STATIC_LIBRARIES := \
+ libgcemetadata \
+ libyuv_static
+
+LOCAL_SRC_FILES := \
+ geometry_utils.cpp \
+ hwcomposer.cpp \
+ gce_composer.cpp \
+ stats_keeper.cpp \
+ base_composer.cpp
+
+LOCAL_CFLAGS += \
+ -DLOG_TAG=\"hwcomposer\" \
+ -DGATHER_STATS \
+ $(GCE_VERSION_CFLAGS)
+
+LOCAL_C_INCLUDES := \
+ device/google/gce/hwcomposer \
+ device/google/gce/include \
+ external/libyuv/files/include \
+ bionic \
+ $(GCE_STLPORT_INCLUDES)
+
+include device/google/gce/libs/base/libbase.mk
+LOCAL_SHARED_LIBRARIES += $(GCE_LIBBASE_LIB_NAME)
+LOCAL_C_INCLUDES += $(GCE_LIBBASE_INCLUDE_DIR)
diff --git a/guest/hals/hwcomposer/legacy/hwcomposer_common.h b/guest/hals/hwcomposer/legacy/hwcomposer_common.h
new file mode 100644
index 0000000..857337f
--- /dev/null
+++ b/guest/hals/hwcomposer/legacy/hwcomposer_common.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#ifndef GCE_HWCOMPOSER_HWCOMPOSER_COMMON_H
+#define GCE_HWCOMPOSER_HWCOMPOSER_COMMON_H
+
+#include <api_level_fixes.h>
+
+#include <hardware/hwcomposer.h>
+#include <hardware/hwcomposer_defs.h>
+
+#if GCE_PLATFORM_SDK_BEFORE(J_MR1)
+typedef hwc_composer_device_t gce_hwc_device;
+typedef hwc_layer_t gce_hwc_layer;
+#define IS_TARGET_FRAMEBUFFER(x) false
+#define IS_PRIMARY_DISPLAY(x) true
+#define GCE_HWC_DEVICE_API_VERSION HWC_DEVICE_API_VERSION_0_3
+#else
+typedef hwc_composer_device_1_t gce_hwc_device;
+typedef hwc_layer_1_t gce_hwc_layer;
+#define IS_TARGET_FRAMEBUFFER(x) ((x) == HWC_FRAMEBUFFER_TARGET)
+#define IS_PRIMARY_DISPLAY(x) ((x) == HWC_DISPLAY_PRIMARY)
+#define GCE_HWC_DEVICE_API_VERSION HWC_DEVICE_API_VERSION_1_1
+#endif
+
+#endif
diff --git a/guest/hals/hwcomposer/legacy/stats_keeper.cpp b/guest/hals/hwcomposer/legacy/stats_keeper.cpp
new file mode 100644
index 0000000..9c9487f
--- /dev/null
+++ b/guest/hals/hwcomposer/legacy/stats_keeper.cpp
@@ -0,0 +1,227 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#include <cutils/log.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <algorithm>
+#include <utility>
+#include <vector>
+
+#include "geometry_utils.h"
+#include "stats_keeper.h"
+
+using avd::time::TimeDifference;
+using avd::time::Nanoseconds;
+using avd::time::Microseconds;
+using avd::time::Seconds;
+using avd::time::MonotonicTimePoint;
+using avd::Mutex;
+using avd::LockGuard;
+
+namespace avd {
+
+namespace {
+
+// These functions assume that there is at least one suitable element inside
+// the multiset.
+template <class T>
+void MultisetDeleteOne(std::multiset<T>* mset, const T& key) {
+ mset->erase(mset->find(key));
+}
+template <class T>
+const T& MultisetMin(const std::multiset<T>& mset) {
+ return *mset.begin();
+}
+template <class T>
+const T& MultisetMax(const std::multiset<T>& mset) {
+ return *mset.rbegin();
+}
+
+} // namespace
+
+StatsKeeper::StatsKeeper(TimeDifference timespan,
+ int64_t vsync_base,
+ int32_t vsync_period)
+ : period_length_(timespan, 1),
+ num_layers_(0),
+ num_hwcomposited_layers_(0),
+ num_prepare_calls_(0),
+ num_set_calls_(0),
+ prepare_call_total_time_(0),
+ set_call_total_time_(0),
+ total_layers_area(0),
+ total_invisible_area(0),
+ vsync_base_(vsync_base),
+ vsync_period_(vsync_period) {
+ last_composition_stats_.num_prepare_calls = 0;
+}
+
+StatsKeeper::~StatsKeeper() {}
+
+void StatsKeeper::RecordPrepareStart(int num_layers) {
+ last_composition_stats_.num_layers = num_layers;
+ last_composition_stats_.num_prepare_calls++;
+ num_prepare_calls_++;
+ last_composition_stats_.prepare_start = MonotonicTimePoint::Now();
+ // Calculate the (expected) time of last VSYNC event. We can only make a guess about it because
+ // the vsync thread could run late or surfaceflinger could run late and call prepare from a
+ // previous vsync cycle.
+ int64_t last_vsync =
+ Nanoseconds(last_composition_stats_.set_start.SinceEpoch()).count();
+ last_vsync -= (last_vsync - vsync_base_) % vsync_period_;
+ last_composition_stats_.last_vsync = MonotonicTimePoint() + Nanoseconds(last_vsync);
+}
+
+void StatsKeeper::RecordPrepareEnd(int num_hwcomposited_layers) {
+ last_composition_stats_.prepare_end = MonotonicTimePoint::Now();
+ last_composition_stats_.num_hwc_layers = num_hwcomposited_layers;
+}
+
+void StatsKeeper::RecordSetStart() {
+ last_composition_stats_.set_start = MonotonicTimePoint::Now();
+}
+
+void StatsKeeper::RecordSetEnd() {
+ last_composition_stats_.set_end = MonotonicTimePoint::Now();
+ LockGuard<Mutex> lock(mutex_);
+ num_set_calls_++;
+ while (!raw_composition_data_.empty() &&
+ period_length_ < last_composition_stats_.set_end -
+ raw_composition_data_.front().time_point()) {
+ const CompositionData& front = raw_composition_data_.front();
+
+ num_prepare_calls_ -= front.num_prepare_calls();
+ --num_set_calls_;
+ num_layers_ -= front.num_layers();
+ num_hwcomposited_layers_ -= front.num_hwcomposited_layers();
+ prepare_call_total_time_ =
+ Nanoseconds(prepare_call_total_time_ - front.prepare_time());
+ set_call_total_time_ =
+ Nanoseconds(set_call_total_time_ - front.set_calls_time());
+
+ MultisetDeleteOne(&prepare_calls_per_set_calls_, front.num_prepare_calls());
+ MultisetDeleteOne(&layers_per_compositions_, front.num_layers());
+ MultisetDeleteOne(&prepare_call_times_, front.prepare_time());
+ MultisetDeleteOne(&set_call_times_, front.set_calls_time());
+ if (front.num_hwcomposited_layers() != 0) {
+ MultisetDeleteOne(
+ &set_call_times_per_hwcomposited_layer_ns_,
+ front.set_calls_time().count() / front.num_hwcomposited_layers());
+ }
+
+ raw_composition_data_.pop_front();
+ }
+ Nanoseconds last_prepare_call_time_(last_composition_stats_.prepare_end -
+ last_composition_stats_.prepare_start);
+ Nanoseconds last_set_call_total_time_(last_composition_stats_.set_end -
+ last_composition_stats_.set_start);
+ raw_composition_data_.push_back(CompositionData(
+ last_composition_stats_.set_end, last_composition_stats_.num_prepare_calls,
+ last_composition_stats_.num_layers, last_composition_stats_.num_hwc_layers, last_prepare_call_time_,
+ last_set_call_total_time_));
+
+ const CompositionData& back = raw_composition_data_.back();
+
+ // There may be several calls to prepare before a call to set, but the only
+ // valid call is the last one, so we need to compute these here:
+ num_layers_ += last_composition_stats_.num_layers;
+ num_hwcomposited_layers_ += last_composition_stats_.num_hwc_layers;
+ prepare_call_total_time_ =
+ Nanoseconds(prepare_call_total_time_ + last_prepare_call_time_);
+ set_call_total_time_ =
+ Nanoseconds(set_call_total_time_ + last_set_call_total_time_);
+ prepare_calls_per_set_calls_.insert(last_composition_stats_.num_prepare_calls);
+ layers_per_compositions_.insert(last_composition_stats_.num_layers);
+ prepare_call_times_.insert(last_prepare_call_time_);
+ set_call_times_.insert(last_set_call_total_time_);
+ if (last_composition_stats_.num_hwc_layers != 0) {
+ set_call_times_per_hwcomposited_layer_ns_.insert(
+ last_set_call_total_time_.count() / last_composition_stats_.num_hwc_layers);
+ }
+
+ // Reset the counter
+ last_composition_stats_.num_prepare_calls = 0;
+}
+
+void StatsKeeper::SynchronizedDump(char* buffer,
+ int buffer_size) const {
+ LockGuard<Mutex> lock(mutex_);
+ int chars_written = 0;
+// Make sure there is enough space to write the next line
+#define bprintf(...) \
+ (chars_written += (chars_written < buffer_size) \
+ ? (snprintf(&buffer[chars_written], \
+ buffer_size - chars_written, __VA_ARGS__)) \
+ : 0)
+
+ bprintf(
+ "HWComposer stats from the %lld seconds just before the last call to "
+ "set() (which happended %lld seconds ago):\n",
+ Seconds(period_length_).count(),
+ Seconds(MonotonicTimePoint::Now() - last_composition_stats_.set_end).count());
+ bprintf(" Layer count: %d\n", num_layers_);
+
+ if (num_layers_ == 0 || num_prepare_calls_ == 0 || num_set_calls_ == 0) {
+ return;
+ }
+
+ bprintf(" Layers composited by hwcomposer: %d (%d%%)\n",
+ num_hwcomposited_layers_,
+ 100 * num_hwcomposited_layers_ / num_layers_);
+ bprintf(" Number of calls to prepare(): %d\n", num_prepare_calls_);
+ bprintf(" Number of calls to set(): %d\n", num_set_calls_);
+ if (num_set_calls_ > 0) {
+ bprintf(
+ " Maximum number of calls to prepare() before a single call to set(): "
+ "%d\n",
+ MultisetMax(prepare_calls_per_set_calls_));
+ }
+ bprintf(
+ " Time spent on prepare() (in microseconds):\n max: %lld\n "
+ "average: %lld\n min: %lld\n total: %lld\n",
+ Microseconds(MultisetMax(prepare_call_times_)).count(),
+ Microseconds(prepare_call_total_time_).count() / num_prepare_calls_,
+ Microseconds(MultisetMin(prepare_call_times_)).count(),
+ Microseconds(prepare_call_total_time_).count());
+ bprintf(
+ " Time spent on set() (in microseconds):\n max: %lld\n average: "
+ "%lld\n min: %lld\n total: %lld\n",
+ Microseconds(MultisetMax(set_call_times_)).count(),
+ Microseconds(set_call_total_time_).count() / num_set_calls_,
+ Microseconds(MultisetMin(set_call_times_)).count(),
+ Microseconds(set_call_total_time_).count());
+ if (num_hwcomposited_layers_ > 0) {
+ bprintf(
+ " Per layer compostition time:\n max: %lld\n average: %lld\n "
+ "min: %lld\n",
+ Microseconds(MultisetMax(set_call_times_per_hwcomposited_layer_ns_))
+ .count(),
+ Microseconds(set_call_total_time_).count() / num_hwcomposited_layers_,
+ Microseconds(MultisetMin(set_call_times_per_hwcomposited_layer_ns_))
+ .count());
+ }
+ bprintf("Statistics from last 100 compositions:\n");
+ bprintf(" Total area: %lld square pixels\n", total_layers_area);
+ if (total_layers_area != 0) {
+ bprintf(" Total invisible area: %lld square pixels, %lld%%\n",
+ total_invisible_area,
+ 100 * total_invisible_area / total_layers_area);
+ }
+#undef bprintf
+}
+
+} // namespace avd
diff --git a/guest/hals/hwcomposer/legacy/stats_keeper.h b/guest/hals/hwcomposer/legacy/stats_keeper.h
new file mode 100644
index 0000000..1a9dd06
--- /dev/null
+++ b/guest/hals/hwcomposer/legacy/stats_keeper.h
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#ifndef GCE_HWCOMPOSER_STATS_H_
+#define GCE_HWCOMPOSER_STATS_H_
+
+#include <GceFrameBufferControl.h>
+#include <MonotonicTime.h>
+#include <Pthread.h>
+#include <android-base/thread_annotations.h>
+#include <deque>
+#include <set>
+
+#include "hwcomposer_common.h"
+
+namespace avd {
+
+class CompositionData {
+ public:
+ CompositionData(avd::time::MonotonicTimePoint time_point,
+ int num_prepares, int num_layers, int num_hwcomposited_layers,
+ avd::time::Nanoseconds prepare_time,
+ avd::time::Nanoseconds set_calls_time)
+ : time_point_(time_point),
+ num_prepare_calls_(num_prepares),
+ num_layers_(num_layers),
+ num_hwcomposited_layers_(num_hwcomposited_layers),
+ prepare_time_(prepare_time),
+ set_calls_time_(set_calls_time) {}
+
+ avd::time::MonotonicTimePoint time_point() const {
+ return time_point_;
+ }
+
+ int num_prepare_calls() const { return num_prepare_calls_; }
+
+ int num_layers() const { return num_layers_; }
+
+ int num_hwcomposited_layers() const { return num_hwcomposited_layers_; }
+
+ avd::time::Nanoseconds prepare_time() const {
+ return prepare_time_;
+ }
+
+ avd::time::Nanoseconds set_calls_time() const {
+ return set_calls_time_;
+ }
+
+ private:
+ avd::time::MonotonicTimePoint time_point_;
+ int num_prepare_calls_;
+ int num_layers_;
+ int num_hwcomposited_layers_;
+ avd::time::Nanoseconds prepare_time_;
+ avd::time::Nanoseconds set_calls_time_;
+};
+
+class StatsKeeper {
+ public:
+ // The timespan parameter indicates for how long we keep stats about the past
+ // compositions.
+ StatsKeeper(avd::time::TimeDifference timespan,
+ int64_t vsync_base,
+ int32_t vsync_period);
+ StatsKeeper();
+ ~StatsKeeper();
+
+ // Record the time at which a call to prepare was made, takes the number of
+ // layers received (excluding the framebuffer) as a parameter.
+ void RecordPrepareStart(int num_layers);
+ // Record the time at which a call to prepare (was about to) returned, takes
+ // the number of layers marked for hardware composition as a parameter.
+ void RecordPrepareEnd(int num_hwcomposited_layers);
+ void RecordSetStart();
+ void RecordSetEnd() EXCLUDES(mutex_);
+
+ const CompositionStats& last_composition_stats() { return last_composition_stats_; }
+
+ // Calls to this function are synchronized with calls to 'RecordSetEnd' with a
+ // mutex. The other Record* functions do not need such synchronization because
+ // they access last_* variables only, which are not read by 'Dump'.
+ void SynchronizedDump(char* buffer, int buffer_size) const EXCLUDES(mutex_);
+
+ private:
+
+ avd::time::TimeDifference period_length_;
+
+ // Base and period of the VSYNC signal, allows to accurately calculate the
+ // time of the last vsync broadcast.
+ int64_t vsync_base_;
+ int32_t vsync_period_;
+ // Data collected about ongoing composition. These variables are not accessed
+ // from Dump(), so they don't need to be guarded by a mutex.
+ CompositionStats last_composition_stats_;
+
+ // Aggregated performance data collected from past compositions. These
+ // variables are modified when a composition is completed and when old
+ // compositions need to be discarded in RecordSetEnd(), and is accessed from
+ // Dump(). Non-aggregated data is kept in the raw_composition_data_ deque to
+ // be able to discard old values from the aggregated data.
+ int num_layers_ GUARDED_BY(mutex_);
+ int num_hwcomposited_layers_ GUARDED_BY(mutex_);
+ int num_prepare_calls_ GUARDED_BY(mutex_);
+ int num_set_calls_ GUARDED_BY(mutex_);
+ avd::time::Nanoseconds prepare_call_total_time_ GUARDED_BY(mutex_);
+ avd::time::Nanoseconds set_call_total_time_ GUARDED_BY(mutex_);
+ // These are kept in multisets to be able to calculate mins and maxs of
+ // changing sets of (not necessarily different) values.
+ std::multiset<int> prepare_calls_per_set_calls_ GUARDED_BY(mutex_);
+ std::multiset<int> layers_per_compositions_ GUARDED_BY(mutex_);
+ std::multiset<avd::time::Nanoseconds> prepare_call_times_
+ GUARDED_BY(mutex_);
+ std::multiset<avd::time::Nanoseconds> set_call_times_
+ GUARDED_BY(mutex_);
+ std::multiset<int64_t> set_call_times_per_hwcomposited_layer_ns_
+ GUARDED_BY(mutex_);
+
+ // Time-ordered list of compositions, used to update the global aggregated
+ // performance data when old compositions fall out of the period of interest.
+ std::deque<CompositionData> raw_composition_data_ GUARDED_BY(mutex_);
+
+ // TODO(jemoreira): Add min/max/average composition times per layer area units
+
+ std::deque<std::pair<int64_t, int64_t> > composition_areas GUARDED_BY(mutex_);
+ int64_t total_layers_area GUARDED_BY(mutex_);
+ int64_t total_invisible_area GUARDED_BY(mutex_);
+
+ // Controls access to data from past compositions.
+ mutable avd::Mutex mutex_;
+};
+
+template <class Composer>
+class StatsKeepingComposer {
+ public:
+ // Keep stats from the last 10 seconds.
+ StatsKeepingComposer(int64_t vsync_base_timestamp, int32_t vsync_period_ns)
+ : composer_(vsync_base_timestamp, vsync_period_ns),
+ stats_keeper_(avd::time::TimeDifference(avd::time::Seconds(10), 1),
+ vsync_base_timestamp,
+ vsync_period_ns) {
+ // Don't let the composer broadcast by itself, allow it to return to collect
+ // the timings and broadcast then.
+ composer_.ReplaceFbBroadcaster(NULL);
+ }
+ ~StatsKeepingComposer() {}
+
+ int PrepareLayers(size_t num_layers, gce_hwc_layer* layers) {
+ stats_keeper_.RecordPrepareStart(num_layers);
+ int num_hwc_layers = composer_.PrepareLayers(num_layers, layers);
+ stats_keeper_.RecordPrepareEnd(num_hwc_layers);
+ return num_hwc_layers;
+ }
+
+ int SetLayers(size_t num_layers, gce_hwc_layer* layers) {
+ stats_keeper_.RecordSetStart();
+ int yoffset = composer_.SetLayers(num_layers, layers);
+ stats_keeper_.RecordSetEnd();
+ if (yoffset >= 0) {
+ GceFrameBufferControl::getInstance().BroadcastFrameBufferChanged(
+ yoffset, &stats_keeper_.last_composition_stats());
+ } else {
+ ALOGE("%s: Error on SetLayers(), yoffset: %d", __FUNCTION__, yoffset);
+ }
+ return yoffset;
+ }
+
+ void Dump(char* buff, int buff_len) {
+ stats_keeper_.SynchronizedDump(buff, buff_len);
+ }
+
+ private:
+ StatsKeeper stats_keeper_;
+ Composer composer_;
+};
+
+} // namespace avd
+
+#endif
diff --git a/guest/libs/legacy_framebuffer/Android.mk b/guest/libs/legacy_framebuffer/Android.mk
new file mode 100644
index 0000000..b150b2b
--- /dev/null
+++ b/guest/libs/legacy_framebuffer/Android.mk
@@ -0,0 +1,92 @@
+# Copyright (C) 2016 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.
+
+gceframebuffer_common_src_files := \
+ GceFrameBuffer.cpp \
+ GceFrameBufferControl.cpp \
+ RegionRegistry.cpp
+
+gceframebuffer_common_c_flags := -Wall -Werror $(GCE_VERSION_CFLAGS)
+
+gceframebuffer_common_c_includes := \
+ device/google/gce/include
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libgceframebuffer
+LOCAL_VENDOR_MODULE := true
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := ${gceframebuffer_common_src_files}
+LOCAL_CFLAGS += ${gceframebuffer_common_c_flags}
+LOCAL_C_INCLUDES := ${gceframebuffer_common_c_includes} \
+ $(GCE_STLPORT_INCLUDES)
+
+LOCAL_STATIC_LIBRARIES := \
+ libgcemetadata \
+ libgcecutils \
+ libjsoncpp
+
+LOCAL_SHARED_LIBRARIES := \
+ liblog \
+ libutils \
+ libcutils \
+ $(GCE_STLPORT_LIBS)
+
+include $(BUILD_SHARED_LIBRARY)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libgceframebuffer
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := ${gceframebuffer_common_src_files}
+LOCAL_CFLAGS += ${gceframebuffer_common_c_flags}
+LOCAL_C_INCLUDES := ${gceframebuffer_common_c_includes}
+
+LOCAL_STATIC_LIBRARIES := \
+ libgcemetadata \
+ libjsoncpp \
+ liblog \
+ libutils \
+ libcutils \
+ $(GCE_STLPORT_LIBS)
+
+include $(BUILD_STATIC_LIBRARY)
+
+# This is needed only before M
+# By M STLport is completely replaces with libcxx
+ifneq (,$(filter $(PLATFORM_SDK_VERSION),16 17 18 19 21 22))
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := ${gceframebuffer_common_src_files}
+LOCAL_CFLAGS += ${gceframebuffer_common_c_flags}
+LOCAL_C_INCLUDES := ${gceframebuffer_common_c_includes}
+
+LOCAL_STATIC_LIBRARIES += \
+ libgcemetadata_cxx \
+ libjsoncpp_cxx \
+ libgcecutils_cxx \
+ libutils \
+ libcutils \
+ liblog
+
+LOCAL_MULTILIB := first
+LOCAL_MODULE := libgceframebuffer_cxx
+LOCAL_MODULE_TAGS := optional
+include external/libcxx/libcxx.mk
+include $(BUILD_STATIC_LIBRARY)
+endif
diff --git a/guest/libs/legacy_framebuffer/DisplayProperties.h b/guest/libs/legacy_framebuffer/DisplayProperties.h
new file mode 100644
index 0000000..a1d4efc
--- /dev/null
+++ b/guest/libs/legacy_framebuffer/DisplayProperties.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+// Google Compute Engine (GCE) Camera HAL - Initial metadata reader.
+#ifndef DEVICE_GOOGLE_GCE_HEADLESS_DISPLAY_PROPERTIES_H_
+#define DEVICE_GOOGLE_GCE_HEADLESS_DISPLAY_PROPERTIES_H_
+
+#include <AutoResources.h>
+
+namespace avd {
+
+class DisplayProperties {
+ public:
+ DisplayProperties() :
+ x_res_(1280),
+ y_res_(720),
+ bits_per_pixel_(32),
+ dpi_(160),
+ default_(true) {
+ config_.SetToString("1280x720x32x160");
+ }
+
+ void Parse(const char* value);
+
+ int GetXRes() const { return x_res_; }
+ int GetYRes() const { return y_res_; }
+ int GetBitsPerPixel() const { return bits_per_pixel_; }
+ int GetDpi() const { return dpi_; }
+ bool IsDefault() const { return default_; }
+ const char* GetConfig() const { return config_.data(); }
+
+ private:
+ // Screen width in pixels
+ int x_res_;
+ // Screen height in pixels
+ int y_res_;
+ // Depth of the screen (obsolete)
+ int bits_per_pixel_;
+ // Pixels per inch
+ int dpi_;
+ // Default
+ bool default_;
+ // Unparsed configuration
+ AutoFreeBuffer config_;
+};
+
+} // namespace avd
+#endif // DEVICE_GOOGLE_GCE_HEADLESS_DISPLAY_PROPERTIES_H_
diff --git a/guest/libs/legacy_framebuffer/GceFrameBuffer.cpp b/guest/libs/legacy_framebuffer/GceFrameBuffer.cpp
new file mode 100644
index 0000000..2005419
--- /dev/null
+++ b/guest/libs/legacy_framebuffer/GceFrameBuffer.cpp
@@ -0,0 +1,211 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+#include <api_level_fixes.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <utils/String8.h>
+
+#define LOG_TAG "GceFrameBuffer"
+#include <cutils/log.h>
+#include <system/graphics.h>
+#include "GceMetadataAttributes.h"
+#include "InitialMetadataReader.h"
+
+#include "GceFrameBuffer.h"
+
+
+const char* const GceFrameBuffer::kFrameBufferPath =
+ "/dev/userspace_framebuffer";
+
+
+const GceFrameBuffer & GceFrameBuffer::getInstance() {
+ static GceFrameBuffer instance;
+ instance.Configure();
+ return instance;
+}
+
+
+GceFrameBuffer::GceFrameBuffer()
+ : line_length_(-1) { }
+
+
+void GceFrameBuffer::Configure() {
+ const char* metadata_value =
+ avd::InitialMetadataReader::getInstance()->GetValueForKey(
+ GceMetadataAttributes::kDisplayConfigurationKey);
+ display_properties_.Parse(metadata_value);
+ line_length_ = align(
+ display_properties_.GetXRes() * (
+ display_properties_.GetBitsPerPixel() / 8));
+}
+
+
+bool GceFrameBuffer::OpenFrameBuffer(int* frame_buffer_fd) {
+ int fb_fd;
+ if ((fb_fd = open(GceFrameBuffer::kFrameBufferPath, O_RDWR)) < 0) {
+ SLOGE("Failed to open '%s' (%s)",
+ GceFrameBuffer::kFrameBufferPath, strerror(errno));
+ return false;
+ }
+
+ const GceFrameBuffer& config = GceFrameBuffer::getInstance();
+
+ if (ftruncate(fb_fd, config.total_buffer_size()) < 0) {
+ SLOGE("Failed to truncate framebuffer (%s)", strerror(errno));
+ return false;
+ }
+
+ *frame_buffer_fd = fb_fd;
+ return true;
+}
+
+
+bool GceFrameBuffer::OpenAndMapFrameBuffer(void** fb_memory,
+ int* frame_buffer_fd) {
+ int fb_fd;
+ if (!GceFrameBuffer::OpenFrameBuffer(&fb_fd)) { return false; }
+
+ size_t fb_size = GceFrameBuffer::getInstance().total_buffer_size();
+
+ void* mmap_res = mmap(0, fb_size, PROT_READ, MAP_SHARED, fb_fd, 0);
+ if (mmap_res == MAP_FAILED) {
+ SLOGE("Failed to mmap framebuffer (%s)", strerror(errno));
+ close(fb_fd);
+ return false;
+ }
+
+ // Modify the pointers only after mmap succeeds.
+ *fb_memory = mmap_res;
+ *frame_buffer_fd = fb_fd;
+
+ return true;
+}
+
+bool GceFrameBuffer::UnmapAndCloseFrameBuffer(void* fb_memory,
+ int frame_buffer_fd) {
+ size_t fb_size = GceFrameBuffer::getInstance().total_buffer_size();
+ return munmap(fb_memory, fb_size) == 0 && close(frame_buffer_fd) == 0;
+}
+
+
+int GceFrameBuffer::hal_format() const {
+ switch(display_properties_.GetBitsPerPixel()) {
+ case 32:
+ if (kRedShift) {
+ return HAL_PIXEL_FORMAT_BGRA_8888;
+ } else {
+ return HAL_PIXEL_FORMAT_RGBX_8888;
+ }
+ default:
+ return HAL_PIXEL_FORMAT_RGB_565;
+ }
+}
+
+const char* pixel_format_to_string(int format) {
+ switch (format) {
+ // Formats that are universal across versions
+ case HAL_PIXEL_FORMAT_RGBA_8888:
+ return "RGBA_8888";
+ case HAL_PIXEL_FORMAT_RGBX_8888:
+ return "RGBX_8888";
+ case HAL_PIXEL_FORMAT_BGRA_8888:
+ return "BGRA_8888";
+ case HAL_PIXEL_FORMAT_RGB_888:
+ return "RGB_888";
+ case HAL_PIXEL_FORMAT_RGB_565:
+ return "RGB_565";
+ case HAL_PIXEL_FORMAT_YV12:
+ return "YV12";
+ case HAL_PIXEL_FORMAT_YCrCb_420_SP:
+ return "YCrCb_420_SP";
+ case HAL_PIXEL_FORMAT_YCbCr_422_SP:
+ return "YCbCr_422_SP";
+ case HAL_PIXEL_FORMAT_YCbCr_422_I:
+ return "YCbCr_422_I";
+
+#if GCE_PLATFORM_SDK_AFTER(J)
+ // First supported on JBMR1 (API 17)
+ case HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED:
+ return "IMPLEMENTATION_DEFINED";
+ case HAL_PIXEL_FORMAT_BLOB:
+ return "BLOB";
+#endif
+#if GCE_PLATFORM_SDK_AFTER(J_MR1)
+ // First supported on JBMR2 (API 18)
+ case HAL_PIXEL_FORMAT_YCbCr_420_888:
+ return "YCbCr_420_888";
+ case HAL_PIXEL_FORMAT_Y8:
+ return "Y8";
+ case HAL_PIXEL_FORMAT_Y16:
+ return "Y16";
+#endif
+#if GCE_PLATFORM_SDK_AFTER(K)
+ // Support was added in L (API 21)
+ case HAL_PIXEL_FORMAT_RAW_OPAQUE:
+ return "RAW_OPAQUE";
+ // This is an alias for RAW_SENSOR in L and replaces it in M.
+ case HAL_PIXEL_FORMAT_RAW16:
+ return "RAW16";
+ case HAL_PIXEL_FORMAT_RAW10:
+ return "RAW10";
+#endif
+#if GCE_PLATFORM_SDK_AFTER(L_MR1)
+ case HAL_PIXEL_FORMAT_YCbCr_444_888:
+ return "YCbCr_444_888";
+ case HAL_PIXEL_FORMAT_YCbCr_422_888:
+ return "YCbCr_422_888";
+ case HAL_PIXEL_FORMAT_RAW12:
+ return "RAW12";
+ case HAL_PIXEL_FORMAT_FLEX_RGBA_8888:
+ return "FLEX_RGBA_8888";
+ case HAL_PIXEL_FORMAT_FLEX_RGB_888:
+ return "FLEX_RGB_888";
+#endif
+
+ // Formats that have been removed
+#if GCE_PLATFORM_SDK_BEFORE(K)
+ // Support was dropped on K (API 19)
+ case HAL_PIXEL_FORMAT_RGBA_5551:
+ return "RGBA_5551";
+ case HAL_PIXEL_FORMAT_RGBA_4444:
+ return "RGBA_4444";
+#endif
+#if GCE_PLATFORM_SDK_BEFORE(L)
+ // Renamed to RAW_16 in L. Both were present for L, but it was completely
+ // removed in M.
+ case HAL_PIXEL_FORMAT_RAW_SENSOR:
+ return "RAW_SENSOR";
+#endif
+#if GCE_PLATFORM_SDK_AFTER(J_MR2) && GCE_PLATFORM_SDK_BEFORE(M)
+ // Supported K, L, and LMR1. Not supported on JBMR0, JBMR1, JBMR2, and M
+ case HAL_PIXEL_FORMAT_sRGB_X_8888:
+ return "sRGB_X_8888";
+ case HAL_PIXEL_FORMAT_sRGB_A_8888:
+ return "sRGB_A_8888";
+#endif
+ }
+ return "UNKNOWN";
+}
diff --git a/guest/libs/legacy_framebuffer/GceFrameBuffer.h b/guest/libs/legacy_framebuffer/GceFrameBuffer.h
new file mode 100644
index 0000000..dea3dfa
--- /dev/null
+++ b/guest/libs/legacy_framebuffer/GceFrameBuffer.h
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+#ifndef GCE_FRAME_BUFFER_H_
+#define GCE_FRAME_BUFFER_H_
+
+#include <DisplayProperties.h>
+#include <UniquePtr.h>
+#include <pthread.h>
+#include <sys/mman.h>
+#include <climits>
+
+struct private_handle_t;
+struct remoter_request_packet;
+
+inline size_t roundUpToPageSize(size_t x) {
+ return (x + (PAGE_SIZE-1)) & ~(PAGE_SIZE-1);
+}
+
+class GceFrameBuffer {
+public:
+ static const GceFrameBuffer& getInstance();
+
+ static int align(int input, int alignment = kAlignment) {
+ return (input + alignment - 1) & -alignment;
+ }
+
+ int bits_per_pixel() const {
+ return display_properties_.GetBitsPerPixel();
+ }
+
+ size_t bufferSize() const {
+ return line_length_ * display_properties_.GetYRes();
+ }
+
+ int dpi() const {
+ return display_properties_.GetDpi();
+ }
+
+ int hal_format() const;
+
+ int line_length() const { return line_length_; }
+
+ int total_buffer_size() const {
+ return roundUpToPageSize(line_length_ * y_res_virtual() +
+ GceFrameBuffer::kSwiftShaderPadding);
+ }
+
+ int x_res() const {
+ return display_properties_.GetXRes();
+ }
+
+ int y_res() const {
+ return display_properties_.GetYRes();
+ }
+
+ int y_res_virtual() const {
+ return display_properties_.GetYRes() * kNumBuffers;
+ }
+
+ static const int kAlignment = 8;
+ static const int kNumHwcBuffers = 3;
+ // Without sync fences enabled surfaceflinger uses only 2 framebuffers,
+ // regardless of how many are available
+ static const int kNumSfBuffers = 3;
+ static const int kNumBuffers = kNumHwcBuffers + kNumSfBuffers;
+ static const char* const kFrameBufferPath;
+
+ static const int kRedShift = 0;
+ static const int kRedBits = 8;
+ static const int kGreenShift = 8;
+ static const int kGreenBits = 8;
+ static const int kBlueShift = 16;
+ static const int kBlueBits = 8;
+ static const int kAlphaShift = 24;
+ static const int kAlphaBits = 8;
+ typedef uint32_t Pixel;
+ static const int kSwiftShaderPadding = 4;
+
+ // Opens the framebuffer file. Ensures the file has the appropriate size by
+ // calling ftruncate.
+ static bool OpenFrameBuffer(int* frame_buffer_fd);
+
+ // Maps the framebuffer into memory. It's the caller's responsibility to
+ // unmap the memory and close the file when done.
+ static bool OpenAndMapFrameBuffer(void** fb_memory, int* frame_buffer_fd);
+ static bool UnmapAndCloseFrameBuffer(void* fb_memory, int frame_buffer_fd);
+
+private:
+ GceFrameBuffer();
+ void Configure();
+
+ avd::DisplayProperties display_properties_;
+ static const int kBitsPerPixel = sizeof(Pixel) * CHAR_BIT;
+ // Length of a scan-line in bytes.
+ int line_length_;
+ DISALLOW_COPY_AND_ASSIGN(GceFrameBuffer);
+};
+
+const char* pixel_format_to_string(int format);
+
+#endif
diff --git a/guest/libs/legacy_framebuffer/GceFrameBufferControl.cpp b/guest/libs/legacy_framebuffer/GceFrameBufferControl.cpp
new file mode 100644
index 0000000..c0fa84d
--- /dev/null
+++ b/guest/libs/legacy_framebuffer/GceFrameBufferControl.cpp
@@ -0,0 +1,294 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+#include <errno.h>
+#include <fcntl.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <unistd.h>
+
+#include <utils/String8.h>
+
+#define LOG_TAG "GceFrameBufferControl"
+#include <cutils/log.h>
+#include <system/graphics.h>
+
+#include "GceFrameBufferControl.h"
+#include "gralloc_gce_priv.h"
+
+enum { NOT_YET = 0, IN_PROGRESS, DONE };
+
+struct FrameBufferControl {
+ pthread_mutex_t mutex;
+ pthread_cond_t cond_var;
+ uint32_t seq_num;
+ volatile int yoffset;
+ volatile int initialized;
+ volatile uint32_t buffer_bits;
+ CompositionStats stats;
+};
+
+// __sync_lock_test_and_set is described to work on intel, but not on many other
+// targets.
+#define ATOMICALLY_SET(x, val) __sync_lock_test_and_set(&(x), (val))
+#define ATOMICALLY_COMPARE_AND_SWAP(x, old, val) \
+ __sync_val_compare_and_swap(&(x), (old), (val))
+// fetch the value, don't modify it (or with 0)
+#define ATOMICALLY_GET(x) __sync_or_and_fetch(&(x), 0)
+
+const char* const GceFrameBufferControl::kFrameBufferControlPath =
+ "/dev/framebuffer_control";
+
+GceFrameBufferControl& GceFrameBufferControl::getInstance() {
+ static GceFrameBufferControl instance;
+ // If not initialized before and fails to initialize now
+ if (!instance.Initialize()) {
+ LOG_ALWAYS_FATAL(
+ "Unable to initialize the framebuffer control structure (%s)... "
+ "aborting!",
+ strerror(errno));
+ }
+ return instance;
+}
+
+uint32_t GceFrameBufferControl::GetAndSetNextAvailableBufferBit(uint32_t filter) {
+ if (pthread_mutex_lock(&control_memory_->mutex)) {
+ ALOGE("Failed to acquire lock on framebuffer control mutex (%s) - %s",
+ strerror(errno), __FUNCTION__);
+ return 0;
+ }
+ uint32_t bit = control_memory_->buffer_bits;
+ bit &= filter;
+ if (bit == filter) {
+ // All bits in the filter are already set in the set
+ bit = 0LU;
+ } else {
+ // Set available bits to 1
+ bit = (bit^filter);
+ // isolate first available bit
+ bit &= ~bit + 1LU;
+ // set it on bit set on shared memory
+ control_memory_->buffer_bits |= bit;
+ }
+
+ pthread_mutex_unlock(&control_memory_->mutex);
+ return bit;
+}
+
+int GceFrameBufferControl::UnsetBufferBits(uint32_t bits) {
+ if (pthread_mutex_lock(&control_memory_->mutex)) {
+ ALOGE("Failed to acquire lock on framebuffer control mutex (%s) - %s",
+ strerror(errno), __FUNCTION__);
+ return -1;
+ }
+
+ control_memory_->buffer_bits &= ~bits;
+
+ pthread_mutex_unlock(&control_memory_->mutex);
+ return 0;
+}
+
+GceFrameBufferControl::GceFrameBufferControl()
+ : control_fd_(-1), control_memory_(NULL) {}
+
+namespace {
+
+bool MapFrameBufferControl(FrameBufferControl** control_memory_ptr,
+ int* fbc_fd) {
+ size_t control_size = sizeof(FrameBufferControl);
+ mode_t fb_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH;
+ int control_fd;
+ if ((control_fd = open(GceFrameBufferControl::kFrameBufferControlPath, O_RDWR,
+ fb_mode)) < 0) {
+ ALOGE("Failed to open framebuffer control at %s (%s)",
+ GceFrameBufferControl::kFrameBufferControlPath, strerror(errno));
+ return false;
+ }
+
+ if (ftruncate(control_fd, sizeof(FrameBufferControl)) < 0) {
+ ALOGE("Failed to truncate framebuffer control at %s (%s)",
+ GceFrameBufferControl::kFrameBufferControlPath, strerror(errno));
+ return false;
+ }
+
+ void* control_memory;
+ control_memory =
+ mmap(0, control_size, PROT_READ | PROT_WRITE, MAP_SHARED, control_fd, 0);
+ if (control_memory == MAP_FAILED) {
+ ALOGE("Failed to mmap framebuffer control (%s)", strerror(errno));
+ close(control_fd);
+ return false;
+ }
+
+ *control_memory_ptr = reinterpret_cast<FrameBufferControl*>(control_memory);
+ *fbc_fd = control_fd;
+
+ return true;
+}
+
+void UnmapFrameBufferControl(FrameBufferControl** control_memory_ptr,
+ int* fbc_fd) {
+ munmap(*control_memory_ptr, sizeof(FrameBufferControl));
+ *control_memory_ptr = NULL;
+ close(*fbc_fd);
+ *fbc_fd = -1;
+}
+}
+
+bool GceFrameBufferControl::Initialize() {
+ if (control_fd_ >= 0) {
+ return true;
+ }
+
+ if (!MapFrameBufferControl(&control_memory_, &control_fd_)) {
+ return false;
+ }
+
+ int initializing_state = ATOMICALLY_COMPARE_AND_SWAP(
+ control_memory_->initialized, NOT_YET, IN_PROGRESS);
+ switch (initializing_state) {
+ case DONE:
+ return true;
+
+ case IN_PROGRESS: { // wait 1 sec and try again
+ do {
+ sleep(1);
+ initializing_state = ATOMICALLY_GET(control_memory_->initialized);
+ if (initializing_state != DONE) {
+ ALOGW(
+ "Framebuffer control structure has not yet been initialized "
+ "after one second. Value of initialized flag: %d",
+ initializing_state);
+ }
+ } while (initializing_state != DONE);
+ return true;
+ }
+
+ case NOT_YET: { // flag set to IN_PROGRESS, proceed to initialize
+ pthread_mutexattr_t mutex_attr;
+ pthread_mutexattr_init(&mutex_attr);
+ pthread_mutexattr_setpshared(&mutex_attr, PTHREAD_PROCESS_SHARED);
+ int retval = pthread_mutex_init(&(control_memory_->mutex), &mutex_attr);
+ if (retval) {
+ ALOGE("Failed to acquire lock on framebuffer control mutex (%s) - %s",
+ strerror(errno), __FUNCTION__);
+ UnmapFrameBufferControl(&control_memory_, &control_fd_);
+ return false;
+ }
+ pthread_mutexattr_destroy(&mutex_attr);
+
+ pthread_condattr_t cond_attr;
+ pthread_condattr_init(&cond_attr);
+ pthread_condattr_setpshared(&cond_attr, PTHREAD_PROCESS_SHARED);
+ retval = pthread_cond_init(&(control_memory_->cond_var), &cond_attr);
+ if (retval) {
+ ALOGE("Failed to initialize cond var for framebuffer control (%s)",
+ strerror(errno));
+ pthread_mutex_destroy(&(control_memory_->mutex));
+ UnmapFrameBufferControl(&control_memory_, &control_fd_);
+ return false;
+ }
+ pthread_condattr_destroy(&cond_attr);
+
+ ATOMICALLY_SET(control_memory_->buffer_bits, 0LU);
+ ATOMICALLY_SET(control_memory_->seq_num, 0);
+ ATOMICALLY_SET(control_memory_->initialized, DONE);
+
+ return true;
+ }
+
+ default: { // unrecognized value
+ ALOGE("Framebuffer control memory is corrupt, initialized = %d",
+ initializing_state);
+ UnmapFrameBufferControl(&control_memory_, &control_fd_);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+int GceFrameBufferControl::GetCurrentYOffset() const {
+ if (!control_memory_) return -1;
+ return control_memory_->yoffset;
+}
+
+int GceFrameBufferControl::WaitForFrameBufferChangeSince(
+ uint32_t previous_fb_seq,
+ int* yoffset_p,
+ uint32_t* fb_seq_p,
+ CompositionStats* stats_p) {
+ if (pthread_mutex_lock(&control_memory_->mutex)) {
+ ALOGE("Failed to acquire lock on framebuffer control mutex (%s) - %s",
+ strerror(errno), __FUNCTION__);
+ return -1;
+ }
+ int retval = 0;
+
+ while (control_memory_->seq_num == previous_fb_seq) {
+ retval =
+ pthread_cond_wait(&control_memory_->cond_var, &control_memory_->mutex);
+ }
+
+ if (fb_seq_p) {
+ *fb_seq_p = control_memory_->seq_num;
+ }
+ if (yoffset_p) {
+ *yoffset_p = control_memory_->yoffset;
+ }
+ if (stats_p) {
+ *stats_p = control_memory_->stats;
+ }
+
+ pthread_mutex_unlock(&control_memory_->mutex);
+ return retval;
+}
+
+int GceFrameBufferControl::WaitForFrameBufferChange(int* yoffset_p) {
+ return WaitForFrameBufferChangeSince(
+ control_memory_->seq_num, yoffset_p, NULL, NULL);
+}
+
+int GceFrameBufferControl::BroadcastFrameBufferChanged(int yoffset) {
+ return BroadcastFrameBufferChanged(yoffset, NULL);
+}
+
+// increments the framebuffer sequential number, ensuring it's never zero
+static inline uint32_t seq_inc(uint32_t num) {
+ ++num;
+ return num? num: 1;
+}
+
+int GceFrameBufferControl::BroadcastFrameBufferChanged(
+ int yoffset, const CompositionStats* stats) {
+ if (pthread_mutex_lock(&control_memory_->mutex)) {
+ ALOGE("Failed to acquire lock on framebuffer control mutex (%s)",
+ strerror(errno));
+ return -1;
+ }
+ control_memory_->yoffset = yoffset;
+ control_memory_->seq_num = seq_inc(control_memory_->seq_num);
+ if (stats) { control_memory_->stats = *stats; }
+ pthread_cond_broadcast(&control_memory_->cond_var);
+ pthread_mutex_unlock(&control_memory_->mutex);
+
+ return 0;
+}
diff --git a/guest/libs/legacy_framebuffer/GceFrameBufferControl.h b/guest/libs/legacy_framebuffer/GceFrameBufferControl.h
new file mode 100644
index 0000000..0d6eff7
--- /dev/null
+++ b/guest/libs/legacy_framebuffer/GceFrameBufferControl.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+#ifndef GCE_FRAME_BUFFER_CONTROL_H_
+#define GCE_FRAME_BUFFER_CONTROL_H_
+
+#include <MonotonicTime.h>
+// For DISALLOW_COPY_AND_ASSIGN
+#include <UniquePtr.h>
+
+struct FrameBufferControl;
+
+struct CompositionStats {
+ avd::time::MonotonicTimePoint prepare_start;
+ avd::time::MonotonicTimePoint prepare_end;
+ avd::time::MonotonicTimePoint set_start;
+ avd::time::MonotonicTimePoint set_end;
+ avd::time::MonotonicTimePoint last_vsync;
+ // There may be more than one call to prepare, the timestamps are with regards to the last one (the one that precedes the set call)
+ int num_prepare_calls;
+ int num_layers;
+ // The number of layers composed by the hwcomposer
+ int num_hwc_layers;
+};
+
+class GceFrameBufferControl {
+ public:
+ static GceFrameBufferControl& getInstance();
+
+ static const char* const kFrameBufferControlPath;
+
+ // The framebuffer control structure mantains a bit set to keep track of the
+ // buffers that have been allocated already. This function atomically finds an
+ // unset (0) bit in the set, sets it to 1 and returns it. It will only
+ // consider bits already set in the filter parameter.
+ uint32_t GetAndSetNextAvailableBufferBit(uint32_t filter);
+ // Returns 0 on success
+ int UnsetBufferBits(uint32_t bits);
+
+ // Returns the yoffset of the last framebuffer update or a negative number on
+ // error.
+ int GetCurrentYOffset() const;
+ // Returns the value returned by the pthread_cond_wait, or -1 if the control
+ // structure has not been initialized by the hwcomposer yet.
+ int WaitForFrameBufferChange(int* yoffset_p);
+ // Uses a sequential number to determine whether the client was notified of
+ // the last framebuffer change and therefore needs to wait for a new one or if
+ // it can just return with the last one. It also provides the timings of the
+ // composition. Any NULL input parameters will be ignored. The sequential
+ // numbers are guaranteed to never be zero, so a value of zero can be used to
+ // get the last frame without waiting (useful when we want to get a frame for
+ // the first time).
+ int WaitForFrameBufferChangeSince(uint32_t previous_fb_seq,
+ int* yoffset_p,
+ uint32_t* fb_seq_p,
+ CompositionStats* stats_p);
+
+ // Returns 0 on success, a negative number on error.
+ int BroadcastFrameBufferChanged(int yoffset);
+
+ // Returns 0 on success, a negative number on error.
+ int BroadcastFrameBufferChanged(int yoffset, const CompositionStats* stats);
+
+ private:
+ GceFrameBufferControl();
+
+ // Map the control structure to memory and initialize its contents.
+ bool Initialize();
+
+ // FD for the frame buffer control.
+ int control_fd_;
+ // Pointer to the mapped frame buffer control.
+ FrameBufferControl* control_memory_;
+
+ DISALLOW_COPY_AND_ASSIGN(GceFrameBufferControl);
+};
+
+#endif
diff --git a/guest/libs/legacy_framebuffer/RegionRegistry.cpp b/guest/libs/legacy_framebuffer/RegionRegistry.cpp
new file mode 100644
index 0000000..4fea58b
--- /dev/null
+++ b/guest/libs/legacy_framebuffer/RegionRegistry.cpp
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+#include <limits.h>
+#include <errno.h>
+#include <pthread.h>
+#include <unistd.h>
+#include <string.h>
+
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <cutils/hashmap.h>
+#define LOG_TAG "GceGrallocRegionRegistry"
+#include <cutils/log.h>
+#include <cutils/atomic.h>
+
+#include <linux/ashmem.h>
+
+#include <hardware/hardware.h>
+#include <hardware/gralloc.h>
+#include <system/graphics.h>
+
+#include "gralloc_gce_priv.h"
+
+// TODO(ghartman): Make the configurable through a property
+static const bool g_log_refs = false;
+
+struct GrallocRegion {
+ void* base_;
+ int num_references_;
+
+ GrallocRegion() : base_(0), num_references_(0) { }
+ // Copy constructors are ok.
+};
+
+
+static const char* get_buffer_name(
+ const private_handle_t* hnd, char output[ASHMEM_NAME_LEN]) {
+ output[0] = '\0';
+ if (!hnd) {
+ ALOGE("Attempted to log gralloc name hnd=NULL");
+ return output;
+ }
+ if (hnd->fd == -1) {
+ ALOGE("Attempted to log gralloc name hnd=%p with fd == -1", hnd);
+ return output;
+ }
+ int rval = ioctl(hnd->fd, ASHMEM_GET_NAME, output);
+ if (rval == -1) {
+ output[0] = '\0';
+ }
+ return output;
+}
+
+
+static int str_hash(void* str) {
+ return hashmapHash(str, strlen(reinterpret_cast<const char*>(str)));
+}
+
+
+static bool str_equal(void* a, void* b) {
+ return strcmp(
+ reinterpret_cast<const char*>(a),
+ reinterpret_cast<const char*>(b)) == 0;
+}
+
+
+static Hashmap* get_regions() {
+ static Hashmap* regionMap = hashmapCreate(19, str_hash, str_equal);
+ return regionMap;
+}
+
+
+static GrallocRegion* lock_region_for_handle(
+ const private_handle_t* hnd, char region_name[ASHMEM_NAME_LEN]) {
+ region_name[0] = '\0';
+ get_buffer_name(hnd, region_name);
+ Hashmap* hash = get_regions();
+ hashmapLock(hash);
+ GrallocRegion* region = reinterpret_cast<GrallocRegion*>(
+ hashmapGet(hash, region_name));
+ if (!region) {
+ region = new GrallocRegion;
+ hashmapPut(hash, strdup(region_name), region);
+ }
+ return region;
+}
+
+
+/* The current implementation uses only a single lock for all regions.
+ * This method takes a region to simplfy the refactoring if we go to
+ * finer-grained locks.
+ */
+static inline void unlock_region(GrallocRegion* ) {
+ hashmapUnlock(get_regions());
+}
+
+
+void* reference_region(const char* op, const private_handle_t* hnd) {
+ char name_buf[ASHMEM_NAME_LEN];
+ GrallocRegion* region = lock_region_for_handle(hnd, name_buf);
+ if (!region->base_) {
+ void* mappedAddress = mmap(
+ 0, hnd->total_size, PROT_READ|PROT_WRITE, MAP_SHARED, hnd->fd, 0);
+ if (mappedAddress == MAP_FAILED) {
+ ALOGE("Could not mmap %s", strerror(errno));
+ unlock_region(region);
+ return NULL;
+ }
+ if (!(hnd->flags & private_handle_t::PRIV_FLAGS_FRAMEBUFFER)) {
+ // Set up the guard pages. The last page is always a guard
+ uintptr_t base = uintptr_t(mappedAddress);
+ uintptr_t addr = base + hnd->total_size - PAGE_SIZE;
+ if (mprotect((void*)addr, PAGE_SIZE, PROT_NONE) == -1) {
+ ALOGE("mprotect base=%p, pg=%p failed (%s)",
+ (void*)base, (void*)addr, strerror(errno));
+ }
+ }
+ region->base_ = mappedAddress;
+ ALOGI("Mapped %s hnd=%p fd=%d base=%p format=%s(0x%x) width=%d height=%d",
+ name_buf, hnd, hnd->fd, region->base_,
+ pixel_format_to_string(hnd->format), hnd->format,
+ hnd->x_res, hnd->y_res);
+ }
+
+ void* rval = region->base_;
+ ++region->num_references_;
+ ALOGI_IF(g_log_refs, "Referencing name=%s op=%s addr=%p new numRefs=%d",
+ name_buf, op, region->base_, region->num_references_);
+ unlock_region(region);
+ return rval;
+}
+
+
+int unreference_region(const char* op, const private_handle_t* hnd) {
+ char name_buf[ASHMEM_NAME_LEN];
+
+ GrallocRegion* region = lock_region_for_handle(hnd, name_buf);
+ if (!region->base_) {
+ ALOGE("Unmapping region with no map hnd=%p", hnd);
+ unlock_region(region);
+ return -1;
+ }
+ if (region->num_references_ < 1) {
+ ALOGE(
+ "unmap with hnd=%p, numReferences=%d", hnd, region->num_references_);
+ unlock_region(region);
+ return -1;
+ }
+ --region->num_references_;
+ if (!region->num_references_) {
+ ALOGI("Unmapped %s hnd=%p fd=%d base=%p", name_buf, hnd,
+ hnd->fd, region->base_);
+ if (munmap(region->base_, hnd->total_size) < 0) {
+ ALOGE("Could not unmap %s", strerror(errno));
+ }
+ region->base_ = 0;
+ }
+ ALOGI_IF(g_log_refs, "Unreferencing name=%s op=%s addr=%p new numRefs=%d",
+ name_buf, op, region->base_, region->num_references_);
+ unlock_region(region);
+ return 0;
+}
diff --git a/guest/libs/legacy_framebuffer/RegionRegistry.h b/guest/libs/legacy_framebuffer/RegionRegistry.h
new file mode 100644
index 0000000..1aded12
--- /dev/null
+++ b/guest/libs/legacy_framebuffer/RegionRegistry.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+#ifndef GCE_REGION_REGISTRY_H_
+#define GCE_REGION_REGISTRY_H_
+
+struct private_handle_t;
+
+/**
+ * Map the memory associated with hnd->fd or, if already mapped, increment
+ * its reference count.
+ */
+void* reference_region(
+ const char* op, const private_handle_t* hnd);
+
+/**
+ * Decrement the reference count associated with hnd->fd, unmapping its
+ * memory iff the reference count reaches 0.
+ */
+int unreference_region(const char* op, const private_handle_t* hnd);
+
+#endif // GCE_REGION_REGISTRY_H_
diff --git a/guest/libs/wpa_supplicant_8_lib/Android.mk b/guest/libs/wpa_supplicant_8_lib/Android.mk
new file mode 100644
index 0000000..7776080
--- /dev/null
+++ b/guest/libs/wpa_supplicant_8_lib/Android.mk
@@ -0,0 +1,79 @@
+# Copyright (C) 2016 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.
+
+LOCAL_PATH := $(call my-dir)
+
+ifeq ($(WPA_SUPPLICANT_VERSION),VER_0_8_X)
+
+ifneq ($(BOARD_WPA_SUPPLICANT_DRIVER),)
+ CONFIG_DRIVER_$(BOARD_WPA_SUPPLICANT_DRIVER) := y
+endif
+
+# Use a custom libnl on releases before N
+ifeq (0, $(shell test $(PLATFORM_SDK_VERSION) -lt 24; echo $$?))
+EXTERNAL_GCE_LIBNL_INCLUDE := external/gce/libnl/include
+else
+EXTERNAL_GCE_LIBNL_INCLUDE :=
+endif
+
+
+WPA_SUPPL_DIR = external/wpa_supplicant_8
+WPA_SRC_FILE :=
+
+include $(WPA_SUPPL_DIR)/wpa_supplicant/android.config
+
+WPA_SUPPL_DIR_INCLUDE = $(WPA_SUPPL_DIR)/src \
+ $(WPA_SUPPL_DIR)/src/common \
+ $(WPA_SUPPL_DIR)/src/drivers \
+ $(WPA_SUPPL_DIR)/src/l2_packet \
+ $(WPA_SUPPL_DIR)/src/utils \
+ $(WPA_SUPPL_DIR)/src/wps \
+ $(WPA_SUPPL_DIR)/wpa_supplicant \
+ $(EXTERNAL_GCE_LIBNL_INCLUDE)
+
+WPA_SUPPL_DIR_INCLUDE += external/libnl/include
+
+ifdef CONFIG_DRIVER_NL80211
+WPA_SRC_FILE += driver_cmd_nl80211.c
+endif
+
+ifeq ($(TARGET_ARCH),arm)
+# To force sizeof(enum) = 4
+L_CFLAGS += -mabi=aapcs-linux
+endif
+
+ifdef CONFIG_ANDROID_LOG
+L_CFLAGS += -DCONFIG_ANDROID_LOG
+endif
+
+########################
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := lib_driver_cmd_simulated
+LOCAL_SHARED_LIBRARIES := libc libcutils
+
+LOCAL_CFLAGS := $(L_CFLAGS) \
+ $(GCE_VERSION_CFLAGS)
+
+LOCAL_SRC_FILES := $(WPA_SRC_FILE)
+
+LOCAL_C_INCLUDES := \
+ device/google/gce/include \
+ $(WPA_SUPPL_DIR_INCLUDE)\
+
+include $(BUILD_STATIC_LIBRARY)
+
+########################
+
+endif
diff --git a/guest/libs/wpa_supplicant_8_lib/driver_cmd_nl80211.c b/guest/libs/wpa_supplicant_8_lib/driver_cmd_nl80211.c
new file mode 100644
index 0000000..223e7f8
--- /dev/null
+++ b/guest/libs/wpa_supplicant_8_lib/driver_cmd_nl80211.c
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+/*
+ * Driver interaction with extended Linux CFG8021
+ */
+
+#include "driver_cmd_nl80211.h"
+
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#if GCE_PLATFORM_SDK_AFTER(L_MR1)
+// Android M exposes headers more directly.
+#include <netinet/in.h>
+#include <linux/if.h>
+#include "driver_nl80211.h"
+#elif GCE_PLATFORM_SDK_AFTER(J_MR2)
+// Android versions K and L put structures in hardware_legacy
+#include "hardware_legacy/driver_nl80211.h"
+#else
+// Android version J does not expose structures directly. These structures are
+// manually defined later.
+#include <netinet/in.h>
+#include <linux/if.h>
+#endif
+
+#include "common.h"
+#include "wpa_supplicant_i.h"
+#include "config.h"
+#include "android_drv.h"
+
+
+int wpa_driver_nl80211_driver_cmd(
+ void* priv, char* cmd, char* buf, size_t buf_len) {
+ struct i802_bss* bss = priv;
+ struct wpa_driver_nl80211_data* drv = bss->drv;
+ struct ifreq ifr;
+ android_wifi_priv_cmd priv_cmd;
+ int ret = 0;
+
+ D("%s: called", __FUNCTION__);
+ if (os_strcasecmp(cmd, "STOP") == 0) {
+ linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 0);
+ wpa_msg(drv->ctx, MSG_INFO, WPA_EVENT_DRIVER_STATE "STOPPED");
+ } else if (os_strcasecmp(cmd, "START") == 0) {
+ linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 1);
+ wpa_msg(drv->ctx, MSG_INFO, WPA_EVENT_DRIVER_STATE "STARTED");
+ } else if (os_strcasecmp(cmd, "MACADDR") == 0) {
+ u8 macaddr[ETH_ALEN] = {};
+
+ ret = linux_get_ifhwaddr(drv->global->ioctl_sock, bss->ifname, macaddr);
+ if (!ret)
+ ret = os_snprintf(
+ buf, buf_len, "Macaddr = " MACSTR "\n", MAC2STR(macaddr));
+ } else if (os_strcasecmp(cmd, "RELOAD") == 0) {
+ wpa_msg(drv->ctx, MSG_INFO, WPA_EVENT_DRIVER_STATE "HANGED");
+ } else { // Use private command
+ return 0;
+ }
+ return ret;
+}
+
+
+int wpa_driver_set_p2p_noa(void* priv, u8 count, int start, int duration) {
+ D("%s: called", __FUNCTION__);
+ return 0;
+}
+
+
+int wpa_driver_get_p2p_noa(void* priv, u8* buf, size_t len) {
+ D("%s: called", __FUNCTION__);
+ return 0;
+}
+
+
+int wpa_driver_set_p2p_ps(void* priv, int legacy_ps, int opp_ps, int ctwindow) {
+ D("%s: called", __FUNCTION__);
+ return -1;
+}
+
+
+int wpa_driver_set_ap_wps_p2p_ie(
+ void* priv, const struct wpabuf* beacon,
+ const struct wpabuf* proberesp, const struct wpabuf* assocresp) {
+ D("%s: called", __FUNCTION__);
+ return 0;
+}
diff --git a/guest/libs/wpa_supplicant_8_lib/driver_cmd_nl80211.h b/guest/libs/wpa_supplicant_8_lib/driver_cmd_nl80211.h
new file mode 100644
index 0000000..dfd982f
--- /dev/null
+++ b/guest/libs/wpa_supplicant_8_lib/driver_cmd_nl80211.h
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+#ifndef DEVICE_GOOGLE_GCE_WPA_SUPPLICANT_8_H_
+#define DEVICE_GOOGLE_GCE_WPA_SUPPLICANT_8_H_
+
+#include <api_level_fixes.h>
+
+#include <memory.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "common.h"
+#include "linux_ioctl.h"
+#include "wpa_supplicant_i.h"
+
+#define LOG_TAG "GceWpaSupplicant8Driver"
+
+#include "cutils/log.h"
+
+#define GCE_WPA_SUPPLICANT_DEBUG 0
+
+#if GCE_WPA_SUPPLICANT_DEBUG
+# define D(...) ALOGD(__VA_ARGS__)
+#else
+# define D(...) ((void)0)
+#endif
+
+
+typedef struct android_wifi_priv_cmd {
+ char* buf;
+ int used_len;
+ int total_len;
+} android_wifi_priv_cmd;
+
+#if GCE_PLATFORM_SDK_BEFORE(K)
+
+#include "driver.h"
+
+struct i802_bss {
+ struct wpa_driver_nl80211_data* drv;
+ struct i802_bss* next;
+ int ifindex;
+ char ifname[IFNAMSIZ + 1];
+ char brname[IFNAMSIZ];
+
+ unsigned int beacon_set:1;
+ unsigned int added_if_into_bridge:1;
+ unsigned int added_bridge:1;
+ unsigned int in_deinit:1;
+
+ u8 addr[ETH_ALEN];
+
+ int freq;
+
+ void* ctx;
+ struct nl_handle* nl_preq;
+ struct nl_handle* nl_mgmt;
+ struct nl_cb* nl_cb;
+
+ struct nl80211_wiphy_data *wiphy_data;
+ struct dl_list wiphy_list;
+};
+
+struct nl80211_global {
+ struct dl_list interfaces;
+ int if_add_ifindex;
+ struct netlink_data *netlink;
+ struct nl_cb* nl_cb;
+ struct nl_handle* nl;
+ int nl80211_id;
+ int ioctl_sock; // socket for ioctl() use
+
+ struct nl_handle* nl_event;
+};
+
+struct wpa_driver_nl80211_data {
+ struct nl80211_global* global;
+ struct dl_list list;
+ struct dl_list wiphy_list;
+ char phyname[32];
+ void* ctx;
+ int ifindex;
+ int if_removed;
+ int if_disabled;
+ int ignore_if_down_event;
+ struct rfkill_data* rfkill;
+ struct wpa_driver_capa capa;
+ u8* extended_capa;
+ u8* extended_capa_mask;
+ unsigned int extended_capa_len;
+ int has_capability;
+ // More internal data follows.
+};
+
+#endif // GCE_PLATFORM_SDK_AFTER(J)
+
+#endif // DEVICE_GOOGLE_GCE_WPA_SUPPLICANT_8_H_