| /* |
| * 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. |
| */ |
| |
| #include "trusty_confirmation_ui.h" |
| |
| #include <devices/device_parameters.h> |
| #include <layouts/layout.h> |
| |
| #include <teeui/error.h> |
| #include <teeui/localization/ConfirmationUITranslations.h> |
| #include <teeui/utils.h> |
| |
| #include <interface/secure_fb/secure_fb.h> |
| |
| #include "trusty_operation.h" |
| |
| #include <inttypes.h> |
| #include <stdio.h> |
| |
| #include <trusty_log.h> |
| |
| #define TLOG_TAG "confirmationui" |
| |
| using teeui::ResponseCode; |
| |
| static constexpr const teeui::Color kColorEnabled = 0xff212121; |
| static constexpr const teeui::Color kColorDisabled = 0xffbdbdbd; |
| static constexpr const teeui::Color kColorEnabledInv = 0xffdedede; |
| static constexpr const teeui::Color kColorDisabledInv = 0xff424242; |
| static constexpr const teeui::Color kColorBackground = 0xffffffff; |
| static constexpr const teeui::Color kColorBackgroundInv = 0xff212121; |
| static constexpr const teeui::Color kColorShieldInv = 0xffc4cb80; |
| static constexpr const teeui::Color kColorShield = 0xff778500; |
| |
| template <typename Label, typename Layout> |
| static teeui::Error updateString(Layout* layout) { |
| using namespace teeui; |
| const char* str; |
| auto& label = std::get<Label>(*layout); |
| |
| str = localization::lookup(TranslationId(label.textId())); |
| if (str == nullptr) { |
| TLOGW("Given translation_id %" PRIu64 " not found", label.textId()); |
| return Error::Localization; |
| } |
| label.setText({str, str + strlen(str)}); |
| return Error::OK; |
| } |
| |
| template <typename Context> |
| static void updateColorScheme(Context* ctx, bool inverted) { |
| using namespace teeui; |
| if (inverted) { |
| ctx->template setParam<ShieldColor>(kColorShieldInv); |
| ctx->template setParam<ColorText>(kColorDisabledInv); |
| ctx->template setParam<ColorBG>(kColorBackgroundInv); |
| } else { |
| ctx->template setParam<ShieldColor>(kColorShield); |
| ctx->template setParam<ColorText>(kColorDisabled); |
| ctx->template setParam<ColorBG>(kColorBackground); |
| } |
| 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; |
| } |
| |
| template <typename... Elements> |
| static teeui::Error drawElements(std::tuple<Elements...>& layout, |
| const teeui::PixelDrawer& drawPixel) { |
| // Error::operator|| is overloaded, so we don't get short circuit |
| // evaluation. But we get the first error that occurs. We will still try and |
| // draw the remaining elements in the order they appear in the layout tuple. |
| return (std::get<Elements>(layout).draw(drawPixel) || ...); |
| } |
| |
| 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; |
| } |
| } |
| |
| teeui::Error TrustyConfirmationUI::updateTranslations() { |
| using namespace teeui; |
| if (auto error = updateString<LabelOK>(&layout_)) |
| return error; |
| if (auto error = updateString<LabelCancel>(&layout_)) |
| return error; |
| if (auto error = updateString<LabelTitle>(&layout_)) |
| return error; |
| if (auto error = updateString<LabelHint>(&layout_)) |
| return error; |
| return Error::OK; |
| } |
| |
| ResponseCode TrustyConfirmationUI::start(const char* prompt, |
| const char* lang_id, |
| bool inverted, |
| bool magnified) { |
| enabled_ = true; |
| inverted_ = inverted; |
| auto rc = trusty_secure_fb_get_secure_fb(&fb_info_); |
| if (rc != TTUI_ERROR_OK) { |
| TLOGE("trusty_secure_fb_get_secure_fb returned %d\n", rc); |
| return ResponseCode::UIError; |
| } |
| if (fb_info_.pixel_format != TTUI_PF_RGBA8) { |
| TLOGE("Unknown pixel format %u\n", fb_info_.pixel_format); |
| return ResponseCode::UIError; |
| } |
| |
| using namespace teeui; |
| |
| auto ctx = devices::getDeviceContext(magnified); |
| |
| if (*ctx.getParam<RightEdgeOfScreen>() != pxs(fb_info_.width) || |
| *ctx.getParam<BottomOfScreen>() != pxs(fb_info_.height)) { |
| TLOGE("Framebuffer dimensions do not match panel configuration\n"); |
| TLOGE("Check device configuration\n"); |
| return ResponseCode::UIError; |
| } |
| |
| layout_ = instantiateLayout(ConfUILayout(), ctx); |
| |
| localization::selectLangId(lang_id); |
| if (auto error = updateTranslations()) { |
| return teeuiError2ResponseCode(error); |
| } |
| updateColorScheme(&ctx, inverted_); |
| |
| std::get<LabelBody>(layout_).setText({prompt, prompt + strlen(prompt)}); |
| |
| showInstructions(false /* enable */); |
| auto render_error = renderAndSwap(); |
| if (render_error != ResponseCode::OK) { |
| stop(); |
| } |
| return render_error; |
| } |
| |
| ResponseCode TrustyConfirmationUI::renderAndSwap() { |
| auto drawPixel = teeui::makePixelDrawer([&, this](uint32_t x, uint32_t y, |
| teeui::Color color) |
| -> teeui::Error { |
| TLOGD("px %u %u: %08x", x, y, color); |
| size_t pos = y * fb_info_.line_stride + x * fb_info_.pixel_stride; |
| TLOGD("pos: %zu, bufferSize: %" PRIu32 "\n", pos, fb_info_.size); |
| if (pos >= fb_info_.size) { |
| return teeui::Error::OutOfBoundsDrawing; |
| } |
| double alfa = (color & 0xff000000) >> 24; |
| alfa /= 255.0; |
| auto& pixel = *reinterpret_cast<teeui::Color*>(fb_info_.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 = kColorBackground; |
| if (inverted_) { |
| bgColor = kColorBackgroundInv; |
| } |
| uint8_t* line_iter = fb_info_.buffer; |
| for (uint32_t yi = 0; yi < fb_info_.height; ++yi) { |
| auto pixel_iter = line_iter; |
| for (uint32_t xi = 0; xi < fb_info_.width; ++xi) { |
| *reinterpret_cast<uint32_t*>(pixel_iter) = bgColor; |
| pixel_iter += fb_info_.pixel_stride; |
| } |
| line_iter += fb_info_.line_stride; |
| } |
| |
| if (auto error = drawElements(layout_, drawPixel)) { |
| TLOGE("Element drawing failed: %u\n", error.code()); |
| return teeuiError2ResponseCode(error); |
| } |
| |
| auto rc = trusty_secure_fb_display_next(&fb_info_, false); |
| if (rc != TTUI_ERROR_OK) { |
| TLOGE("trusty_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; |
| Color color; |
| if (enable) { |
| if (inverted_) |
| color = kColorEnabledInv; |
| else |
| color = kColorEnabled; |
| } else { |
| if (inverted_) |
| color = kColorDisabledInv; |
| else |
| color = kColorDisabled; |
| } |
| std::get<LabelOK>(layout_).setTextColor(color); |
| std::get<LabelCancel>(layout_).setTextColor(color); |
| ResponseCode rc = ResponseCode::OK; |
| if (enable) { |
| rc = renderAndSwap(); |
| if (rc != ResponseCode::OK) { |
| stop(); |
| } |
| } |
| return ResponseCode::OK; |
| } |
| |
| void TrustyConfirmationUI::stop() { |
| TLOGI("calling gui stop\n"); |
| trusty_secure_fb_release_display(); |
| TLOGI("calling gui stop - done\n"); |
| } |