1 /*
2  * Copyright (c) 2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "boot_compile_progress.h"
17 
18 #include <chrono>
19 #include "event_handler.h"
20 #include "parameter.h"
21 #include "parameters.h"
22 #include "platform/common/rs_system_properties.h"
23 #include "recording/recording_canvas.h"
24 #ifdef NEW_RENDER_CONTEXT
25 #include "render_context_factory.h"
26 #include "rs_surface_factory.h"
27 #endif
28 #include "animation/rs_animation_common.h"
29 #include "animation/rs_cubic_bezier_interpolator.h"
30 #include "animation/rs_interpolator.h"
31 #include "transaction/rs_interfaces.h"
32 #include "transaction/rs_render_service_client.h"
33 #include "transaction/rs_transaction.h"
34 #include "util.h"
35 
36 namespace OHOS {
37 namespace {
38     constexpr const char* OTA_COMPILE_TIME_LIMIT = "persist.bms.optimizing_apps.timing";
39     constexpr int32_t OTA_COMPILE_TIME_LIMIT_DEFAULT = 4 * 60;
40     constexpr const char* OTA_COMPILE_DISPLAY_INFO = "const.bms.optimizing_apps.display_info";
41     const std::string OTA_COMPILE_DISPLAY_INFO_DEFAULT = "正在优化应用";
42     constexpr const int32_t ONE_HUNDRED_PERCENT = 100;
43     constexpr const int32_t SEC_MS = 1000;
44     constexpr const int32_t CIRCLE_NUM = 3;
45     constexpr const float RADIUS = 10.0f;
46     constexpr const float OFFSET_Y_PERCENT = 0.85f;
47     constexpr const float HEIGHT_PERCENT = 0.05f;
48     constexpr const int TEXT_BLOB_OFFSET = 5;
49     constexpr const int FONT_SIZE_VP = 12;
50     constexpr const int32_t MAX_RETRY_TIMES = 5;
51     constexpr const int32_t WAIT_MS = 500;
52     constexpr const int32_t WAITING_BMS_TIMEOUT = 30;
53     constexpr const int32_t LOADING_FPS = 30;
54     constexpr const int32_t PERIOD_FPS = 10;
55     constexpr const int32_t CHANGE_FREQ = 4;
56     constexpr const float TEN_FRAME_TIMES = 10.0f;
57     constexpr const float SHARP_CURVE_CTLX1 = 0.33f;
58     constexpr const float SHARP_CURVE_CTLY1 = 0.0f;
59     constexpr const float SHARP_CURVE_CTLX2 = 0.67f;
60     constexpr const float SHARP_CURVE_CTLY2 = 1.0f;
61     constexpr const float OPACITY_ARR[3][3][2] = {
62         { { 0.2f, 1.0f }, { 1.0f, 0.5f }, { 0.5f, 0.2f } },
63         { { 0.5f, 0.2f }, { 0.2f, 1.0f }, { 1.0f, 0.5f } },
64         { { 1.0f, 0.5f }, { 0.5f, 0.2f }, { 0.2f, 1.0f } },
65     };
66 }
67 
Init(const BootAnimationConfig & config)68 void BootCompileProgress::Init(const BootAnimationConfig& config)
69 {
70     LOGI("ota compile, screenId: " BPUBU64 "", config.screenId);
71     screenId_ = config.screenId;
72     rotateDegree_ = config.rotateDegree;
73     Rosen::RSInterfaces& interface = Rosen::RSInterfaces::GetInstance();
74     Rosen::RSScreenModeInfo modeInfo = interface.GetScreenActiveMode(config.screenId);
75     windowWidth_ = modeInfo.GetScreenWidth();
76     windowHeight_ = modeInfo.GetScreenHeight();
77     fontSize_ = TransalteVp2Pixel(std::min(windowWidth_, windowHeight_), FONT_SIZE_VP);
78 
79     timeLimitSec_ = system::GetIntParameter<int32_t>(OTA_COMPILE_TIME_LIMIT, OTA_COMPILE_TIME_LIMIT_DEFAULT);
80     tf_ = Rosen::Drawing::Typeface::MakeFromName("HarmonyOS Sans SC", Rosen::Drawing::FontStyle());
81 
82     displayInfo_ = system::GetParameter(OTA_COMPILE_DISPLAY_INFO, OTA_COMPILE_DISPLAY_INFO_DEFAULT);
83     sharpCurve_ = std::make_shared<Rosen::RSCubicBezierInterpolator>(
84         SHARP_CURVE_CTLX1, SHARP_CURVE_CTLY1, SHARP_CURVE_CTLX2, SHARP_CURVE_CTLY2);
85     compileRunner_ = AppExecFwk::EventRunner::Create(false);
86     compileHandler_ = std::make_shared<AppExecFwk::EventHandler>(compileRunner_);
87     compileHandler_->PostTask([this] { this->CreateCanvasNode(); });
88     compileHandler_->PostTask([this] { this->RegisterVsyncCallback(); });
89     compileRunner_->Run();
90 }
91 
CreateCanvasNode()92 bool BootCompileProgress::CreateCanvasNode()
93 {
94     struct Rosen::RSSurfaceNodeConfig surfaceNodeConfig;
95     surfaceNodeConfig.SurfaceNodeName = "BootCompileProgressNode";
96     surfaceNodeConfig.isSync = true;
97     Rosen::RSSurfaceNodeType surfaceNodeType = Rosen::RSSurfaceNodeType::SELF_DRAWING_WINDOW_NODE;
98     rsSurfaceNode_ = Rosen::RSSurfaceNode::Create(surfaceNodeConfig, surfaceNodeType);
99     if (!rsSurfaceNode_) {
100         LOGE("ota compile, SFNode create failed");
101         compileRunner_->Stop();
102         return false;
103     }
104     float positionZ = MAX_ZORDER + 1;
105     rsSurfaceNode_->SetRotation(rotateDegree_);
106     rsSurfaceNode_->SetPositionZ(positionZ);
107     rsSurfaceNode_->SetBounds(0, 0, windowWidth_, windowHeight_);
108     rsSurfaceNode_->SetBackgroundColor(SK_ColorTRANSPARENT);
109     rsSurfaceNode_->SetFrameGravity(Rosen::Gravity::RESIZE_ASPECT);
110     rsSurfaceNode_->SetBootAnimation(true);
111     Rosen::RSTransaction::FlushImplicitTransaction();
112     rsSurfaceNode_->AttachToDisplay(screenId_);
113     Rosen::RSTransaction::FlushImplicitTransaction();
114 
115     rsCanvasNode_ = Rosen::RSCanvasNode::Create();
116     rsCanvasNode_->SetBounds(0, 0, windowWidth_, windowHeight_);
117     rsCanvasNode_->SetFrame(0, windowHeight_ * OFFSET_Y_PERCENT, windowWidth_, windowHeight_ * HEIGHT_PERCENT);
118     rsCanvasNode_->SetBackgroundColor(Rosen::Drawing::Color::COLOR_TRANSPARENT);
119     rsCanvasNode_->SetPositionZ(positionZ);
120     rsSurfaceNode_->AddChild(rsCanvasNode_, 0);
121 
122     LOGI("CreateCanvasNode success");
123     return true;
124 }
125 
RegisterVsyncCallback()126 bool BootCompileProgress::RegisterVsyncCallback()
127 {
128     if (system::GetParameter(BMS_COMPILE_STATUS, "-1") == BMS_COMPILE_STATUS_END) {
129         LOGI("bms compile is already done.");
130         compileRunner_->Stop();
131         return false;
132     }
133 
134     if (!WaitBmsStartIfNeeded()) {
135         compileRunner_->Stop();
136         return false;
137     }
138 
139     auto& rsClient = Rosen::RSInterfaces::GetInstance();
140     int32_t retry = 0;
141     while (receiver_ == nullptr) {
142         if (retry++ > MAX_RETRY_TIMES) {
143             LOGE("get vsync receiver failed");
144             compileRunner_->Stop();
145             return false;
146         }
147         if (retry > 1) {
148             std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_MS));
149         }
150         receiver_ = rsClient.CreateVSyncReceiver("BootCompileProgress", compileHandler_);
151     }
152     VsyncError ret = receiver_->Init();
153     if (ret) {
154         compileRunner_->Stop();
155         LOGE("init vsync receiver failed");
156         return false;
157     }
158 
159     Rosen::VSyncReceiver::FrameCallback fcb = {
160         .userData_ = this,
161         .callback_ = [this](int64_t, void*) { this->OnVsync(); },
162     };
163     ret = receiver_->SetVSyncRate(fcb, CHANGE_FREQ);
164     if (ret) {
165         compileRunner_->Stop();
166         LOGE("set vsync rate failed");
167     }
168 
169     startTimeMs_ = std::chrono::duration_cast<std::chrono::milliseconds>(
170         std::chrono::system_clock::now().time_since_epoch()).count();
171     endTimePredictMs_ = startTimeMs_ + timeLimitSec_ * SEC_MS;
172 
173     LOGI("RegisterVsyncCallback success");
174     return true;
175 }
176 
WaitBmsStartIfNeeded()177 bool BootCompileProgress::WaitBmsStartIfNeeded()
178 {
179     if (WaitParameter(BMS_COMPILE_STATUS, BMS_COMPILE_STATUS_BEGIN.c_str(), WAITING_BMS_TIMEOUT) != 0) {
180         LOGE("waiting bms start oat compile failed.");
181         return false;
182     }
183     return true;
184 }
185 
OnVsync()186 void BootCompileProgress::OnVsync()
187 {
188     if (!isUpdateOptEnd_) {
189         compileHandler_->PostTask([this] { this->DrawCompileProgress(); });
190     } else {
191         LOGI("ota compile completed");
192         compileRunner_->Stop();
193     }
194 }
195 
DrawCompileProgress()196 void BootCompileProgress::DrawCompileProgress()
197 {
198     UpdateCompileProgress();
199 
200     auto canvas = static_cast<Rosen::Drawing::RecordingCanvas*>(
201         rsCanvasNode_->BeginRecording(windowWidth_, windowHeight_ * HEIGHT_PERCENT));
202     if (canvas == nullptr) {
203         LOGE("DrawCompileProgress canvas is null");
204         return;
205     }
206     Rosen::Drawing::Font font;
207     font.SetTypeface(tf_);
208     font.SetSize(fontSize_);
209     char info[64] = {0};
210     int ret = sprintf_s(info, sizeof(info), "%s %d%%", displayInfo_.c_str(), progress_);
211     if (ret == -1) {
212         LOGE("set info failed");
213         return;
214     }
215     std::shared_ptr<Rosen::Drawing::TextBlob> textBlob = Rosen::Drawing::TextBlob::MakeFromString(info, font);
216 
217     Rosen::Drawing::Brush whiteBrush;
218     whiteBrush.SetColor(0xFFFFFFFF);
219     whiteBrush.SetAntiAlias(true);
220     canvas->AttachBrush(whiteBrush);
221 
222     auto textWidth = font.MeasureText(info, strlen(info), Rosen::Drawing::TextEncoding::UTF8, nullptr);
223     float scalarX = windowWidth_ / NUMBER_TWO - textWidth / NUMBER_TWO;
224     float scalarY = TEXT_BLOB_OFFSET + textBlob->Bounds()->GetHeight() / NUMBER_TWO;
225     canvas->DrawTextBlob(textBlob.get(), scalarX, scalarY);
226     canvas->DetachBrush();
227 
228     Rosen::Drawing::Brush grayBrush;
229     grayBrush.SetColor(0x40FFFFFF);
230     grayBrush.SetAntiAlias(true);
231 
232     int32_t freqNum = times_++;
233     for (int i = 0; i < CIRCLE_NUM; i++) {
234         canvas->AttachBrush(DrawProgressPoint(i, freqNum));
235         int pointX = windowWidth_/2.0f + 4 * RADIUS * (i - 1); // align point in center and 2RADUIS between two points
236         int pointY = rsCanvasNode_->GetPaintHeight() - 2 * RADIUS;
237         canvas->DrawCircle({pointX, pointY}, RADIUS);
238         canvas->DetachBrush();
239     }
240 
241     rsCanvasNode_->FinishRecording();
242     Rosen::RSTransaction::FlushImplicitTransaction();
243 
244     if (progress_ >= ONE_HUNDRED_PERCENT) {
245         isUpdateOptEnd_ = true;
246     }
247 }
248 
UpdateCompileProgress()249 void BootCompileProgress::UpdateCompileProgress()
250 {
251     if (!isBmsCompileDone_) {
252         isBmsCompileDone_ = system::GetParameter(BMS_COMPILE_STATUS, "-1") == BMS_COMPILE_STATUS_END;
253         int64_t now =
254             std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch())
255             .count();
256         if (endTimePredictMs_ < now) {
257             progress_ = ONE_HUNDRED_PERCENT;
258             return;
259         }
260         progress_ = (int32_t)((now - startTimeMs_) * ONE_HUNDRED_PERCENT / (timeLimitSec_ * SEC_MS));
261         progress_ = progress_ < 0 ? 0 : progress_ > ONE_HUNDRED_PERCENT ? ONE_HUNDRED_PERCENT: progress_;
262     } else {
263         progress_++;
264     }
265 
266     LOGD("update progress: %{public}d", progress_);
267 }
268 
DrawProgressPoint(int32_t idx,int32_t frameNum)269 Rosen::Drawing::Brush BootCompileProgress::DrawProgressPoint(int32_t idx, int32_t frameNum)
270 {
271     int32_t fpsNo = frameNum % LOADING_FPS;
272     int32_t fpsStage = fpsNo / PERIOD_FPS;
273     float input  = fpsNo % PERIOD_FPS / TEN_FRAME_TIMES;
274 
275     float startOpaCity = OPACITY_ARR[idx][fpsStage][0];
276     float endOpaCity = OPACITY_ARR[idx][fpsStage][1];
277 
278     float fraction = sharpCurve_->Interpolate(input);
279     float opacity = startOpaCity + (endOpaCity - startOpaCity) * fraction;
280 
281     Rosen::Drawing::Brush brush;
282     brush.SetColor(0xFFFFFFFF);
283     brush.SetAntiAlias(true);
284     brush.SetAlphaF(opacity);
285     return brush;
286 }
287 } // namespace OHOS
288