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 <android-base/logging.h>
24 #include "host/frontend/vnc_server/frame_buffer_watcher.h"
25 
26 DEFINE_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 
32 using cuttlefish::vnc::BlackBoard;
33 using cuttlefish::vnc::Stripe;
34 
MakeSeqNumberVec()35 cuttlefish::vnc::SeqNumberVec cuttlefish::vnc::MakeSeqNumberVec() {
36   return SeqNumberVec(FrameBufferWatcher::StripesPerFrame());
37 }
38 
NewStripeReady(int index,StripeSeqNumber seq_num)39 void 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 
Register(const VncClientConnection * conn)51 void 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 
Unregister(const VncClientConnection * conn)60 void 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 
NoNewStripesFor(const SeqNumberVec & seq_nums) const66 bool 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 
WaitForSenderWork(const VncClientConnection * conn)78 cuttlefish::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 
WaitForAtLeastOneClientConnection()97 void BlackBoard::WaitForAtLeastOneClientConnection() {
98   std::unique_lock<std::mutex> guard(m_);
99   while (clients_.empty()) {
100     new_client_cv_.wait(guard);
101   }
102 }
103 
SetOrientation(const VncClientConnection * conn,ScreenOrientation orientation)104 void 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 
SignalClientNeedsEntireScreen(const VncClientConnection * conn)114 void BlackBoard::SignalClientNeedsEntireScreen(
115     const VncClientConnection* conn) {
116   std::lock_guard<std::mutex> guard(m_);
117   ResetToZero(&GetStateForClient(conn).stripe_seq_nums);
118 }
119 
ResetToZero(SeqNumberVec * seq_nums)120 void BlackBoard::ResetToZero(SeqNumberVec* seq_nums) {
121   seq_nums->assign(FrameBufferWatcher::StripesPerFrame(), StripeSeqNumber{});
122 }
123 
FrameBufferUpdateRequestReceived(const VncClientConnection * conn)124 void 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 
StopWaiting(const VncClientConnection * conn)133 void 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 
set_frame_buffer_watcher(cuttlefish::vnc::FrameBufferWatcher * frame_buffer_watcher)141 void BlackBoard::set_frame_buffer_watcher(
142     cuttlefish::vnc::FrameBufferWatcher* frame_buffer_watcher) {
143   std::lock_guard<std::mutex> guard(m_);
144   frame_buffer_watcher_ = frame_buffer_watcher;
145 }
146 
set_jpeg_quality_level(int quality_level)147 void 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 
GetStateForClient(const VncClientConnection * conn)163 BlackBoard::ClientFBUState& BlackBoard::GetStateForClient(
164     const VncClientConnection* conn) {
165   CHECK(clients_.count(conn));
166   return clients_[conn];
167 }
168