Home | History | Annotate | Download | only in vnc_server
      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 
     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 cvd::vnc::BlackBoard;
     33 using cvd::vnc::Stripe;
     34 
     35 cvd::vnc::SeqNumberVec cvd::vnc::MakeSeqNumberVec() {
     36   return SeqNumberVec(FrameBufferWatcher::StripesPerFrame());
     37 }
     38 
     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 
     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 
     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 
     66 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 
     78 cvd::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 
     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 
    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 
    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 
    120 void BlackBoard::ResetToZero(SeqNumberVec* seq_nums) {
    121   seq_nums->assign(FrameBufferWatcher::StripesPerFrame(), StripeSeqNumber{});
    122 }
    123 
    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 
    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 
    141 void 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 
    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 
    163 BlackBoard::ClientFBUState& BlackBoard::GetStateForClient(
    164     const VncClientConnection* conn) {
    165   CHECK(clients_.count(conn));
    166   return clients_[conn];
    167 }
    168