• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 OTA_COMPILE_DISPLAY_INFO_DEFAULT = "正在优化应用";
39     constexpr const int32_t ONE_HUNDRED_PERCENT = 100;
40     constexpr const int32_t SEC_MS = 1000;
41     constexpr const int32_t CIRCLE_NUM = 3;
42     constexpr const float RADIUS = 10.0f;
43     constexpr const float OFFSET_Y_PERCENT = 0.85f;
44     constexpr const float HEIGHT_PERCENT = 0.05f;
45     constexpr const int TEXT_BLOB_OFFSET = 5;
46     constexpr const int FONT_SIZE_VP = 12;
47     constexpr const int32_t MAX_RETRY_TIMES = 5;
48     constexpr const int32_t WAIT_MS = 500;
49     constexpr const int32_t WAITING_BMS_TIMEOUT = 30;
50     constexpr const int32_t LOADING_FPS = 30;
51     constexpr const int32_t PERIOD_FPS = 10;
52     constexpr const int32_t CHANGE_FREQ = 4;
53     constexpr const float TEN_FRAME_TIMES = 10.0f;
54     constexpr const float SHARP_CURVE_CTLX1 = 0.33f;
55     constexpr const float SHARP_CURVE_CTLY1 = 0.0f;
56     constexpr const float SHARP_CURVE_CTLX2 = 0.67f;
57     constexpr const float SHARP_CURVE_CTLY2 = 1.0f;
58     constexpr const float OPACITY_ARR[3][3][2] = {
59         { { 0.2f, 1.0f }, { 1.0f, 0.5f }, { 0.5f, 0.2f } },
60         { { 0.5f, 0.2f }, { 0.2f, 1.0f }, { 1.0f, 0.5f } },
61         { { 1.0f, 0.5f }, { 0.5f, 0.2f }, { 0.2f, 1.0f } },
62     };
63     constexpr const char* BUNDLE_SCAN_PARAM_NAME = "bms.scanning_apps.status";
64     constexpr const int BUNDLE_SCAN_WAITING_TIMEOUT = 3;
65 }
66 
Init(const BootAnimationConfig & config,bool needOtaCompile,bool needBundleScan)67 void BootCompileProgress::Init(const BootAnimationConfig& config, bool needOtaCompile, bool needBundleScan)
68 {
69     LOGI("ota compile, screenId: " BPUBU64 "", config.screenId);
70     needOtaCompile_ = needOtaCompile;
71     needBundleScan_ = needBundleScan;
72     paramNeeded_.clear();
73     if (needOtaCompile_) {
74         paramNeeded_.insert(BMS_COMPILE_STATUS);
75     }
76     if (needBundleScan_) {
77         paramNeeded_.insert(BUNDLE_SCAN_PARAM_NAME);
78     }
79     screenId_ = config.screenId;
80     rotateDegree_ = config.rotateDegree;
81     Rosen::RSInterfaces& interface = Rosen::RSInterfaces::GetInstance();
82     Rosen::RSScreenModeInfo modeInfo = interface.GetScreenActiveMode(config.screenId);
83     windowWidth_ = modeInfo.GetScreenWidth();
84     windowHeight_ = modeInfo.GetScreenHeight();
85     fontSize_ = TransalteVp2Pixel(std::min(windowWidth_, windowHeight_), FONT_SIZE_VP);
86 
87     timeLimitSec_ = system::GetIntParameter<int32_t>(OTA_COMPILE_TIME_LIMIT, OTA_COMPILE_TIME_LIMIT_DEFAULT);
88     tf_ = Rosen::Drawing::Typeface::MakeFromName("HarmonyOS Sans SC", Rosen::Drawing::FontStyle());
89 
90     displayInfo_ = system::GetParameter(OTA_COMPILE_DISPLAY_INFO, OTA_COMPILE_DISPLAY_INFO_DEFAULT);
91     sharpCurve_ = std::make_shared<Rosen::RSCubicBezierInterpolator>(
92         SHARP_CURVE_CTLX1, SHARP_CURVE_CTLY1, SHARP_CURVE_CTLX2, SHARP_CURVE_CTLY2);
93     compileRunner_ = AppExecFwk::EventRunner::Create(false);
94     compileHandler_ = std::make_shared<AppExecFwk::EventHandler>(compileRunner_);
95     compileHandler_->PostTask([this] { this->CreateCanvasNode(); });
96     compileHandler_->PostTask([this] { this->RegisterVsyncCallback(); });
97     compileRunner_->Run();
98 }
99 
CreateCanvasNode()100 bool BootCompileProgress::CreateCanvasNode()
101 {
102     struct Rosen::RSSurfaceNodeConfig surfaceNodeConfig;
103     surfaceNodeConfig.SurfaceNodeName = "BootCompileProgressNode";
104     surfaceNodeConfig.isSync = true;
105     Rosen::RSSurfaceNodeType surfaceNodeType = Rosen::RSSurfaceNodeType::SELF_DRAWING_WINDOW_NODE;
106     rsUIDirector_ = OHOS::Rosen::RSUIDirector::Create();
107     rsUIDirector_->Init(false, false);
108     auto rsUIContext = rsUIDirector_->GetRSUIContext();
109     rsSurfaceNode_ = Rosen::RSSurfaceNode::Create(surfaceNodeConfig, surfaceNodeType, true, false, rsUIContext);
110     if (!rsSurfaceNode_) {
111         LOGE("ota compile, SFNode create failed");
112         compileRunner_->Stop();
113         return false;
114     }
115     float positionZ = MAX_ZORDER + 1;
116     rsSurfaceNode_->SetRotation(rotateDegree_);
117     rsSurfaceNode_->SetPositionZ(positionZ);
118     rsSurfaceNode_->SetBounds(0, 0, windowWidth_, windowHeight_);
119     rsSurfaceNode_->SetBackgroundColor(SK_ColorTRANSPARENT);
120     rsSurfaceNode_->SetFrameGravity(Rosen::Gravity::RESIZE_ASPECT);
121     rsSurfaceNode_->SetBootAnimation(true);
122     Rosen::RSTransaction::FlushImplicitTransaction();
123     rsSurfaceNode_->AttachToDisplay(screenId_);
124     Rosen::RSTransaction::FlushImplicitTransaction();
125 
126     rsCanvasNode_ = Rosen::RSCanvasNode::Create(true, false, rsUIContext);
127     rsCanvasNode_->SetBounds(0, 0, windowWidth_, windowHeight_);
128     rsCanvasNode_->SetFrame(0, windowHeight_ * OFFSET_Y_PERCENT, windowWidth_, windowHeight_ * HEIGHT_PERCENT);
129     rsCanvasNode_->SetBackgroundColor(Rosen::Drawing::Color::COLOR_TRANSPARENT);
130     rsCanvasNode_->SetPositionZ(positionZ);
131     rsSurfaceNode_->AddChild(rsCanvasNode_, 0);
132 
133     LOGI("CreateCanvasNode success");
134     return true;
135 }
136 
RegisterVsyncCallback()137 bool BootCompileProgress::RegisterVsyncCallback()
138 {
139     if (CheckParams()) {
140         LOGI("all param status are COMPLETED");
141         compileRunner_->Stop();
142         return false;
143     }
144     if (!WaitParamsIfNeeded()) {
145         LOGI("no param is ready, progress bar stop");
146         compileRunner_->Stop();
147         return false;
148     }
149 
150     auto& rsClient = Rosen::RSInterfaces::GetInstance();
151     int32_t retry = 0;
152     while (receiver_ == nullptr) {
153         if (retry++ > MAX_RETRY_TIMES) {
154             LOGE("get vsync receiver failed");
155             compileRunner_->Stop();
156             return false;
157         }
158         if (retry > 1) {
159             std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_MS));
160         }
161         receiver_ = rsClient.CreateVSyncReceiver("BootCompileProgress", compileHandler_);
162     }
163     VsyncError ret = receiver_->Init();
164     if (ret) {
165         compileRunner_->Stop();
166         LOGE("init vsync receiver failed");
167         return false;
168     }
169 
170     Rosen::VSyncReceiver::FrameCallback fcb = {
171         .userData_ = this,
172         .callback_ = [this](int64_t, void*) { this->OnVsync(); },
173     };
174     ret = receiver_->SetVSyncRate(fcb, CHANGE_FREQ);
175     if (ret) {
176         compileRunner_->Stop();
177         LOGE("set vsync rate failed");
178     }
179 
180     startTimeMs_ = std::chrono::duration_cast<std::chrono::milliseconds>(
181         std::chrono::system_clock::now().time_since_epoch()).count();
182     endTimePredictMs_ = startTimeMs_ + timeLimitSec_ * SEC_MS;
183 
184     LOGI("RegisterVsyncCallback success");
185     return true;
186 }
187 
CheckParams()188 bool BootCompileProgress::CheckParams()
189 {
190     bool check1 = CheckBmsStartParam();
191     bool check2 = CheckBundleScanParam();
192     if (check1 && check2) {
193         return true;
194     }
195     return false;
196 }
197 
CheckBundleScanParam()198 bool BootCompileProgress::CheckBundleScanParam()
199 {
200     if (!needBundleScan_) {
201         return true;
202     }
203     if (system::GetParameter(BUNDLE_SCAN_PARAM_NAME, "-1") == "1") {
204         paramNeeded_.erase(BUNDLE_SCAN_PARAM_NAME);
205         return true;
206     }
207     return false;
208 }
209 
CheckBmsStartParam()210 bool BootCompileProgress::CheckBmsStartParam()
211 {
212     if (!needOtaCompile_) {
213         return true;
214     }
215     if (system::GetParameter(BMS_COMPILE_STATUS, "-1") == BMS_COMPILE_STATUS_END) {
216         paramNeeded_.erase(BMS_COMPILE_STATUS);
217         return true;
218     }
219     return false;
220 }
221 
WaitParamsIfNeeded()222 bool BootCompileProgress::WaitParamsIfNeeded()
223 {
224     bool wait1 = WaitBmsStartIfNeeded();
225     bool wait2 = WaitBundleScanIfNeeded();
226     if (!wait1 && !wait2) {
227         return false;
228     }
229     return true;
230 }
231 
WaitBundleScanIfNeeded()232 bool BootCompileProgress::WaitBundleScanIfNeeded()
233 {
234     if (!needBundleScan_) {
235         return true;
236     }
237     if (WaitParameter(BUNDLE_SCAN_PARAM_NAME, "0", BUNDLE_SCAN_WAITING_TIMEOUT) != 0) {
238         paramNeeded_.erase(BUNDLE_SCAN_PARAM_NAME);
239         LOGE("waiting bundle scan failed.");
240         return false;
241     }
242     return true;
243 }
244 
WaitBmsStartIfNeeded()245 bool BootCompileProgress::WaitBmsStartIfNeeded()
246 {
247     if (!needOtaCompile_) {
248         return true;
249     }
250     if (WaitParameter(BMS_COMPILE_STATUS, BMS_COMPILE_STATUS_BEGIN.c_str(), WAITING_BMS_TIMEOUT) != 0) {
251         paramNeeded_.erase(BMS_COMPILE_STATUS);
252         LOGE("waiting bms start oat compile failed.");
253         return false;
254     }
255     return true;
256 }
257 
OnVsync()258 void BootCompileProgress::OnVsync()
259 {
260     if (!isUpdateOptEnd_) {
261         compileHandler_->PostTask([this] { this->DrawCompileProgress(); });
262     } else {
263         LOGI("ota compile completed");
264         compileRunner_->Stop();
265     }
266 }
267 
DrawCompileProgress()268 void BootCompileProgress::DrawCompileProgress()
269 {
270     UpdateCompileProgress();
271 
272     auto canvas = static_cast<Rosen::Drawing::RecordingCanvas*>(
273         rsCanvasNode_->BeginRecording(windowWidth_, windowHeight_ * HEIGHT_PERCENT));
274     if (canvas == nullptr) {
275         LOGE("DrawCompileProgress canvas is null");
276         return;
277     }
278 
279     Rosen::Drawing::Font font;
280     font.SetTypeface(tf_);
281     font.SetSize(fontSize_);
282     char info[64] = {0};
283     int ret = sprintf_s(info, sizeof(info), "%s %d%%", displayInfo_.c_str(), progress_);
284     if (ret == -1) {
285         LOGE("set info failed");
286         return;
287     }
288     std::shared_ptr<Rosen::Drawing::TextBlob> textBlob = Rosen::Drawing::TextBlob::MakeFromString(info, font);
289     auto textBound = textBlob->Bounds();
290     if (textBound == nullptr) {
291         LOGE("textBound is null");
292         return;
293     }
294 
295     Rosen::Drawing::Brush whiteBrush;
296     whiteBrush.SetColor(0xFFFFFFFF);
297     whiteBrush.SetAntiAlias(true);
298     canvas->AttachBrush(whiteBrush);
299 
300     auto textWidth = font.MeasureText(info, strlen(info), Rosen::Drawing::TextEncoding::UTF8, nullptr);
301     float scalarX = windowWidth_ / NUMBER_TWO - textWidth / NUMBER_TWO;
302     float scalarY = TEXT_BLOB_OFFSET + textBound->GetHeight() / NUMBER_TWO;
303     canvas->DrawTextBlob(textBlob.get(), scalarX, scalarY);
304     canvas->DetachBrush();
305 
306     Rosen::Drawing::Brush grayBrush;
307     grayBrush.SetColor(0x40FFFFFF);
308     grayBrush.SetAntiAlias(true);
309 
310     int32_t freqNum = times_++;
311     for (int i = 0; i < CIRCLE_NUM; i++) {
312         canvas->AttachBrush(DrawProgressPoint(i, freqNum));
313         int pointX = windowWidth_/2.0f + 4 * RADIUS * (i - 1); // align point in center and 2RADUIS between two points
314         int pointY = rsCanvasNode_->GetPaintHeight() - 2 * RADIUS;
315         canvas->DrawCircle({pointX, pointY}, RADIUS);
316         canvas->DetachBrush();
317     }
318 
319     rsCanvasNode_->FinishRecording();
320     Rosen::RSTransaction::FlushImplicitTransaction();
321 
322     if (progress_ >= ONE_HUNDRED_PERCENT) {
323         isUpdateOptEnd_ = true;
324     }
325 }
326 
UpdateCompileProgress()327 void BootCompileProgress::UpdateCompileProgress()
328 {
329     (void)CheckParams();
330     if (paramNeeded_.size() > 0) {
331         int64_t now =
332             std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch())
333             .count();
334         if (endTimePredictMs_ < now) {
335             progress_ = ONE_HUNDRED_PERCENT;
336             return;
337         }
338         if (!timeLimitSec_) {
339             return;
340         }
341         progress_ = (int32_t)((now - startTimeMs_) * ONE_HUNDRED_PERCENT / (timeLimitSec_ * SEC_MS));
342         progress_ = progress_ < 0 ? 0 : progress_ > ONE_HUNDRED_PERCENT ? ONE_HUNDRED_PERCENT: progress_;
343     } else {
344         progress_++;
345     }
346 
347     LOGD("update progress: %{public}d", progress_);
348 }
349 
DrawProgressPoint(int32_t idx,int32_t frameNum)350 Rosen::Drawing::Brush BootCompileProgress::DrawProgressPoint(int32_t idx, int32_t frameNum)
351 {
352     int32_t fpsNo = frameNum % LOADING_FPS;
353     int32_t fpsStage = fpsNo / PERIOD_FPS;
354     float input  = fpsNo % PERIOD_FPS / TEN_FRAME_TIMES;
355 
356     float startOpaCity = OPACITY_ARR[idx][fpsStage][0];
357     float endOpaCity = OPACITY_ARR[idx][fpsStage][1];
358 
359     float fraction = sharpCurve_->Interpolate(input);
360     float opacity = startOpaCity + (endOpaCity - startOpaCity) * fraction;
361 
362     Rosen::Drawing::Brush brush;
363     brush.SetColor(0xFFFFFFFF);
364     brush.SetAntiAlias(true);
365     brush.SetAlphaF(opacity);
366     return brush;
367 }
368 } // namespace OHOS
369