• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 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/libs/confui/host_renderer.h"
18 
19 #include "host/libs/config/cuttlefish_config.h"
20 
21 namespace cuttlefish {
22 namespace confui {
alfaCombineChannel(std::uint32_t shift,double alfa,teeui::Color a,teeui::Color b)23 static teeui::Color alfaCombineChannel(std::uint32_t shift, double alfa,
24                                        teeui::Color a, teeui::Color b) {
25   a >>= shift;
26   a &= 0xff;
27   b >>= shift;
28   b &= 0xff;
29   double acc = alfa * a + (1 - alfa) * b;
30   if (acc <= 0) return 0;
31   std::uint32_t result = acc;
32   if (result > 255) return 255 << shift;
33   return result << shift;
34 }
35 
36 /**
37  * create a raw frame for confirmation UI dialog
38  *
39  * Many rendering code borrowed from the following source
40  *  https://android.googlesource.com/trusty/app/confirmationui/+/0429cc7/src
41  */
42 class ConfUiRendererImpl {
43   friend class ConfUiRenderer;
44 
45  public:
46   using LabelConfMsg = teeui::LabelBody;
47 
48  private:
49   static Result<std::unique_ptr<ConfUiRendererImpl>> GenerateRenderer(
50       const std::uint32_t display, const std::string& confirmation_msg,
51       const std::string& locale, const bool inverted, const bool magnified);
52 
53   /**
54    * this does not repaint from the scratch all the time
55    *
56    * It does repaint its frame buffer only when w/h of
57    * current display has changed
58    */
59   std::unique_ptr<TeeUiFrameWrapper>& RenderRawFrame();
60 
IsFrameReady() const61   bool IsFrameReady() const { return raw_frame_ && !raw_frame_->IsEmpty(); }
62 
IsInConfirm(const std::uint32_t x,const std::uint32_t y)63   bool IsInConfirm(const std::uint32_t x, const std::uint32_t y) {
64     return IsInside<teeui::LabelOK>(x, y);
65   }
IsInCancel(const std::uint32_t x,const std::uint32_t y)66   bool IsInCancel(const std::uint32_t x, const std::uint32_t y) {
67     return IsInside<teeui::LabelCancel>(x, y);
68   }
69 
IsSetUpSuccessful() const70   bool IsSetUpSuccessful() const { return is_setup_well_; }
71   ConfUiRendererImpl(const std::uint32_t display,
72                      const std::string& confirmation_msg,
73                      const std::string& locale, const bool inverted,
74                      const bool magnified);
75 
76   struct Boundary {            // inclusive but.. LayoutElement's size is float
77     std::uint32_t x, y, w, h;  // (x, y) is the top left
78   };
79 
80   template <typename LayoutElement>
GetBoundary(LayoutElement && e) const81   Boundary GetBoundary(LayoutElement&& e) const {
82     auto box = e.bounds_;
83     Boundary b;
84     // (x,y) is left top. so floor() makes sense
85     // w, h are witdh and height in float. perhaps ceiling makes more
86     // sense
87     b.x = static_cast<std::uint32_t>(box.x().floor().count());
88     b.y = static_cast<std::uint32_t>(box.y().floor().count());
89     b.w = static_cast<std::uint32_t>(box.w().ceil().count());
90     b.h = static_cast<std::uint32_t>(box.h().ceil().count());
91     return b;
92   }
93 
94   template <typename Element>
IsInside(const std::uint32_t x,const std::uint32_t y) const95   bool IsInside(const std::uint32_t x, const std::uint32_t y) const {
96     auto box = GetBoundary(std::get<Element>(layout_));
97     if (x >= box.x && x <= box.x + box.w && y >= box.y && y <= box.y + box.h) {
98       return true;
99     }
100     return false;
101   }
102   // essentially, to repaint from the scratch, so returns new frame
103   // when successful. Or, nullopt
104   std::unique_ptr<TeeUiFrameWrapper> RepaintRawFrame(const int w, const int h);
105 
106   bool InitLayout(const std::string& lang_id);
107   teeui::Error UpdateTranslations();
108   teeui::Error UpdateLocale();
109   void SetDeviceContext(const unsigned long long w, const unsigned long long h,
110                         bool is_inverted, bool is_magnified);
111 
112   // a callback function to be effectively sent to TeeUI library
113   teeui::Error UpdatePixels(TeeUiFrameWrapper& buffer, std::uint32_t x,
114                             std::uint32_t y, teeui::Color color);
115 
116   // second param is for type deduction
117   template <typename... Elements>
drawElements(std::tuple<Elements...> & layout,const teeui::PixelDrawer & drawPixel)118   static teeui::Error drawElements(std::tuple<Elements...>& layout,
119                                    const teeui::PixelDrawer& drawPixel) {
120     // Error::operator|| is overloaded, so we don't get short circuit
121     // evaluation. But we get the first error that occurs. We will still try and
122     // draw the remaining elements in the order they appear in the layout tuple.
123     return (std::get<Elements>(layout).draw(drawPixel) || ...);
124   }
125   void UpdateColorScheme(const bool is_inverted);
126   template <typename Label>
SetText(const std::string & text)127   auto SetText(const std::string& text) {
128     return std::get<Label>(layout_).setText(
129         {text.c_str(), text.c_str() + text.size()});
130   }
131 
132   template <typename Label>
133   teeui::Error UpdateString();
134 
135   std::uint32_t display_num_;
136   teeui::layout_t<teeui::ConfUILayout> layout_;
137   std::string lang_id_;
138   std::string prompt_text_;  // confirmation ui message
139 
140   /**
141    * Potentially, the same frame could be requested multiple times.
142    *
143    * While another thread/caller is using this frame, the frame should
144    * be kept here, too, to be returned upon future requests.
145    *
146    */
147   std::unique_ptr<TeeUiFrameWrapper> raw_frame_;
148   std::uint32_t current_height_;
149   std::uint32_t current_width_;
150   teeui::Color color_bg_;
151   teeui::Color color_text_;
152   teeui::Color shield_color_;
153   bool is_inverted_;
154   bool is_magnified_;
155   teeui::context<teeui::ConfUIParameters> ctx_;
156   bool is_setup_well_;
157 
158   static constexpr const teeui::Color kColorBackground = 0xffffffff;
159   static constexpr const teeui::Color kColorBackgroundInv = 0xff212121;
160   static constexpr const teeui::Color kColorDisabled = 0xffbdbdbd;
161   static constexpr const teeui::Color kColorDisabledInv = 0xff424242;
162   static constexpr const teeui::Color kColorEnabled = 0xff212121;
163   static constexpr const teeui::Color kColorEnabledInv = 0xffdedede;
164   static constexpr const teeui::Color kColorShield = 0xff778500;
165   static constexpr const teeui::Color kColorShieldInv = 0xffc4cb80;
166   static constexpr const teeui::Color kColorText = 0xff212121;
167   static constexpr const teeui::Color kColorTextInv = 0xffdedede;
168 };
169 
170 Result<std::unique_ptr<ConfUiRendererImpl>>
GenerateRenderer(const std::uint32_t display,const std::string & confirmation_msg,const std::string & locale,const bool inverted,const bool magnified)171 ConfUiRendererImpl::GenerateRenderer(const std::uint32_t display,
172                                      const std::string& confirmation_msg,
173                                      const std::string& locale,
174                                      const bool inverted,
175                                      const bool magnified) {
176   ConfUiRendererImpl* raw_ptr = new ConfUiRendererImpl(
177       display, confirmation_msg, locale, inverted, magnified);
178   CF_EXPECT(raw_ptr && raw_ptr->IsSetUpSuccessful(),
179             "Failed to create ConfUiRendererImpl");
180   return std::unique_ptr<ConfUiRendererImpl>(raw_ptr);
181 }
182 
GetDpi(const int display_num=0)183 static int GetDpi(const int display_num = 0) {
184   auto config = CuttlefishConfig::Get();
185   CHECK(config) << "Config is Missing";
186   auto instance = config->ForDefaultInstance();
187   auto display_configs = instance.display_configs();
188   CHECK_GT(display_configs.size(), display_num)
189       << "Invalid display number " << display_num;
190   return display_configs[display_num].dpi;
191 }
192 
193 /**
194  * device configuration
195  *
196  * ctx_{# of pixels in 1 mm, # of pixels per 1 density independent pixels}
197  *
198  * The numbers are, however, to fit for the host webRTC local/remote clients
199  * in general, not necessarily the supposedly guest device (e.g. Auto, phone,
200  * etc)
201  *
202  * In general, for a normal PC, roughly ctx_(6.45211, 400.0/412.0) is a good
203  * combination for the default DPI, 320. If we want to see the impact
204  * of the change in the guest DPI, we could adjust the combination above
205  * proportionally
206  *
207  */
ConfUiRendererImpl(const std::uint32_t display,const std::string & confirmation_msg,const std::string & locale,const bool inverted,const bool magnified)208 ConfUiRendererImpl::ConfUiRendererImpl(const std::uint32_t display,
209                                        const std::string& confirmation_msg,
210                                        const std::string& locale,
211                                        const bool inverted,
212                                        const bool magnified)
213     : display_num_{display},
214       lang_id_{locale},
215       prompt_text_{confirmation_msg},
216       current_height_{ScreenConnectorInfo::ScreenHeight(display_num_)},
217       current_width_{ScreenConnectorInfo::ScreenWidth(display_num_)},
218       is_inverted_(inverted),
219       is_magnified_(magnified),
220       ctx_(6.45211 * GetDpi() / 320.0, 400.0 / 412.0 * GetDpi() / 320.0),
221       is_setup_well_(false) {
222   SetDeviceContext(current_width_, current_height_, is_inverted_,
223                    is_magnified_);
224   layout_ = teeui::instantiateLayout(teeui::ConfUILayout(), ctx_);
225 
226   if (auto error = UpdateLocale()) {
227     ConfUiLog(ERROR) << "Update Translation Error: " << Enum2Base(error.code());
228     // is_setup_well_ = false;
229     return;
230   }
231   UpdateColorScheme(is_inverted_);
232   SetText<LabelConfMsg>(prompt_text_);
233   is_setup_well_ = true;
234 }
235 
UpdateLocale()236 teeui::Error ConfUiRendererImpl::UpdateLocale() {
237   using teeui::Error;
238   teeui::localization::selectLangId(lang_id_.c_str());
239   if (auto error = UpdateTranslations()) {
240     return error;
241   }
242   return Error::OK;
243 }
244 
245 template <typename Label>
UpdateString()246 teeui::Error ConfUiRendererImpl::UpdateString() {
247   using namespace teeui;
248   const char* str;
249   auto& label = std::get<Label>(layout_);
250   str = localization::lookup(TranslationId(label.textId()));
251   if (str == nullptr) {
252     ConfUiLog(ERROR) << "Given translation_id" << label.textId() << "not found";
253     return Error::Localization;
254   }
255   label.setText({str, str + strlen(str)});
256   return Error::OK;
257 }
258 
UpdateTranslations()259 teeui::Error ConfUiRendererImpl::UpdateTranslations() {
260   using namespace teeui;
261   if (auto error = UpdateString<LabelOK>()) {
262     return error;
263   }
264   if (auto error = UpdateString<LabelCancel>()) {
265     return error;
266   }
267   if (auto error = UpdateString<LabelTitle>()) {
268     return error;
269   }
270   if (auto error = UpdateString<LabelHint>()) {
271     return error;
272   }
273   return Error::OK;
274 }
275 
SetDeviceContext(const unsigned long long w,const unsigned long long h,const bool is_inverted,const bool is_magnified)276 void ConfUiRendererImpl::SetDeviceContext(const unsigned long long w,
277                                           const unsigned long long h,
278                                           const bool is_inverted,
279                                           const bool is_magnified) {
280   using namespace teeui;
281   const auto screen_width = operator""_px(w);
282   const auto screen_height = operator""_px(h);
283   ctx_.setParam<RightEdgeOfScreen>(pxs(screen_width));
284   ctx_.setParam<BottomOfScreen>(pxs(screen_height));
285   if (is_magnified) {
286     ctx_.setParam<DefaultFontSize>(18_dp);
287     ctx_.setParam<BodyFontSize>(20_dp);
288   } else {
289     ctx_.setParam<DefaultFontSize>(14_dp);
290     ctx_.setParam<BodyFontSize>(16_dp);
291   }
292   if (is_inverted) {
293     ctx_.setParam<ShieldColor>(kColorShieldInv);
294     ctx_.setParam<ColorText>(kColorTextInv);
295     ctx_.setParam<ColorBG>(kColorBackgroundInv);
296     ctx_.setParam<ColorButton>(kColorShieldInv);
297   } else {
298     ctx_.setParam<ShieldColor>(kColorShield);
299     ctx_.setParam<ColorText>(kColorText);
300     ctx_.setParam<ColorBG>(kColorBackground);
301     ctx_.setParam<ColorButton>(kColorShield);
302   }
303 }
304 
UpdatePixels(TeeUiFrameWrapper & raw_frame,std::uint32_t x,std::uint32_t y,teeui::Color color)305 teeui::Error ConfUiRendererImpl::UpdatePixels(TeeUiFrameWrapper& raw_frame,
306                                               std::uint32_t x, std::uint32_t y,
307                                               teeui::Color color) {
308   auto buffer = raw_frame.data();
309   const auto height = raw_frame.Height();
310   const auto width = raw_frame.Width();
311   auto pos = width * y + x;
312   if (pos >= (height * width)) {
313     ConfUiLog(ERROR) << "Rendering Out of Bound";
314     return teeui::Error::OutOfBoundsDrawing;
315   }
316   const double alfa = ((color & 0xff000000) >> 24) / 255.0;
317   auto& pixel = *reinterpret_cast<teeui::Color*>(buffer + pos);
318   pixel = alfaCombineChannel(0, alfa, color, pixel) |
319           alfaCombineChannel(8, alfa, color, pixel) |
320           alfaCombineChannel(16, alfa, color, pixel);
321   return teeui::Error::OK;
322 }
323 
UpdateColorScheme(const bool is_inverted)324 void ConfUiRendererImpl::UpdateColorScheme(const bool is_inverted) {
325   using namespace teeui;
326   color_text_ = is_inverted ? kColorDisabledInv : kColorDisabled;
327   shield_color_ = is_inverted ? kColorShieldInv : kColorShield;
328   color_bg_ = is_inverted ? kColorBackgroundInv : kColorBackground;
329 
330   ctx_.setParam<ShieldColor>(shield_color_);
331   ctx_.setParam<ColorText>(color_text_);
332   ctx_.setParam<ColorBG>(color_bg_);
333   return;
334 }
335 
RenderRawFrame()336 std::unique_ptr<TeeUiFrameWrapper>& ConfUiRendererImpl::RenderRawFrame() {
337   /* we repaint only if one or more of the followng meet:
338    *
339    *  1. raw_frame_ is empty
340    *  2. the current_width_ and current_height_ is out of date
341    *
342    */
343   const int w = ScreenConnectorInfo::ScreenWidth(display_num_);
344   const int h = ScreenConnectorInfo::ScreenHeight(display_num_);
345   if (!IsFrameReady() || current_height_ != h || current_width_ != w) {
346     auto new_frame = RepaintRawFrame(w, h);
347     if (!new_frame) {
348       // must repaint but failed
349       raw_frame_ = nullptr;
350       return raw_frame_;
351     }
352     // repainting from the scratch successful in a new frame
353     raw_frame_ = std::move(new_frame);
354     current_width_ = w;
355     current_height_ = h;
356   }
357   return raw_frame_;
358 }
359 
RepaintRawFrame(const int w,const int h)360 std::unique_ptr<TeeUiFrameWrapper> ConfUiRendererImpl::RepaintRawFrame(
361     const int w, const int h) {
362   std::get<teeui::LabelOK>(layout_).setTextColor(kColorEnabled);
363   std::get<teeui::LabelCancel>(layout_).setTextColor(kColorEnabled);
364 
365   /**
366    * should be uint32_t for teeui APIs.
367    * It assumes that each raw frame buffer element is 4 bytes
368    */
369   const teeui::Color background_color =
370       is_inverted_ ? kColorBackgroundInv : kColorBackground;
371   auto new_raw_frame =
372       std::make_unique<TeeUiFrameWrapper>(w, h, background_color);
373   auto draw_pixel = teeui::makePixelDrawer(
374       [this, &new_raw_frame](std::uint32_t x, std::uint32_t y,
375                              teeui::Color color) -> teeui::Error {
376         return this->UpdatePixels(*new_raw_frame, x, y, color);
377       });
378 
379   // render all components
380   const auto error = drawElements(layout_, draw_pixel);
381   if (error) {
382     ConfUiLog(ERROR) << "Painting failed: " << error.code();
383     return nullptr;
384   }
385 
386   return new_raw_frame;
387 }
388 
ConfUiRenderer(ScreenConnectorFrameRenderer & screen_connector)389 ConfUiRenderer::ConfUiRenderer(ScreenConnectorFrameRenderer& screen_connector)
390     : screen_connector_{screen_connector} {}
391 
~ConfUiRenderer()392 ConfUiRenderer::~ConfUiRenderer() {}
393 
RenderDialog(const std::uint32_t display_num,const std::string & prompt_text,const std::string & locale,const std::vector<teeui::UIOption> & ui_options)394 Result<void> ConfUiRenderer::RenderDialog(
395     const std::uint32_t display_num, const std::string& prompt_text,
396     const std::string& locale, const std::vector<teeui::UIOption>& ui_options) {
397   renderer_impl_ = CF_EXPECT(ConfUiRendererImpl::GenerateRenderer(
398       display_num, prompt_text, locale, IsInverted(ui_options),
399       IsMagnified(ui_options)));
400   auto& teeui_frame = renderer_impl_->RenderRawFrame();
401   CF_EXPECT(teeui_frame != nullptr, "RenderRawFrame() failed.");
402   ConfUiLog(VERBOSE) << "actually trying to render the frame"
403                      << thread::GetName();
404   auto frame_width = teeui_frame->Width();
405   auto frame_height = teeui_frame->Height();
406   auto frame_stride_bytes = teeui_frame->ScreenStrideBytes();
407   auto frame_bytes = reinterpret_cast<std::uint8_t*>(teeui_frame->data());
408   CF_EXPECT(screen_connector_.RenderConfirmationUi(
409       display_num, frame_width, frame_height, frame_stride_bytes, frame_bytes));
410   return {};
411 }
412 
IsInverted(const std::vector<teeui::UIOption> & ui_options) const413 bool ConfUiRenderer::IsInverted(
414     const std::vector<teeui::UIOption>& ui_options) const {
415   return Contains(ui_options, teeui::UIOption::AccessibilityInverted);
416 }
417 
IsMagnified(const std::vector<teeui::UIOption> & ui_options) const418 bool ConfUiRenderer::IsMagnified(
419     const std::vector<teeui::UIOption>& ui_options) const {
420   return Contains(ui_options, teeui::UIOption::AccessibilityMagnified);
421 }
422 
IsInConfirm(const std::uint32_t x,const std::uint32_t y)423 bool ConfUiRenderer::IsInConfirm(const std::uint32_t x, const std::uint32_t y) {
424   if (!renderer_impl_) {
425     ConfUiLog(INFO) << "renderer_impl_ is nullptr";
426   }
427   return renderer_impl_ && renderer_impl_->IsInConfirm(x, y);
428 }
IsInCancel(const std::uint32_t x,const std::uint32_t y)429 bool ConfUiRenderer::IsInCancel(const std::uint32_t x, const std::uint32_t y) {
430   if (!renderer_impl_) {
431     ConfUiLog(INFO) << "renderer_impl_ is nullptr";
432   }
433   return renderer_impl_ && renderer_impl_->IsInCancel(x, y);
434 }
435 
436 }  // end of namespace confui
437 }  // end of namespace cuttlefish
438