/* * Copyright 2019, The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #define TLOG_TAG "confirmationui" #include "trusty_confirmation_ui.h" #include "trusty_operation.h" #include "device_parameters.h" #include #include #include #include #include #include #include #include using teeui::ResponseCode; template static void updateColorScheme(Context* ctx, bool inverted) { using namespace teeui::layouts; using namespace teeui; if (inverted) { ctx->template setParam(kColorShieldInv); ctx->template setParam(kColorBackground); ctx->template setParam(kColorBackgroundInv); ctx->template setParam(kColorButtonInv); ctx->template setParam(kColorEnabled); ctx->template setParam(kColorHintInv); } else { ctx->template setParam(kColorShield); ctx->template setParam(kColorEnabled); ctx->template setParam(kColorBackground); ctx->template setParam(kColorButton); ctx->template setParam(kColorBackground); ctx->template setParam(kColorHint); } return; } static teeui::Color alfaCombineChannel(uint32_t shift, double alfa, teeui::Color a, teeui::Color b) { a >>= shift; a &= 0xff; b >>= shift; b &= 0xff; double acc = alfa * a + (1 - alfa) * b; if (acc <= 0) return 0; uint32_t result = acc; if (result > 255) return 255 << shift; return result << shift; } static ResponseCode teeuiError2ResponseCode(const teeui::Error& e) { switch (e.code()) { case teeui::Error::OK: return ResponseCode::OK; case teeui::Error::NotInitialized: return ResponseCode::UIError; case teeui::Error::FaceNotLoaded: return ResponseCode::UIErrorMissingGlyph; case teeui::Error::CharSizeNotSet: return ResponseCode::UIError; case teeui::Error::GlyphNotLoaded: return ResponseCode::UIErrorMissingGlyph; case teeui::Error::GlyphNotRendered: return ResponseCode::UIErrorMissingGlyph; case teeui::Error::GlyphNotExtracted: return ResponseCode::UIErrorMissingGlyph; case teeui::Error::UnsupportedPixelFormat: return ResponseCode::UIError; case teeui::Error::OutOfBoundsDrawing: return ResponseCode::UIErrorMessageTooLong; case teeui::Error::BBoxComputation: return ResponseCode::UIErrorMessageTooLong; case teeui::Error::OutOfMemory: return ResponseCode::UIErrorMessageTooLong; case teeui::Error::Localization: return ResponseCode::UIError; default: return ResponseCode::UIError; } } ResponseCode TrustyConfirmationUI::start(const char* prompt, const char* lang_id, bool inverted, bool magnified) { ResponseCode render_error = ResponseCode::OK; enabled_ = true; inverted_ = inverted; using namespace teeui; const int displayCount = devices::getDisplayCount(); if (displayCount < 1) { TLOGE("Invalid displayCount: %d\n", displayCount); return ResponseCode::UIError; } fb_info_.resize(displayCount); secure_fb_handle_.resize(displayCount); layout_.resize(displayCount); for (int i = 0; i < displayCount; ++i) { if (auto rc = secure_fb_open(&secure_fb_handle_[i], &fb_info_[i], i)) { TLOGE("secure_fb_open returned %d\n", rc); stop(); return ResponseCode::UIError; } if (fb_info_[i].pixel_format != TTUI_PF_RGBA8) { TLOGE("Unknown pixel format %u\n", fb_info_[i].pixel_format); stop(); return ResponseCode::UIError; } std::optional> ctx = devices::getDisplayContext(fb_info_[i].display_index, magnified); if (!ctx) { TLOGE("Failed to get device context: %d\n", i); stop(); return ResponseCode::UIError; } /* Get rotated frame buffer dimensions */ uint32_t rwidth, rheight; if (fb_info_[i].rotation == TTUI_DRAW_ROTATION_90 || fb_info_[i].rotation == TTUI_DRAW_ROTATION_270) { rwidth = fb_info_[i].height; rheight = fb_info_[i].width; } else { rwidth = fb_info_[i].width; rheight = fb_info_[i].height; } /* Check the layout context and framebuffer agree on dimensions */ if (*ctx->getParam() != pxs(rwidth) || *ctx->getParam() != pxs(rheight)) { TLOGE("Framebuffer dimensions do not match panel configuration\n"); stop(); return ResponseCode::UIError; } /* Set the colours */ updateColorScheme(&ctx.value(), inverted_); /* Get the layout for the display */ std::optional> layout = devices::getDisplayLayout(fb_info_[i].display_index, inverted, *ctx); if (!layout) { TLOGE("Failed to get device layout: %d\n", i); stop(); return ResponseCode::UIError; } layout_[i] = std::move(*layout); /* Configure the layout */ layout_[i]->setLanguage(lang_id); layout_[i]->setConfirmationMessage(prompt); layout_[i]->showInstructions(true /* enable */); render_error = renderAndSwap(i); if (render_error != ResponseCode::OK) { stop(); return render_error; } } return ResponseCode::OK; } ResponseCode TrustyConfirmationUI::renderAndSwap(uint32_t idx) { /* All display will be rendering the same content */ auto drawPixel = teeui::makePixelDrawer([&, this](uint32_t x, uint32_t y, teeui::Color color) -> teeui::Error { uint32_t temp; TLOGD("px %u %u: %08x", x, y, color); /* Transform co-ordinates for rotation */ switch (fb_info_[idx].rotation) { case TTUI_DRAW_ROTATION_0: break; case TTUI_DRAW_ROTATION_90: temp = y; y = x; x = (fb_info_[idx].width - temp) - 1; break; case TTUI_DRAW_ROTATION_180: x = (fb_info_[idx].width - x) - 1; y = (fb_info_[idx].height - y) - 1; break; case TTUI_DRAW_ROTATION_270: temp = x; x = y; y = (fb_info_[idx].height - temp) - 1; break; default: return teeui::Error::UnsupportedPixelFormat; } size_t pos = y * fb_info_[idx].line_stride + x * fb_info_[idx].pixel_stride; TLOGD("pos: %zu, bufferSize: %" PRIu32 "\n", pos, fb_info_[idx].size); if (pos >= fb_info_[idx].size) { return teeui::Error::OutOfBoundsDrawing; } double alfa = (color & 0xff000000) >> 24; alfa /= 255.0; auto& pixel = *reinterpret_cast(fb_info_[idx].buffer + pos); pixel = alfaCombineChannel(0, alfa, color, pixel) | alfaCombineChannel(8, alfa, color, pixel) | alfaCombineChannel(16, alfa, color, pixel); return teeui::Error::OK; }); TLOGI("begin rendering\n"); teeui::Color bgColor = inverted_ ? teeui::layouts::kColorBackgroundInv : teeui::layouts::kColorBackground; uint8_t* line_iter = fb_info_[idx].buffer; for (uint32_t yi = 0; yi < fb_info_[idx].height; ++yi) { auto pixel_iter = line_iter; for (uint32_t xi = 0; xi < fb_info_[idx].width; ++xi) { *reinterpret_cast(pixel_iter) = bgColor; pixel_iter += fb_info_[idx].pixel_stride; } line_iter += fb_info_[idx].line_stride; } if (auto error = layout_[idx]->drawElements(drawPixel)) { TLOGE("Element drawing failed: %u\n", error.code()); return teeuiError2ResponseCode(error); } if (auto rc = secure_fb_display_next(secure_fb_handle_[idx], &fb_info_[idx])) { TLOGE("secure_fb_display_next returned %d\n", rc); return ResponseCode::UIError; } return ResponseCode::OK; } ResponseCode TrustyConfirmationUI::showInstructions(bool enable) { using namespace teeui; if (enabled_ == enable) return ResponseCode::OK; enabled_ = enable; ResponseCode rc = ResponseCode::OK; for (auto i = 0; i < (int)layout_.size(); ++i) { layout_[i]->showInstructions(enable); if (enable) { rc = renderAndSwap(i); if (rc != ResponseCode::OK) { stop(); break; } } } return rc; } void TrustyConfirmationUI::stop() { TLOGI("calling gui stop\n"); for (auto& secure_fb_handle: secure_fb_handle_) { secure_fb_close(secure_fb_handle); secure_fb_handle = NULL; } TLOGI("calling gui stop - done\n"); }