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