1 /*
2  * Copyright 2019, 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 #define TLOG_TAG "confirmationui"
18 
19 #include "trusty_confirmation_ui.h"
20 #include "trusty_operation.h"
21 
22 #include "device_parameters.h"
23 
24 #include <interface/secure_fb/secure_fb.h>
25 #include <inttypes.h>
26 #include <layouts/layout.h>
27 #include <stdio.h>
28 #include <teeui/error.h>
29 #include <teeui/localization/ConfirmationUITranslations.h>
30 #include <teeui/utils.h>
31 #include <trusty_log.h>
32 
33 using teeui::ResponseCode;
34 
35 template <typename Context>
updateColorScheme(Context * ctx,bool inverted)36 static void updateColorScheme(Context* ctx, bool inverted) {
37     using namespace teeui::layouts;
38     using namespace teeui;
39 
40     if (inverted) {
41         ctx->template setParam<ShieldColor>(kColorShieldInv);
42         ctx->template setParam<ColorText>(kColorBackground);
43         ctx->template setParam<ColorBG>(kColorBackgroundInv);
44         ctx->template setParam<ColorButton>(kColorButtonInv);
45         ctx->template setParam<ColorButtonBG>(kColorEnabled);
46         ctx->template setParam<ColorTextHint>(kColorHintInv);
47     } else {
48         ctx->template setParam<ShieldColor>(kColorShield);
49         ctx->template setParam<ColorText>(kColorEnabled);
50         ctx->template setParam<ColorBG>(kColorBackground);
51         ctx->template setParam<ColorButton>(kColorButton);
52         ctx->template setParam<ColorButtonBG>(kColorBackground);
53         ctx->template setParam<ColorTextHint>(kColorHint);
54     }
55     return;
56 }
57 
alfaCombineChannel(uint32_t shift,double alfa,teeui::Color a,teeui::Color b)58 static teeui::Color alfaCombineChannel(uint32_t shift,
59                                        double alfa,
60                                        teeui::Color a,
61                                        teeui::Color b) {
62     a >>= shift;
63     a &= 0xff;
64     b >>= shift;
65     b &= 0xff;
66     double acc = alfa * a + (1 - alfa) * b;
67     if (acc <= 0)
68         return 0;
69     uint32_t result = acc;
70     if (result > 255)
71         return 255 << shift;
72     return result << shift;
73 }
74 
teeuiError2ResponseCode(const teeui::Error & e)75 static ResponseCode teeuiError2ResponseCode(const teeui::Error& e) {
76     switch (e.code()) {
77     case teeui::Error::OK:
78         return ResponseCode::OK;
79     case teeui::Error::NotInitialized:
80         return ResponseCode::UIError;
81     case teeui::Error::FaceNotLoaded:
82         return ResponseCode::UIErrorMissingGlyph;
83     case teeui::Error::CharSizeNotSet:
84         return ResponseCode::UIError;
85     case teeui::Error::GlyphNotLoaded:
86         return ResponseCode::UIErrorMissingGlyph;
87     case teeui::Error::GlyphNotRendered:
88         return ResponseCode::UIErrorMissingGlyph;
89     case teeui::Error::GlyphNotExtracted:
90         return ResponseCode::UIErrorMissingGlyph;
91     case teeui::Error::UnsupportedPixelFormat:
92         return ResponseCode::UIError;
93     case teeui::Error::OutOfBoundsDrawing:
94         return ResponseCode::UIErrorMessageTooLong;
95     case teeui::Error::BBoxComputation:
96         return ResponseCode::UIErrorMessageTooLong;
97     case teeui::Error::OutOfMemory:
98         return ResponseCode::UIErrorMessageTooLong;
99     case teeui::Error::Localization:
100         return ResponseCode::UIError;
101     default:
102         return ResponseCode::UIError;
103     }
104 }
105 
start(const char * prompt,const char * lang_id,bool inverted,bool magnified)106 ResponseCode TrustyConfirmationUI::start(const char* prompt,
107                                          const char* lang_id,
108                                          bool inverted,
109                                          bool magnified) {
110     ResponseCode render_error = ResponseCode::OK;
111 
112     enabled_ = true;
113     inverted_ = inverted;
114 
115     using namespace teeui;
116 
117     const int displayCount = devices::getDisplayCount();
118 
119     if (displayCount < 1) {
120         TLOGE("Invalid displayCount:  %d\n", displayCount);
121         return ResponseCode::UIError;
122     }
123 
124     fb_info_.resize(displayCount);
125     secure_fb_handle_.resize(displayCount);
126     layout_.resize(displayCount);
127 
128     for (int i = 0; i < displayCount; ++i) {
129         if (auto rc = secure_fb_open(&secure_fb_handle_[i], &fb_info_[i], i)) {
130             TLOGE("secure_fb_open returned  %d\n", rc);
131             stop();
132             return ResponseCode::UIError;
133         }
134 
135         if (fb_info_[i].pixel_format != TTUI_PF_RGBA8) {
136             TLOGE("Unknown pixel format %u\n", fb_info_[i].pixel_format);
137             stop();
138             return ResponseCode::UIError;
139         }
140 
141         std::optional<context<ConUIParameters>> ctx =
142                 devices::getDisplayContext(fb_info_[i].display_index, magnified);
143         if (!ctx) {
144             TLOGE("Failed to get device context: %d\n", i);
145             stop();
146             return ResponseCode::UIError;
147         }
148 
149         /* Get rotated frame buffer dimensions */
150         uint32_t rwidth, rheight;
151 
152         if (fb_info_[i].rotation == TTUI_DRAW_ROTATION_90 ||
153             fb_info_[i].rotation == TTUI_DRAW_ROTATION_270) {
154             rwidth = fb_info_[i].height;
155             rheight = fb_info_[i].width;
156         } else {
157             rwidth = fb_info_[i].width;
158             rheight = fb_info_[i].height;
159         }
160 
161         /* Check the layout context and framebuffer agree on dimensions */
162         if (*ctx->getParam<RightEdgeOfScreen>() != pxs(rwidth) ||
163             *ctx->getParam<BottomOfScreen>() != pxs(rheight)) {
164             TLOGE("Framebuffer dimensions do not match panel configuration\n");
165             stop();
166             return ResponseCode::UIError;
167         }
168 
169         /* Set the colours */
170         updateColorScheme(&ctx.value(), inverted_);
171 
172         /* Get the layout for the display */
173         std::optional<std::unique_ptr<teeui::layouts::ILayout>> layout =
174                 devices::getDisplayLayout(fb_info_[i].display_index, inverted,
175                                           *ctx);
176         if (!layout) {
177             TLOGE("Failed to get device layout: %d\n", i);
178             stop();
179             return ResponseCode::UIError;
180         }
181 
182         layout_[i] = std::move(*layout);
183 
184         /* Configure the layout */
185         layout_[i]->setLanguage(lang_id);
186         layout_[i]->setConfirmationMessage(prompt);
187         layout_[i]->showInstructions(true /* enable */);
188 
189         render_error = renderAndSwap(i);
190         if (render_error != ResponseCode::OK) {
191             stop();
192             return render_error;
193         }
194     }
195     return ResponseCode::OK;
196 }
197 
renderAndSwap(uint32_t idx)198 ResponseCode TrustyConfirmationUI::renderAndSwap(uint32_t idx) {
199     /* All display will be rendering the same content */
200     auto drawPixel = teeui::makePixelDrawer([&, this](uint32_t x, uint32_t y,
201                                                       teeui::Color color)
202                                                     -> teeui::Error {
203         uint32_t temp;
204 
205         TLOGD("px %u %u: %08x", x, y, color);
206 
207         /* Transform co-ordinates for rotation */
208         switch (fb_info_[idx].rotation) {
209         case TTUI_DRAW_ROTATION_0:
210             break;
211 
212         case TTUI_DRAW_ROTATION_90:
213             temp = y;
214             y = x;
215             x = (fb_info_[idx].width - temp) - 1;
216             break;
217 
218         case TTUI_DRAW_ROTATION_180:
219             x = (fb_info_[idx].width - x) - 1;
220             y = (fb_info_[idx].height - y) - 1;
221             break;
222 
223         case TTUI_DRAW_ROTATION_270:
224             temp = x;
225             x = y;
226             y = (fb_info_[idx].height - temp) - 1;
227             break;
228 
229         default:
230             return teeui::Error::UnsupportedPixelFormat;
231         }
232 
233         size_t pos =
234                 y * fb_info_[idx].line_stride + x * fb_info_[idx].pixel_stride;
235         TLOGD("pos: %zu, bufferSize: %" PRIu32 "\n", pos, fb_info_[idx].size);
236         if (pos >= fb_info_[idx].size) {
237             return teeui::Error::OutOfBoundsDrawing;
238         }
239         double alfa = (color & 0xff000000) >> 24;
240         alfa /= 255.0;
241         auto& pixel =
242                 *reinterpret_cast<teeui::Color*>(fb_info_[idx].buffer + pos);
243 
244         pixel = alfaCombineChannel(0, alfa, color, pixel) |
245                 alfaCombineChannel(8, alfa, color, pixel) |
246                 alfaCombineChannel(16, alfa, color, pixel);
247         return teeui::Error::OK;
248     });
249 
250     TLOGI("begin rendering\n");
251 
252     teeui::Color bgColor = inverted_ ? teeui::layouts::kColorBackgroundInv
253                                      : teeui::layouts::kColorBackground;
254 
255     uint8_t* line_iter = fb_info_[idx].buffer;
256     for (uint32_t yi = 0; yi < fb_info_[idx].height; ++yi) {
257         auto pixel_iter = line_iter;
258         for (uint32_t xi = 0; xi < fb_info_[idx].width; ++xi) {
259             *reinterpret_cast<uint32_t*>(pixel_iter) = bgColor;
260             pixel_iter += fb_info_[idx].pixel_stride;
261         }
262         line_iter += fb_info_[idx].line_stride;
263     }
264 
265     if (auto error = layout_[idx]->drawElements(drawPixel)) {
266         TLOGE("Element drawing failed: %u\n", error.code());
267         return teeuiError2ResponseCode(error);
268     }
269 
270     if (auto rc = secure_fb_display_next(secure_fb_handle_[idx],
271                                          &fb_info_[idx])) {
272         TLOGE("secure_fb_display_next returned  %d\n", rc);
273         return ResponseCode::UIError;
274     }
275 
276     return ResponseCode::OK;
277 }
278 
showInstructions(bool enable)279 ResponseCode TrustyConfirmationUI::showInstructions(bool enable) {
280     using namespace teeui;
281 
282     if (enabled_ == enable)
283         return ResponseCode::OK;
284     enabled_ = enable;
285 
286     ResponseCode rc = ResponseCode::OK;
287 
288     for (auto i = 0; i < (int)layout_.size(); ++i) {
289         layout_[i]->showInstructions(enable);
290 
291         if (enable) {
292             rc = renderAndSwap(i);
293             if (rc != ResponseCode::OK) {
294                 stop();
295                 break;
296             }
297         }
298     }
299 
300     return rc;
301 }
302 
stop()303 void TrustyConfirmationUI::stop() {
304     TLOGI("calling gui stop\n");
305     for (auto& secure_fb_handle: secure_fb_handle_) {
306         secure_fb_close(secure_fb_handle);
307         secure_fb_handle = NULL;
308     }
309     TLOGI("calling gui stop - done\n");
310 }
311