1/*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "host/frontend/vnc_server/blackboard.h"
18
19#include <algorithm>
20#include <utility>
21
22#include <gflags/gflags.h>
23#include <glog/logging.h>
24#include "host/frontend/vnc_server/frame_buffer_watcher.h"
25
26DEFINE_bool(debug_blackboard, false,
27            "Turn on detailed logging for the blackboard");
28
29#define DLOG(LEVEL)                                 \
30  if (FLAGS_debug_blackboard) LOG(LEVEL)
31
32using cvd::vnc::BlackBoard;
33using cvd::vnc::Stripe;
34
35cvd::vnc::SeqNumberVec cvd::vnc::MakeSeqNumberVec() {
36  return SeqNumberVec(FrameBufferWatcher::StripesPerFrame());
37}
38
39void BlackBoard::NewStripeReady(int index, StripeSeqNumber seq_num) {
40  std::lock_guard<std::mutex> guard(m_);
41  DLOG(INFO) << "new stripe arrived from frame watcher";
42  auto& current_seq_num = most_recent_stripe_seq_nums_[index];
43  current_seq_num = std::max(current_seq_num, seq_num);
44  for (auto& client : clients_) {
45    if (client.second.ready_to_receive) {
46      client.second.new_frame_cv.notify_one();
47    }
48  }
49}
50
51void BlackBoard::Register(const VncClientConnection* conn) {
52  {
53    std::lock_guard<std::mutex> guard(m_);
54    CHECK(!clients_.count(conn));
55    clients_[conn];  // constructs new state in place
56  }
57  new_client_cv_.notify_one();
58}
59
60void BlackBoard::Unregister(const VncClientConnection* conn) {
61  std::lock_guard<std::mutex> guard(m_);
62  CHECK(clients_.count(conn));
63  clients_.erase(clients_.find(conn));
64}
65
66bool BlackBoard::NoNewStripesFor(const SeqNumberVec& seq_nums) const {
67  CHECK(seq_nums.size() == most_recent_stripe_seq_nums_.size());
68  for (auto state_seq_num = seq_nums.begin(),
69            held_seq_num = most_recent_stripe_seq_nums_.begin();
70       state_seq_num != seq_nums.end(); ++state_seq_num, ++held_seq_num) {
71    if (*state_seq_num < *held_seq_num) {
72      return false;
73    }
74  }
75  return true;
76}
77
78cvd::vnc::StripePtrVec BlackBoard::WaitForSenderWork(
79    const VncClientConnection* conn) {
80  std::unique_lock<std::mutex> guard(m_);
81  auto& state = GetStateForClient(conn);
82  DLOG(INFO) << "Waiting for stripe...";
83  while (!state.closed &&
84         (!state.ready_to_receive || NoNewStripesFor(state.stripe_seq_nums))) {
85    state.new_frame_cv.wait(guard);
86  }
87  DLOG(INFO) << "At least one new stripe is available, should unblock " << conn;
88  state.ready_to_receive = false;
89  auto new_stripes = frame_buffer_watcher_->StripesNewerThan(
90      state.orientation, state.stripe_seq_nums);
91  for (auto& s : new_stripes) {
92    state.stripe_seq_nums[s->index] = s->seq_number;
93  }
94  return new_stripes;
95}
96
97void BlackBoard::WaitForAtLeastOneClientConnection() {
98  std::unique_lock<std::mutex> guard(m_);
99  while (clients_.empty()) {
100    new_client_cv_.wait(guard);
101  }
102}
103
104void BlackBoard::SetOrientation(const VncClientConnection* conn,
105                                ScreenOrientation orientation) {
106  std::lock_guard<std::mutex> guard(m_);
107  auto& state = GetStateForClient(conn);
108  state.orientation = orientation;
109  // After an orientation change the vnc client will need all stripes from
110  // the new orientation, regardless of age.
111  ResetToZero(&state.stripe_seq_nums);
112}
113
114void BlackBoard::SignalClientNeedsEntireScreen(
115    const VncClientConnection* conn) {
116  std::lock_guard<std::mutex> guard(m_);
117  ResetToZero(&GetStateForClient(conn).stripe_seq_nums);
118}
119
120void BlackBoard::ResetToZero(SeqNumberVec* seq_nums) {
121  seq_nums->assign(FrameBufferWatcher::StripesPerFrame(), StripeSeqNumber{});
122}
123
124void BlackBoard::FrameBufferUpdateRequestReceived(
125    const VncClientConnection* conn) {
126  std::lock_guard<std::mutex> guard(m_);
127  DLOG(INFO) << "Received frame buffer update request";
128  auto& state = GetStateForClient(conn);
129  state.ready_to_receive = true;
130  state.new_frame_cv.notify_one();
131}
132
133void BlackBoard::StopWaiting(const VncClientConnection* conn) {
134  std::lock_guard<std::mutex> guard(m_);
135  auto& state = GetStateForClient(conn);
136  state.closed = true;
137  // Wake up the thread that might be in WaitForSenderWork()
138  state.new_frame_cv.notify_one();
139}
140
141void BlackBoard::set_frame_buffer_watcher(
142    cvd::vnc::FrameBufferWatcher* frame_buffer_watcher) {
143  std::lock_guard<std::mutex> guard(m_);
144  frame_buffer_watcher_ = frame_buffer_watcher;
145}
146
147void BlackBoard::set_jpeg_quality_level(int quality_level) {
148  // NOTE all vnc clients share a common jpeg quality level because the
149  // server doesn't compress per-client. The quality level for all clients
150  // will be whatever the most recent set was by any client.
151  std::lock_guard<std::mutex> guard(m_);
152  if (quality_level < kJpegMinQualityEncoding ||
153      quality_level > kJpegMaxQualityEncoding) {
154    LOG(WARNING) << "Bogus jpeg quality level: " << quality_level
155                 << ". Quality must be in range [" << kJpegMinQualityEncoding
156                 << ", " << kJpegMaxQualityEncoding << "]";
157    return;
158  }
159  jpeg_quality_level_ = 55 + (5 * (quality_level + 32));
160  DLOG(INFO) << "jpeg quality level set to " << jpeg_quality_level_ << "%";
161}
162
163BlackBoard::ClientFBUState& BlackBoard::GetStateForClient(
164    const VncClientConnection* conn) {
165  CHECK(clients_.count(conn));
166  return clients_[conn];
167}
168