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