1 /*
2 * Copyright (c) 2023 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 #include "session_backup_n_exporter.h"
16
17 #include <functional>
18 #include <memory>
19
20 #include "b_error/b_error.h"
21 #include "b_filesystem/b_file.h"
22 #include "b_resources/b_constants.h"
23 #include "b_session_backup.h"
24 #include "backup_kit_inner.h"
25 #include "directory_ex.h"
26 #include "filemgmt_libhilog.h"
27 #include "general_callbacks.h"
28 #include "service_proxy.h"
29
30 namespace OHOS::FileManagement::Backup {
31 using namespace std;
32 using namespace LibN;
33
34 struct BackupEntity {
35 unique_ptr<BSessionBackup> session;
36 shared_ptr<GeneralCallbacks> callbacks;
37 };
38
OnFileReady(weak_ptr<GeneralCallbacks> pCallbacks,const BFileInfo & fileInfo,UniqueFd fd)39 static void OnFileReady(weak_ptr<GeneralCallbacks> pCallbacks, const BFileInfo &fileInfo, UniqueFd fd)
40 {
41 if (pCallbacks.expired()) {
42 HILOGI("callbacks is unbound");
43 return;
44 }
45 auto callbacks = pCallbacks.lock();
46 if (!callbacks) {
47 HILOGI("callback function onFileReady has already been released");
48 return;
49 }
50 if (!bool(callbacks->onFileReady)) {
51 HILOGI("callback function onFileReady is undefined");
52 return;
53 }
54
55 auto cbCompl = [bundleName {fileInfo.owner}, fileName {fileInfo.fileName},
56 fd {make_shared<UniqueFd>(fd.Release())}](napi_env env, NError err) -> NVal {
57 if (err) {
58 return {env, err.GetNapiErr(env)};
59 }
60 NVal obj = NVal::CreateObject(env);
61 obj.AddProp({NVal::DeclareNapiProperty("bundleName", NVal::CreateUTF8String(env, bundleName).val_),
62 NVal::DeclareNapiProperty("uri", NVal::CreateUTF8String(env, fileName).val_),
63 NVal::DeclareNapiProperty("fd", NVal::CreateInt32(env, fd->Release()).val_)});
64
65 return {obj};
66 };
67
68 callbacks->onFileReady.ThreadSafeSchedule(cbCompl);
69 }
70
onBundleBegin(weak_ptr<GeneralCallbacks> pCallbacks,ErrCode err,const BundleName name)71 static void onBundleBegin(weak_ptr<GeneralCallbacks> pCallbacks, ErrCode err, const BundleName name)
72 {
73 if (pCallbacks.expired()) {
74 HILOGI("callbacks is unbound");
75 return;
76 }
77 auto callbacks = pCallbacks.lock();
78 if (!callbacks) {
79 HILOGI("callback function onBundleBegin has already been released");
80 return;
81 }
82 if (!bool(callbacks->onBundleBegin)) {
83 HILOGI("callback function onBundleBegin is undefined");
84 return;
85 }
86
87 auto cbCompl = [name {name}](napi_env env, NError err) -> NVal {
88 return err ? NVal {env, err.GetNapiErr(env)} : NVal::CreateUTF8String(env, name);
89 };
90
91 callbacks->onBundleBegin.ThreadSafeSchedule(cbCompl);
92 }
93
onBundleEnd(weak_ptr<GeneralCallbacks> pCallbacks,ErrCode err,const BundleName name)94 static void onBundleEnd(weak_ptr<GeneralCallbacks> pCallbacks, ErrCode err, const BundleName name)
95 {
96 if (pCallbacks.expired()) {
97 HILOGI("callbacks is unbound");
98 return;
99 }
100 auto callbacks = pCallbacks.lock();
101 if (!callbacks) {
102 HILOGI("callback function onBundleEnd has already been released");
103 return;
104 }
105 if (!bool(callbacks->onBundleEnd)) {
106 HILOGI("callback function onBundleEnd is undefined");
107 return;
108 }
109
110 auto cbCompl = [name {name}](napi_env env, NError err) -> NVal {
111 return err ? NVal {env, err.GetNapiErr(env)} : NVal::CreateUTF8String(env, name);
112 };
113
114 callbacks->onBundleEnd.ThreadSafeSchedule(cbCompl);
115 }
116
onAllBundlesEnd(weak_ptr<GeneralCallbacks> pCallbacks,ErrCode err)117 static void onAllBundlesEnd(weak_ptr<GeneralCallbacks> pCallbacks, ErrCode err)
118 {
119 if (pCallbacks.expired()) {
120 HILOGI("callbacks is unbound");
121 return;
122 }
123 auto callbacks = pCallbacks.lock();
124 if (!callbacks) {
125 HILOGI("callback function onAllBundlesEnd has already been released");
126 return;
127 }
128 if (!bool(callbacks->onAllBundlesEnd)) {
129 HILOGI("callback function onAllBundlesEnd is undefined");
130 return;
131 }
132
133 auto cbCompl = [](napi_env env, NError err) -> NVal {
134 return err ? NVal {env, err.GetNapiErr(env)} : NVal::CreateUndefined(env);
135 };
136
137 callbacks->onAllBundlesEnd.ThreadSafeSchedule(cbCompl);
138 }
139
OnBackupServiceDied(weak_ptr<GeneralCallbacks> pCallbacks)140 static void OnBackupServiceDied(weak_ptr<GeneralCallbacks> pCallbacks)
141 {
142 if (pCallbacks.expired()) {
143 HILOGI("callbacks is unbound");
144 return;
145 }
146 auto callbacks = pCallbacks.lock();
147 if (!callbacks) {
148 HILOGI("js callback function onBackupServiceDied has already been released");
149 return;
150 }
151 if (!bool(callbacks->onBackupServiceDied)) {
152 HILOGI("callback function onBackupServiceDied is undefined");
153 return;
154 }
155
156 auto cbCompl = [](napi_env env, NError err) -> NVal {
157 return err ? NVal {env, err.GetNapiErr(env)} : NVal::CreateUndefined(env);
158 };
159
160 callbacks->onBackupServiceDied.ThreadSafeSchedule(cbCompl);
161 }
162
Constructor(napi_env env,napi_callback_info cbinfo)163 napi_value SessionBackupNExporter::Constructor(napi_env env, napi_callback_info cbinfo)
164 {
165 HILOGI("called SessionBackup::Constructor begin");
166 NFuncArg funcArg(env, cbinfo);
167 if (!funcArg.InitArgs(NARG_CNT::ONE)) {
168 HILOGE("Number of arguments unmatched");
169 NError(EINVAL).ThrowErr(env);
170 return nullptr;
171 }
172
173 NVal callbacks(env, funcArg[NARG_POS::FIRST]);
174 if (!callbacks.TypeIs(napi_object)) {
175 HILOGE("First argument is not an object.");
176 NError(EINVAL).ThrowErr(env);
177 return nullptr;
178 }
179
180 NVal ptr(env, funcArg.GetThisVar());
181 auto backupEntity = std::make_unique<BackupEntity>();
182 backupEntity->callbacks = make_shared<GeneralCallbacks>(env, ptr, callbacks);
183 backupEntity->session = BSessionBackup::Init(BSessionBackup::Callbacks {
184 .onFileReady = bind(OnFileReady, backupEntity->callbacks, placeholders::_1, placeholders::_2),
185 .onBundleStarted = bind(onBundleBegin, backupEntity->callbacks, placeholders::_1, placeholders::_2),
186 .onBundleFinished = bind(onBundleEnd, backupEntity->callbacks, placeholders::_1, placeholders::_2),
187 .onAllBundlesFinished = bind(onAllBundlesEnd, backupEntity->callbacks, placeholders::_1),
188 .onBackupServiceDied = bind(OnBackupServiceDied, backupEntity->callbacks)});
189 if (!backupEntity->session) {
190 NError(BError(BError::Codes::SDK_INVAL_ARG, "Failed to init backup").GetCode()).ThrowErr(env);
191 return nullptr;
192 }
193 if (!NClass::SetEntityFor<BackupEntity>(env, funcArg.GetThisVar(), move(backupEntity))) {
194 HILOGE("Failed to set BackupEntity entity");
195 NError(EINVAL).ThrowErr(env);
196 return nullptr;
197 }
198
199 HILOGI("called SessionBackup::Constructor end");
200 return funcArg.GetThisVar();
201 }
202
AppendBundles(napi_env env,napi_callback_info cbinfo)203 napi_value SessionBackupNExporter::AppendBundles(napi_env env, napi_callback_info cbinfo)
204 {
205 HILOGI("called SessionBackup::AppendBundles begin");
206 NFuncArg funcArg(env, cbinfo);
207 if (!funcArg.InitArgs(NARG_CNT::ONE, NARG_CNT::TWO)) {
208 HILOGE("Number of arguments unmatched");
209 NError(EINVAL).ThrowErr(env);
210 return nullptr;
211 }
212
213 NVal jsBundles(env, funcArg[NARG_POS::FIRST]);
214 auto [succ, bundles, ignore] = jsBundles.ToStringArray();
215 if (!succ) {
216 HILOGE("First argument is not bundles array.");
217 NError(EINVAL).ThrowErr(env);
218 return nullptr;
219 }
220
221 auto backupEntity = NClass::GetEntityOf<BackupEntity>(env, funcArg.GetThisVar());
222 if (!(backupEntity && backupEntity->session)) {
223 HILOGE("Failed to get backupSession entity.");
224 NError(EPERM).ThrowErr(env);
225 return nullptr;
226 }
227
228 auto cbExec = [session {backupEntity->session.get()}, bundles {bundles}]() -> NError {
229 if (!session) {
230 return NError(BError(BError::Codes::SDK_INVAL_ARG, "backup session is nullptr").GetCode());
231 }
232 return NError(session->AppendBundles(bundles));
233 };
234 auto cbCompl = [](napi_env env, NError err) -> NVal {
235 return err ? NVal {env, err.GetNapiErr(env)} : NVal::CreateUndefined(env);
236 };
237
238 HILOGE("Called SessionBackup::AppendBundles end.");
239
240 NVal thisVar(env, funcArg.GetThisVar());
241 if (funcArg.GetArgc() == NARG_CNT::ONE) {
242 return NAsyncWorkPromise(env, thisVar).Schedule(className, cbExec, cbCompl).val_;
243 } else {
244 NVal cb(env, funcArg[NARG_POS::SECOND]);
245 return NAsyncWorkCallback(env, thisVar, cb).Schedule(className, cbExec, cbCompl).val_;
246 }
247 }
248
Export()249 bool SessionBackupNExporter::Export()
250 {
251 HILOGI("called SessionBackupNExporter::Export begin");
252 vector<napi_property_descriptor> props = {NVal::DeclareNapiFunction("appendBundles", AppendBundles)};
253
254 auto [succ, classValue] = NClass::DefineClass(exports_.env_, className, Constructor, std::move(props));
255 if (!succ) {
256 HILOGE("Failed to define class");
257 NError(EIO).ThrowErr(exports_.env_);
258 return false;
259 }
260 succ = NClass::SaveClass(exports_.env_, className, classValue);
261 if (!succ) {
262 HILOGE("Failed to save class");
263 NError(EIO).ThrowErr(exports_.env_);
264 return false;
265 }
266
267 HILOGI("called SessionBackupNExporter::Export end");
268 return exports_.AddProp(className, classValue);
269 }
270
GetClassName()271 string SessionBackupNExporter::GetClassName()
272 {
273 return SessionBackupNExporter::className;
274 }
275
SessionBackupNExporter(napi_env env,napi_value exports)276 SessionBackupNExporter::SessionBackupNExporter(napi_env env, napi_value exports) : NExporter(env, exports) {}
277
~SessionBackupNExporter()278 SessionBackupNExporter::~SessionBackupNExporter() {}
279 } // namespace OHOS::FileManagement::Backup