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 "ui/rs_ui_context_manager.h"
31 #include "util.h"
32
33 namespace OHOS {
34 namespace {
35 constexpr const char* OTA_COMPILE_TIME_LIMIT = "persist.bms.optimizing_apps.timing";
36 constexpr int32_t OTA_COMPILE_TIME_LIMIT_DEFAULT = 4 * 60;
37 constexpr const char* OTA_COMPILE_DISPLAY_INFO = "const.bms.optimizing_apps.display_info";
38 const std::string BOOTEVENT_BMS_MAIN_BUNDLES_READY = "bootevent.bms.main.bundles.ready";
39 const std::string OTA_COMPILE_DISPLAY_INFO_DEFAULT = "正在优化应用";
40 const std::string OTA_COMPILE_DISPLAY_INFO_OVERSEA = "Optimizing Apps";
41 constexpr const char* REGION_PARA_STR_CUST = "const.cust.region";
42 constexpr const char* CHINA_REGION = "cn";
43 constexpr const int32_t ONE_HUNDRED_PERCENT = 100;
44 constexpr const int32_t SEC_MS = 1000;
45 constexpr const int32_t CIRCLE_NUM = 3;
46 constexpr const float RADIUS = 3.0f;
47 constexpr const float RADIUS_WEARABLE = 5.0f;
48 constexpr const float OFFSET_Y_PERCENT = 0.2f;
49 constexpr const float HEIGHT_PERCENT = 0.05f;
50 constexpr const int TEXT_BLOB_OFFSET = 5;
51 constexpr const int FONT_SIZE_PHONE = 12;
52 constexpr const int FONT_SIZE_OTHER = 24;
53 constexpr const int FONT_SIZE_WEARABLE = 24;
54 constexpr const int OFFSET_Y_WEARABLE = 120;
55 constexpr const int MAGIN_WEARABLE = 12;
56 constexpr const int HEIGHT_WEARABLE = FONT_SIZE_WEARABLE + RADIUS_WEARABLE * 2 + MAGIN_WEARABLE;
57 constexpr const int32_t MAX_RETRY_TIMES = 5;
58 constexpr const int32_t WAIT_MS = 500;
59 constexpr const int32_t LOADING_FPS = 30;
60 constexpr const int32_t PERIOD_FPS = 10;
61 constexpr const int32_t CHANGE_FREQ = 4;
62 constexpr const float TEN_FRAME_TIMES = 10.0f;
63 constexpr const float SHARP_CURVE_CTLX1 = 0.33f;
64 constexpr const float SHARP_CURVE_CTLY1 = 0.0f;
65 constexpr const float SHARP_CURVE_CTLX2 = 0.67f;
66 constexpr const float SHARP_CURVE_CTLY2 = 1.0f;
67 constexpr const float OPACITY_ARR[3][3][2] = {
68 { { 0.2f, 1.0f }, { 1.0f, 0.5f }, { 0.5f, 0.2f } },
69 { { 0.5f, 0.2f }, { 0.2f, 1.0f }, { 1.0f, 0.5f } },
70 { { 1.0f, 0.5f }, { 0.5f, 0.2f }, { 0.2f, 1.0f } },
71 };
72 constexpr const int DOUBLE_TIMES = 2;
73 }
74
Init(const std::string & configPath,const BootAnimationConfig & config)75 void BootCompileProgress::Init(const std::string& configPath, const BootAnimationConfig& config)
76 {
77 LOGI("ota compile, screenId: " BPUBU64 "", config.screenId);
78 RecordDeviceType();
79 screenId_ = config.screenId;
80 rotateDegree_ = config.rotateDegree;
81 if (!config.screenStatus.empty()) {
82 screenStatus_ = std::atoi(config.screenStatus.c_str());
83 }
84 ParseProgressConfig(configPath, progressConfigsMap_);
85 Rosen::RSInterfaces& interface = Rosen::RSInterfaces::GetInstance();
86 Rosen::RSScreenModeInfo modeInfo = interface.GetScreenActiveMode(config.screenId);
87 windowWidth_ = modeInfo.GetScreenWidth();
88 windowHeight_ = modeInfo.GetScreenHeight();
89 fontSize_ = isWearable_ ? FONT_SIZE_WEARABLE :
90 TranslateVp2Pixel(std::min(windowWidth_, windowHeight_), isOther_ ? FONT_SIZE_OTHER : FONT_SIZE_PHONE);
91 currentRadius_ = isWearable_ ? RADIUS_WEARABLE :
92 TranslateVp2Pixel(std::min(windowWidth_, windowHeight_), isOther_ ? RADIUS * DOUBLE_TIMES : RADIUS);
93
94 timeLimitSec_ = system::GetIntParameter<int32_t>(OTA_COMPILE_TIME_LIMIT, OTA_COMPILE_TIME_LIMIT_DEFAULT);
95 tf_ = Rosen::Drawing::Typeface::MakeFromName("HarmonyOS Sans SC", Rosen::Drawing::FontStyle());
96
97 std::string defaultDisplayInfo = OTA_COMPILE_DISPLAY_INFO_DEFAULT;
98 if (system::GetParameter(REGION_PARA_STR_CUST, CHINA_REGION) != CHINA_REGION) {
99 defaultDisplayInfo = OTA_COMPILE_DISPLAY_INFO_OVERSEA;
100 }
101 displayInfo_ = system::GetParameter(OTA_COMPILE_DISPLAY_INFO, defaultDisplayInfo);
102 sharpCurve_ = std::make_shared<Rosen::RSCubicBezierInterpolator>(
103 SHARP_CURVE_CTLX1, SHARP_CURVE_CTLY1, SHARP_CURVE_CTLX2, SHARP_CURVE_CTLY2);
104 compileRunner_ = AppExecFwk::EventRunner::Create(false);
105 compileHandler_ = std::make_shared<AppExecFwk::EventHandler>(compileRunner_);
106 compileHandler_->PostTask([this] { this->CreateCanvasNode(); });
107 compileHandler_->PostTask([this] { this->RegisterVsyncCallback(); });
108 compileRunner_->Run();
109 }
110
CreateCanvasNode()111 bool BootCompileProgress::CreateCanvasNode()
112 {
113 struct Rosen::RSSurfaceNodeConfig surfaceNodeConfig;
114 surfaceNodeConfig.SurfaceNodeName = "BootCompileProgressNode";
115 surfaceNodeConfig.isSync = true;
116 Rosen::RSSurfaceNodeType surfaceNodeType = Rosen::RSSurfaceNodeType::SELF_DRAWING_WINDOW_NODE;
117 rsUIDirector_ = OHOS::Rosen::RSUIDirector::Create();
118 rsUIDirector_->Init(false, false);
119 auto rsUIContext = rsUIDirector_->GetRSUIContext();
120 rsSurfaceNode_ = Rosen::RSSurfaceNode::Create(surfaceNodeConfig, surfaceNodeType, true, false, rsUIContext);
121 if (!rsSurfaceNode_) {
122 LOGE("ota compile, SFNode create failed");
123 compileRunner_->Stop();
124 return false;
125 }
126 float positionZ = MAX_ZORDER + 1;
127 rsSurfaceNode_->SetRotation(rotateDegree_);
128 rsSurfaceNode_->SetPositionZ(positionZ);
129 rsSurfaceNode_->SetBounds(0, 0, windowWidth_, windowHeight_);
130 rsSurfaceNode_->SetBackgroundColor(SK_ColorTRANSPARENT);
131 rsSurfaceNode_->SetFrameGravity(Rosen::Gravity::RESIZE_ASPECT);
132 rsSurfaceNode_->SetBootAnimation(true);
133 Rosen::RSTransaction::FlushImplicitTransaction();
134 rsSurfaceNode_->AttachToDisplay(screenId_);
135 Rosen::RSTransaction::FlushImplicitTransaction();
136
137 rsCanvasNode_ = Rosen::RSCanvasNode::Create(true, false, rsUIContext);
138 rsCanvasNode_->SetBounds(0, 0, windowWidth_, windowHeight_);
139 SetFrame();
140 rsCanvasNode_->SetBackgroundColor(Rosen::Drawing::Color::COLOR_TRANSPARENT);
141 rsCanvasNode_->SetPositionZ(positionZ);
142 rsSurfaceNode_->AddChild(rsCanvasNode_, 0);
143
144 LOGI("CreateCanvasNode success");
145 return true;
146 }
147
RegisterVsyncCallback()148 bool BootCompileProgress::RegisterVsyncCallback()
149 {
150 if (IsBmsBundleReady()) {
151 LOGI("bms bundle is already done.");
152 compileRunner_->Stop();
153 return false;
154 }
155
156 auto& rsClient = Rosen::RSInterfaces::GetInstance();
157 int32_t retry = 0;
158 while (receiver_ == nullptr) {
159 if (retry++ > MAX_RETRY_TIMES) {
160 LOGE("get vsync receiver failed");
161 compileRunner_->Stop();
162 return false;
163 }
164 if (retry > 1) {
165 std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_MS));
166 }
167 receiver_ = rsClient.CreateVSyncReceiver("BootCompileProgress", compileHandler_);
168 }
169 VsyncError ret = receiver_->Init();
170 if (ret) {
171 compileRunner_->Stop();
172 LOGE("init vsync receiver failed");
173 return false;
174 }
175
176 Rosen::VSyncReceiver::FrameCallback fcb = {
177 .userData_ = this,
178 .callback_ = [this](int64_t, void*) { this->OnVsync(); },
179 };
180 ret = receiver_->SetVSyncRate(fcb, CHANGE_FREQ);
181 if (ret) {
182 compileRunner_->Stop();
183 LOGE("set vsync rate failed");
184 }
185
186 startTimeMs_ = GetSystemCurrentTime();
187 endTimePredictMs_ = startTimeMs_ + timeLimitSec_ * SEC_MS;
188
189 LOGI("RegisterVsyncCallback success");
190 return true;
191 }
192
IsBmsBundleReady()193 bool BootCompileProgress::IsBmsBundleReady()
194 {
195 return system::GetBoolParameter(BOOTEVENT_BMS_MAIN_BUNDLES_READY, false);
196 }
197
OnVsync()198 void BootCompileProgress::OnVsync()
199 {
200 if (!isUpdateOptEnd_) {
201 compileHandler_->PostTask([this] { this->DrawCompileProgress(); });
202 } else {
203 LOGI("ota compile completed");
204 compileRunner_->Stop();
205 }
206 }
207
DrawCompileProgress()208 void BootCompileProgress::DrawCompileProgress()
209 {
210 UpdateCompileProgress();
211
212 auto canvas = static_cast<Rosen::Drawing::RecordingCanvas*>(rsCanvasNode_->BeginRecording(windowWidth_,
213 isWearable_ ? HEIGHT_WEARABLE : std::max(windowWidth_, windowHeight_) * HEIGHT_PERCENT));
214 if (canvas == nullptr) {
215 LOGE("DrawCompileProgress canvas is null");
216 return;
217 }
218
219 Rosen::Drawing::Font font;
220 font.SetTypeface(tf_);
221 font.SetSize(fontSize_);
222 char info[64] = {0};
223 int ret = sprintf_s(info, sizeof(info), "%s %d%%", displayInfo_.c_str(), progress_);
224 if (ret == -1) {
225 LOGE("set info failed");
226 return;
227 }
228 std::shared_ptr<Rosen::Drawing::TextBlob> textBlob = Rosen::Drawing::TextBlob::MakeFromString(info, font);
229 auto textBound = textBlob->Bounds();
230 if (textBound == nullptr) {
231 LOGE("textBound is null");
232 return;
233 }
234
235 Rosen::Drawing::Brush whiteBrush;
236 whiteBrush.SetColor(0xFFFFFFFF);
237 whiteBrush.SetAntiAlias(true);
238 canvas->AttachBrush(whiteBrush);
239
240 auto textWidth = font.MeasureText(info, strlen(info), Rosen::Drawing::TextEncoding::UTF8, nullptr);
241 float scalarX = windowWidth_ / NUMBER_TWO - textWidth / NUMBER_TWO;
242 float scalarY = TEXT_BLOB_OFFSET + textBound->GetHeight() / NUMBER_TWO;
243 canvas->DrawTextBlob(textBlob.get(), scalarX, scalarY);
244 canvas->DetachBrush();
245
246 DrawMarginBrush(canvas);
247
248 int32_t freqNum = times_++;
249 for (int i = 0; i < CIRCLE_NUM; i++) {
250 canvas->AttachBrush(DrawProgressPoint(i, freqNum));
251 int pointX = windowWidth_/2.0f + 4 * currentRadius_ * (i - 1);
252 int pointY = rsCanvasNode_->GetPaintHeight() - currentRadius_;
253 canvas->DrawCircle({pointX, pointY}, currentRadius_);
254 canvas->DetachBrush();
255 }
256
257 rsCanvasNode_->FinishRecording();
258 Rosen::RSTransaction::FlushImplicitTransaction();
259
260 if (progress_ >= ONE_HUNDRED_PERCENT) {
261 isUpdateOptEnd_ = true;
262 }
263 }
264
DrawMarginBrush(Rosen::Drawing::RecordingCanvas * canvas)265 void BootCompileProgress::DrawMarginBrush(Rosen::Drawing::RecordingCanvas* canvas)
266 {
267 if (isWearable_) {
268 Rosen::Drawing::Brush marginBrush;
269 marginBrush.SetColor(0x00000000);
270 marginBrush.SetAntiAlias(true);
271 Rosen::Drawing::Rect rect(0, FONT_SIZE_WEARABLE, windowWidth_, FONT_SIZE_WEARABLE + MAGIN_WEARABLE);
272 canvas->AttachBrush(marginBrush);
273 canvas->DrawRect(rect);
274 canvas->DetachBrush();
275 }
276 }
277
UpdateCompileProgress()278 void BootCompileProgress::UpdateCompileProgress()
279 {
280 if (!IsBmsBundleReady()) {
281 int64_t now = GetSystemCurrentTime();
282 if (endTimePredictMs_ < now) {
283 progress_ = ONE_HUNDRED_PERCENT;
284 return;
285 }
286 if (!timeLimitSec_) {
287 return;
288 }
289 progress_ = (int32_t)((now - startTimeMs_) * ONE_HUNDRED_PERCENT / (timeLimitSec_ * SEC_MS));
290 progress_ = progress_ < 0 ? 0 : progress_ > ONE_HUNDRED_PERCENT ? ONE_HUNDRED_PERCENT: progress_;
291 } else {
292 progress_++;
293 }
294
295 LOGD("update progress: %{public}d", progress_);
296 }
297
DrawProgressPoint(int32_t idx,int32_t frameNum)298 Rosen::Drawing::Brush BootCompileProgress::DrawProgressPoint(int32_t idx, int32_t frameNum)
299 {
300 int32_t fpsNo = frameNum % LOADING_FPS;
301 int32_t fpsStage = fpsNo / PERIOD_FPS;
302 float input = fpsNo % PERIOD_FPS / TEN_FRAME_TIMES;
303
304 float startOpaCity = OPACITY_ARR[idx][fpsStage][0];
305 float endOpaCity = OPACITY_ARR[idx][fpsStage][1];
306
307 float fraction = sharpCurve_->Interpolate(input);
308 float opacity = startOpaCity + (endOpaCity - startOpaCity) * fraction;
309
310 Rosen::Drawing::Brush brush;
311 brush.SetColor(0xFFFFFFFF);
312 brush.SetAntiAlias(true);
313 brush.SetAlphaF(opacity);
314 return brush;
315 }
316
RecordDeviceType()317 void BootCompileProgress::RecordDeviceType()
318 {
319 std::string deviceType = GetDeviceType();
320 LOGI("deviceType: %{public}s", deviceType.c_str());
321 if (deviceType == DEVICE_TYPE_WEARABLE) {
322 isWearable_ = true;
323 } else if (deviceType != DEVICE_TYPE_PHONE) {
324 isOther_ = true;
325 }
326 }
327
SetSpecialProgressFrame(int32_t maxLength,int32_t screenId)328 void BootCompileProgress::SetSpecialProgressFrame(int32_t maxLength, int32_t screenId)
329 {
330 BootAnimationProgressConfig progressConfig = progressConfigsMap_.find(screenId)->second;
331 float positionX = progressConfig.progressOffset == -1 ? 0 : progressConfig.progressOffset;
332 float positionY = progressConfig.progressHeight == -1 ? windowHeight_ - maxLength * OFFSET_Y_PERCENT
333 : progressConfig.progressHeight;
334 float frameHeight = progressConfig.progressFrameHeight == -1 ? maxLength * HEIGHT_PERCENT
335 : progressConfig.progressFrameHeight;
336 rsCanvasNode_->SetFrame(positionX, positionY, windowWidth_, frameHeight);
337 fontSize_ = progressConfig.progressFontSize == -1 ? fontSize_ : progressConfig.progressFontSize;
338 currentRadius_ = progressConfig.progressRadiusSize == -1 ? currentRadius_ : progressConfig.progressRadiusSize;
339 if (progressConfig.progressDegree > 0) {
340 rsCanvasNode_->SetRotation(progressConfig.progressDegree);
341 }
342 }
343
SetFrame()344 void BootCompileProgress::SetFrame()
345 {
346 int32_t maxLength = std::max(windowWidth_, windowHeight_);
347 int32_t tempScreenId = screenStatus_ == -1 ? static_cast<int32_t> (screenId_) : screenStatus_;
348 if (!progressConfigsMap_.empty() && progressConfigsMap_.find(tempScreenId) != progressConfigsMap_.end()) {
349 SetSpecialProgressFrame(maxLength, tempScreenId);
350 } else if (isWearable_) {
351 rsCanvasNode_->SetFrame(0, windowHeight_ - OFFSET_Y_WEARABLE - HEIGHT_WEARABLE, windowWidth_, HEIGHT_WEARABLE);
352 } else {
353 rsCanvasNode_->SetFrame(0, windowHeight_ - maxLength * OFFSET_Y_PERCENT, windowWidth_,
354 maxLength * HEIGHT_PERCENT);
355 }
356 }
357 } // namespace OHOS
358