• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (C) 2023 Huawei Device Co., Ltd.
2 // Licensed under the Apache License, Version 2.0 (the "License");
3 // you may not use this file except in compliance with the License.
4 // You may obtain a copy of the License at
5 //
6 //     http://www.apache.org/licenses/LICENSE-2.0
7 //
8 // Unless required by applicable law or agreed to in writing, software
9 // distributed under the License is distributed on an "AS IS" BASIS,
10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 // See the License for the specific language governing permissions and
12 // limitations under the License.
13 
14 use std::collections::HashSet;
15 
16 use crate::config::{Action, Mode, Version};
17 use crate::info::State;
18 use crate::manage::network::{NetworkInfo, NetworkState, NetworkType};
19 use crate::task::reason::Reason;
20 
21 const INITIALIZED: u8 = State::Initialized.repr;
22 const RUNNING: u8 = State::Running.repr;
23 const RETRYING: u8 = State::Retrying.repr;
24 const WAITING: u8 = State::Waiting.repr;
25 const PAUSED: u8 = State::Paused.repr;
26 const STOPPED: u8 = State::Stopped.repr;
27 const FAILED: u8 = State::Failed.repr;
28 
29 const APP_BACKGROUND_OR_TERMINATE: u8 = Reason::AppBackgroundOrTerminate.repr;
30 const RUNNING_TASK_MEET_LIMITS: u8 = Reason::RunningTaskMeetLimits.repr;
31 const ACCOUNT_STOPPED: u8 = Reason::AccountStopped.repr;
32 const NETWORK_OFFLINE: u8 = Reason::NetworkOffline.repr;
33 const UNSUPPORTED_NETWORK_TYPE: u8 = Reason::UnsupportedNetworkType.repr;
34 const NETWORK_APP: u8 = Reason::NetworkApp.repr;
35 const NETWORK_ACCOUNT: u8 = Reason::NetworkAccount.repr;
36 const APP_ACCOUNT: u8 = Reason::AppAccount.repr;
37 const NETWORK_APP_ACCOUNT: u8 = Reason::NetworkAppAccount.repr;
38 
39 const DOWNLOAD: u8 = Action::Download.repr;
40 const UPLOAD: u8 = Action::Upload.repr;
41 
42 const BACKGROUND: u8 = Mode::BackGround.repr;
43 const FRONTEND: u8 = Mode::FrontEnd.repr;
44 
45 const API9: u8 = Version::API9 as u8;
46 const API10: u8 = Version::API10 as u8;
47 
48 pub(crate) struct SqlList {
49     sqls: Vec<String>,
50 }
51 
52 impl SqlList {
new() -> Self53     pub(crate) fn new() -> Self {
54         SqlList { sqls: Vec::new() }
55     }
56 
add_network_change(&mut self, info: &NetworkState)57     pub(crate) fn add_network_change(&mut self, info: &NetworkState) {
58         match info {
59             NetworkState::Online(info) => {
60                 self.sqls.push(network_available(info));
61                 if let Some(sql) = network_unavailable(info) {
62                     self.sqls.push(sql);
63                 }
64             }
65             NetworkState::Offline => {
66                 self.sqls.push(network_offline());
67             }
68         }
69     }
70 
add_account_change(&mut self, active_accounts: &HashSet<u64>)71     pub(crate) fn add_account_change(&mut self, active_accounts: &HashSet<u64>) {
72         self.sqls.push(account_available(active_accounts));
73         self.sqls.push(account_unavailable(active_accounts));
74     }
75 
add_app_state_available(&mut self, top_uid: u64)76     pub(crate) fn add_app_state_available(&mut self, top_uid: u64) {
77         self.sqls.push(app_state_available(top_uid));
78     }
79 
add_app_state_unavailable(&mut self, uid: u64)80     pub(crate) fn add_app_state_unavailable(&mut self, uid: u64) {
81         self.sqls.push(app_state_unavailable(uid));
82     }
83 
add_app_uninstall(&mut self, uid: u64)84     pub(crate) fn add_app_uninstall(&mut self, uid: u64) {
85         self.sqls.push(app_uninstall(uid));
86     }
add_special_process_terminate(&mut self, uid: u64)87     pub(crate) fn add_special_process_terminate(&mut self, uid: u64) {
88         self.sqls.push(special_process_terminate(uid));
89     }
90 }
91 
92 impl Iterator for SqlList {
93     type Item = String;
94 
next(&mut self) -> Option<Self::Item>95     fn next(&mut self) -> Option<Self::Item> {
96         self.sqls.pop()
97     }
98 }
99 
app_uninstall(uid: u64) -> String100 pub(crate) fn app_uninstall(uid: u64) -> String {
101     format!("DELETE FROM request_task WHERE uid = {}", uid)
102 }
103 
app_state_unavailable(uid: u64) -> String104 pub(crate) fn app_state_unavailable(uid: u64) -> String {
105     format!(
106         "UPDATE request_task SET
107             state = CASE
108                 WHEN (state = {RUNNING} OR state = {RETRYING}) AND action = {DOWNLOAD} THEN {WAITING}
109                 WHEN (state = {RUNNING} OR state = {RETRYING}) AND action = {UPLOAD} THEN {FAILED}
110                 ELSE state
111             END,
112             reason = CASE
113                 WHEN (state = {RUNNING} OR state = {RETRYING}) THEN {APP_BACKGROUND_OR_TERMINATE}
114                 WHEN state = {WAITING} THEN
115                     CASE reason
116                         WHEN {RUNNING_TASK_MEET_LIMITS} THEN {APP_BACKGROUND_OR_TERMINATE}
117                         WHEN {NETWORK_OFFLINE} THEN {NETWORK_APP}
118                         WHEN {UNSUPPORTED_NETWORK_TYPE} THEN {NETWORK_APP}
119                         WHEN {ACCOUNT_STOPPED} THEN {APP_ACCOUNT}
120                         WHEN {NETWORK_ACCOUNT} THEN {NETWORK_APP_ACCOUNT}
121                         ELSE reason
122                     END
123                 ELSE reason
124             END
125         WHERE
126             uid = {uid} AND mode = {FRONTEND}",
127     )
128 }
129 
app_state_available(uid: u64) -> String130 pub(crate) fn app_state_available(uid: u64) -> String {
131     format!(
132         "UPDATE request_task SET
133             reason = CASE
134                 WHEN reason = {APP_BACKGROUND_OR_TERMINATE} THEN {RUNNING_TASK_MEET_LIMITS}
135                 WHEN reason = {NETWORK_APP} THEN {NETWORK_OFFLINE}
136                 WHEN reason = {APP_ACCOUNT} THEN {ACCOUNT_STOPPED}
137                 WHEN reason = {NETWORK_APP_ACCOUNT} THEN {NETWORK_ACCOUNT}
138                 ELSE reason
139             END
140         WHERE
141             state = {WAITING} AND uid = {uid}",
142     )
143 }
144 
account_unavailable(active_accounts: &HashSet<u64>) -> String145 pub(super) fn account_unavailable(active_accounts: &HashSet<u64>) -> String {
146     let mut sql = format!(
147         "UPDATE request_task SET
148             state = CASE
149                 WHEN state = {RUNNING} OR state = {RETRYING} THEN {WAITING}
150                 ELSE state
151             END,
152             reason = CASE
153                 WHEN (state = {RUNNING} OR state = {RETRYING}) THEN {ACCOUNT_STOPPED}
154                 WHEN state = {WAITING} THEN
155                     CASE reason
156                         WHEN {RUNNING_TASK_MEET_LIMITS} THEN {ACCOUNT_STOPPED}
157                         WHEN {NETWORK_OFFLINE} THEN {NETWORK_ACCOUNT}
158                         WHEN {UNSUPPORTED_NETWORK_TYPE} THEN {NETWORK_ACCOUNT}
159                         WHEN {APP_BACKGROUND_OR_TERMINATE} THEN {APP_ACCOUNT}
160                         WHEN {NETWORK_APP} THEN {NETWORK_APP_ACCOUNT}
161                         ELSE reason
162                     END
163                 ELSE reason
164             END
165         WHERE
166             uid/200000 NOT IN (",
167     );
168 
169     for active_account in active_accounts {
170         sql.push_str(&format!("{},", active_account));
171     }
172     if !active_accounts.is_empty() {
173         sql.pop();
174     }
175 
176     sql.push(')');
177     sql
178 }
179 
account_available(active_accounts: &HashSet<u64>) -> String180 pub(super) fn account_available(active_accounts: &HashSet<u64>) -> String {
181     let mut sql = format!(
182         "UPDATE request_task SET
183             reason = CASE
184                 WHEN reason= {ACCOUNT_STOPPED} THEN {RUNNING_TASK_MEET_LIMITS}
185                 WHEN reason = {NETWORK_ACCOUNT} THEN {NETWORK_OFFLINE}
186                 WHEN reason = {APP_ACCOUNT} THEN {APP_BACKGROUND_OR_TERMINATE}
187                 WHEN reason = {NETWORK_APP_ACCOUNT} THEN {NETWORK_APP}
188                 ELSE reason
189             END
190         WHERE
191             state = {WAITING} AND uid/200000 IN (",
192     );
193 
194     for active_account in active_accounts {
195         sql.push_str(&format!("{},", active_account));
196     }
197     if !active_accounts.is_empty() {
198         sql.pop();
199     }
200     sql.push(')');
201     sql
202 }
203 
network_offline() -> String204 pub(super) fn network_offline() -> String {
205     format!(
206         "UPDATE request_task SET
207             state = CASE
208                 WHEN (state = {RUNNING} OR state = {RETRYING}) AND ((version = {API9} AND action = {DOWNLOAD}) OR (version = {API10} AND mode = {BACKGROUND} AND retry = 1)) THEN {WAITING}
209                 WHEN (state = {RUNNING} OR state = {RETRYING}) AND ((version = {API9} AND action = {UPLOAD}) OR (version = {API10} AND (mode = {FRONTEND} OR retry = 0))) THEN {FAILED}
210                 ELSE state
211             END,
212             reason = CASE
213                 WHEN state = {RUNNING} OR state = {RETRYING} THEN {NETWORK_OFFLINE}
214                 WHEN state = {WAITING} THEN
215                     CASE reason
216                         WHEN {RUNNING_TASK_MEET_LIMITS} THEN {NETWORK_OFFLINE}
217                         WHEN {ACCOUNT_STOPPED} THEN {NETWORK_ACCOUNT}
218                         WHEN {APP_BACKGROUND_OR_TERMINATE} THEN {NETWORK_APP}
219                         WHEN {APP_ACCOUNT} THEN {NETWORK_APP_ACCOUNT}
220                         ELSE reason
221                     END
222                 ELSE reason
223             END"
224     )
225 }
226 
network_unavailable(info: &NetworkInfo) -> Option<String>227 pub(super) fn network_unavailable(info: &NetworkInfo) -> Option<String> {
228     if info.network_type == NetworkType::Other {
229         return None;
230     }
231     let mut unsupported_condition = format!("network != {}", info.network_type.repr);
232     if info.is_metered {
233         unsupported_condition.push_str(" OR metered = 0");
234     }
235     if info.is_roaming {
236         unsupported_condition.push_str(" OR roaming = 0");
237     }
238     Some(format!(
239         "UPDATE request_task SET
240             state = CASE
241                 WHEN (state = {RUNNING} OR state = {RETRYING}) AND ((version = {API9} AND action = {DOWNLOAD}) OR (version = {API10} AND mode = {BACKGROUND} AND retry = 1)) THEN {WAITING}
242                 WHEN (state = {RUNNING} OR state = {RETRYING}) AND ((version = {API9} AND action = {UPLOAD}) OR (version = {API10} AND (mode = {FRONTEND} OR retry = 0))) THEN {FAILED}
243                 ELSE state
244             END,
245             reason = CASE
246                 WHEN state = {RUNNING} OR state = {RETRYING} THEN {UNSUPPORTED_NETWORK_TYPE}
247                 WHEN state = {WAITING} THEN
248                     CASE reason
249                         WHEN {RUNNING_TASK_MEET_LIMITS} THEN {UNSUPPORTED_NETWORK_TYPE}
250                         WHEN {ACCOUNT_STOPPED} THEN {NETWORK_ACCOUNT}
251                         WHEN {APP_BACKGROUND_OR_TERMINATE} THEN {NETWORK_APP}
252                         WHEN {APP_ACCOUNT} THEN {NETWORK_APP_ACCOUNT}
253                         ELSE reason
254                     END
255                 ELSE reason
256             END
257         WHERE
258             {unsupported_condition}"
259     ))
260 }
261 
network_available(info: &NetworkInfo) -> String262 pub(super) fn network_available(info: &NetworkInfo) -> String {
263     let mut sql = format!(
264         "UPDATE request_task SET
265             reason = CASE
266                 WHEN reason = {NETWORK_OFFLINE} THEN {RUNNING_TASK_MEET_LIMITS}
267                 WHEN reason = {UNSUPPORTED_NETWORK_TYPE} THEN {RUNNING_TASK_MEET_LIMITS}
268                 WHEN reason = {NETWORK_ACCOUNT} THEN {ACCOUNT_STOPPED}
269                 WHEN reason = {NETWORK_APP} THEN {APP_BACKGROUND_OR_TERMINATE}
270                 WHEN reason = {NETWORK_APP_ACCOUNT} THEN {APP_ACCOUNT}
271                 ELSE reason
272             END
273         WHERE
274             state = {WAITING}",
275     );
276 
277     if info.network_type == NetworkType::Other {
278         return sql;
279     }
280 
281     sql.push_str(&format!(
282         " AND (network = 0 OR network = {}",
283         info.network_type.repr
284     ));
285     if info.is_metered {
286         sql.push_str(" AND metered = 1");
287     }
288     if info.is_roaming {
289         sql.push_str(" AND roaming = 1");
290     }
291     sql.push(')');
292     sql
293 }
294 
special_process_terminate(uid: u64) -> String295 pub(crate) fn special_process_terminate(uid: u64) -> String {
296     format!(
297         "UPDATE request_task
298         SET
299             state = {FAILED},
300             reason = {APP_BACKGROUND_OR_TERMINATE}
301         WHERE
302             uid = {uid}
303             AND (
304                 state = {INITIALIZED}
305                 OR state = {RUNNING}
306                 OR state = {RETRYING}
307                 OR state = {WAITING}
308                 OR state = {PAUSED}
309                 OR state = {STOPPED}
310             );",
311     )
312 }
313 
314 #[cfg(feature = "oh")]
315 #[cfg(test)]
316 mod test {
317 
318     use super::*;
319     use crate::config::NetworkConfig;
320     use crate::manage::database::RequestDb;
321     use crate::tests::{lock_database, test_init};
322     use crate::utils::get_current_timestamp;
323     use crate::utils::task_id_generator::TaskIdGenerator;
324 
325     const COMPLETED: u8 = State::Completed.repr;
326     const PAUSED: u8 = State::Paused.repr;
327     const INIT: u8 = State::Initialized.repr;
328     const WIFI: u8 = NetworkConfig::Wifi as u8;
329     const CELLULAR: u8 = NetworkConfig::Cellular as u8;
330 
query_state_and_reason(task_id: u32) -> (u8, u8)331     fn query_state_and_reason(task_id: u32) -> (u8, u8) {
332         let db = RequestDb::get_instance();
333         (
334             db.query_integer(&format!(
335                 "SELECT state FROM request_task where task_id = {task_id}"
336             ))[0],
337             db.query_integer(&format!(
338                 "SELECT reason FROM request_task where task_id = {task_id}"
339             ))[0],
340         )
341     }
342 
network(sql: &str, change_reason: u8)343     fn network(sql: &str, change_reason: u8) {
344         let db = RequestDb::get_instance();
345         let task_id = TaskIdGenerator::generate();
346         let fail_reason = get_current_timestamp() as u8;
347 
348         // running
349         db.execute(&format!(
350             "INSERT OR REPLACE INTO request_task (task_id, state, reason, network, version, mode, retry) VALUES ({task_id}, {RUNNING}, {fail_reason}, {WIFI}, {API10}, {BACKGROUND}, 1)",
351         ))
352         .unwrap();
353         db.execute(sql).unwrap();
354         let (state, reason) = query_state_and_reason(task_id);
355         assert_eq!(state, WAITING);
356         assert_eq!(reason, change_reason);
357 
358         db.execute(&format!(
359             "INSERT OR REPLACE INTO request_task (task_id, state, reason, network, version, action) VALUES ({task_id}, {RUNNING}, {fail_reason}, {WIFI}, {API9}, {DOWNLOAD})",
360         ))
361         .unwrap();
362         db.execute(sql).unwrap();
363         let (state, reason) = query_state_and_reason(task_id);
364         assert_eq!(state, WAITING);
365         assert_eq!(reason, change_reason);
366 
367         db.execute(&format!(
368             "INSERT OR REPLACE INTO request_task (task_id, state, reason, network, version, action) VALUES ({task_id}, {RUNNING}, {fail_reason}, {WIFI}, {API9}, {UPLOAD})",
369         ))
370         .unwrap();
371         db.execute(sql).unwrap();
372         let (state, reason) = query_state_and_reason(task_id);
373         assert_eq!(state, FAILED);
374         assert_eq!(reason, change_reason);
375 
376         db.execute(&format!(
377             "INSERT OR REPLACE INTO request_task (task_id, state, reason, network, version, mode, retry) VALUES ({task_id}, {RUNNING}, {fail_reason}, {WIFI}, {API10}, {FRONTEND}, 1)",
378         ))
379         .unwrap();
380         db.execute(sql).unwrap();
381         let (state, reason) = query_state_and_reason(task_id);
382         assert_eq!(state, FAILED);
383         assert_eq!(reason, change_reason);
384 
385         db.execute(&format!(
386             "INSERT OR REPLACE INTO request_task (task_id, state, reason, network, version, mode, retry) VALUES ({task_id}, {RUNNING}, {fail_reason}, {WIFI}, {API10}, {BACKGROUND}, 0)",
387         ))
388         .unwrap();
389         db.execute(sql).unwrap();
390         let (state, reason) = query_state_and_reason(task_id);
391         assert_eq!(state, FAILED);
392         assert_eq!(reason, change_reason);
393 
394         // other state
395         db.execute(&format!(
396             "INSERT OR REPLACE INTO request_task (task_id, state, reason, network) VALUES ({task_id}, {FAILED}, {fail_reason}, {WIFI})",
397         ))
398         .unwrap();
399         db.execute(sql).unwrap();
400 
401         let (state, reason) = query_state_and_reason(task_id);
402         assert_eq!(state, FAILED);
403         assert_eq!(reason, fail_reason);
404 
405         // waiting
406         db.execute(&format!(
407             "INSERT OR REPLACE INTO request_task (task_id, state, reason, network) VALUES ({task_id}, {WAITING}, {RUNNING_TASK_MEET_LIMITS}, {WIFI})",
408         ))
409         .unwrap();
410         db.execute(sql).unwrap();
411 
412         let (state, reason) = query_state_and_reason(task_id);
413         assert_eq!(state, WAITING);
414         assert_eq!(reason, change_reason);
415 
416         // api9 + download
417         db.execute(&format!(
418             "INSERT OR REPLACE INTO request_task (task_id, state, version, action, network, metered, roaming) VALUES ({task_id}, {RUNNING}, {API9}, {DOWNLOAD}, {CELLULAR}, 1, 0)",
419         ))
420         .unwrap();
421         db.execute(sql).unwrap();
422 
423         let (state, reason) = query_state_and_reason(task_id);
424         assert_eq!(state, WAITING);
425         assert_eq!(reason, change_reason);
426 
427         // api9 + upload
428         db.execute(&format!(
429             "INSERT OR REPLACE INTO request_task (task_id, state, version, action, network, metered, roaming) VALUES ({task_id}, {RUNNING}, {API9}, {UPLOAD}, {CELLULAR}, 0, 1)",
430         ))
431         .unwrap();
432         db.execute(sql).unwrap();
433 
434         let (state, reason) = query_state_and_reason(task_id);
435         assert_eq!(state, FAILED);
436         assert_eq!(reason, change_reason);
437 
438         // api10 + background + retry
439         db.execute(&format!(
440             "INSERT OR REPLACE INTO request_task (task_id, state, version, mode, retry, network, metered, roaming) VALUES ({task_id}, {RUNNING}, {API10}, {BACKGROUND}, 1, {CELLULAR}, 0, 0)",
441         ))
442         .unwrap();
443         db.execute(sql).unwrap();
444 
445         let (state, reason) = query_state_and_reason(task_id);
446         assert_eq!(state, WAITING);
447         assert_eq!(reason, change_reason);
448 
449         // api10 + frontEnd + retry
450         db.execute(&format!(
451             "INSERT OR REPLACE INTO request_task (task_id, state, version, mode, retry, network) VALUES ({task_id}, {RUNNING}, {API10}, {FRONTEND}, 1, {WIFI})",
452         ))
453         .unwrap();
454         db.execute(sql).unwrap();
455 
456         let (state, reason) = query_state_and_reason(task_id);
457         assert_eq!(state, FAILED);
458         assert_eq!(reason, change_reason);
459 
460         // api10 + Background
461         db.execute(&format!(
462             "INSERT OR REPLACE INTO request_task (task_id, state, version, mode, retry, network) VALUES ({task_id}, {RUNNING}, {API10}, {BACKGROUND}, 0, {WIFI})",
463         ))
464         .unwrap();
465         db.execute(sql).unwrap();
466 
467         let (state, reason) = query_state_and_reason(task_id);
468         assert_eq!(state, FAILED);
469         assert_eq!(reason, change_reason);
470     }
471 
472     #[test]
ut_network_offline()473     fn ut_network_offline() {
474         test_init();
475         let _lock = lock_database();
476         network(&network_offline(), NETWORK_OFFLINE);
477     }
478 
479     #[test]
ut_network_unsupported()480     fn ut_network_unsupported() {
481         test_init();
482         let _lock = lock_database();
483         let info = NetworkInfo {
484             network_type: NetworkType::Cellular,
485             is_metered: true,
486             is_roaming: true,
487         };
488         network(
489             &network_unavailable(&info).unwrap(),
490             UNSUPPORTED_NETWORK_TYPE,
491         );
492 
493         // network type matches
494         let db = RequestDb::get_instance();
495         let task_id = TaskIdGenerator::generate();
496         db.execute(&format!(
497             "INSERT OR REPLACE INTO request_task (task_id, state, reason, network, metered, roaming) VALUES ({task_id}, {WAITING}, {RUNNING_TASK_MEET_LIMITS}, {CELLULAR}, 1, 1)",
498         ))
499         .unwrap();
500         db.execute(&network_unavailable(&info).unwrap()).unwrap();
501 
502         let (state, reason) = query_state_and_reason(task_id);
503         assert_eq!(state, WAITING);
504         assert_eq!(reason, RUNNING_TASK_MEET_LIMITS);
505     }
506 
507     #[test]
ut_network_online()508     fn ut_network_online() {
509         test_init();
510         let _lock = lock_database();
511         let db = RequestDb::get_instance();
512         let task_id = TaskIdGenerator::generate();
513 
514         let info = NetworkInfo {
515             network_type: NetworkType::Cellular,
516             is_metered: true,
517             is_roaming: true,
518         };
519 
520         // unsupported
521         let unsupported_states = [
522             (WIFI, 1, 1),
523             (CELLULAR, 0, 0),
524             (CELLULAR, 1, 0),
525             (CELLULAR, 0, 1),
526         ];
527         for state in unsupported_states {
528             db.execute(&format!(
529                 "INSERT OR REPLACE INTO request_task (task_id, state, reason, network, metered, roaming) VALUES ({task_id}, {WAITING}, {NETWORK_OFFLINE}, {}, {}, {})",state.0,state.1,state.2
530             )).unwrap();
531 
532             db.execute(&network_available(&info)).unwrap();
533 
534             let state: u8 = db.query_integer(&format!(
535                 "SELECT state FROM request_task where task_id = {task_id}"
536             ))[0];
537             let reason: u8 = db.query_integer(&format!(
538                 "SELECT reason FROM request_task where task_id = {task_id}"
539             ))[0];
540             assert_eq!(state, WAITING);
541             assert_eq!(reason, NETWORK_OFFLINE);
542         }
543 
544         // support
545         db.execute(&format!(
546             "INSERT OR REPLACE INTO request_task (task_id, state, reason, network, metered, roaming) VALUES ({task_id}, {WAITING}, {NETWORK_OFFLINE}, {CELLULAR}, 1, 1)"
547         )).unwrap();
548         db.execute(&network_available(&info)).unwrap();
549 
550         let (state, reason) = query_state_and_reason(task_id);
551         assert_eq!(state, WAITING);
552         assert_eq!(reason, RUNNING_TASK_MEET_LIMITS);
553     }
554 
555     #[test]
ut_app_state_unavailable()556     fn ut_app_state_unavailable() {
557         test_init();
558         let _lock = lock_database();
559         let db = RequestDb::get_instance();
560         let task_id = TaskIdGenerator::generate();
561         let uid = get_current_timestamp();
562         let fail_reason = get_current_timestamp() as u8;
563 
564         // running
565         db.execute(&format!(
566             "INSERT OR REPLACE INTO request_task (task_id, uid, mode, state, reason, action) VALUES ({task_id}, {uid}, {FRONTEND}, {RUNNING}, {fail_reason}, {DOWNLOAD})"
567         )).unwrap();
568         db.execute(&app_state_unavailable(uid)).unwrap();
569 
570         let (state, reason) = query_state_and_reason(task_id);
571         assert_eq!(state, WAITING);
572         assert_eq!(reason, APP_BACKGROUND_OR_TERMINATE);
573 
574         // upload
575         db.execute(&format!(
576             "INSERT OR REPLACE INTO request_task (task_id, uid, mode, state, reason, action) VALUES ({task_id}, {uid}, {FRONTEND}, {RUNNING}, {fail_reason}, {UPLOAD})"
577         )).unwrap();
578         db.execute(&app_state_unavailable(uid)).unwrap();
579 
580         let (state, reason) = query_state_and_reason(task_id);
581         assert_eq!(state, FAILED);
582         assert_eq!(reason, APP_BACKGROUND_OR_TERMINATE);
583 
584         // retrying
585         db.execute(&format!(
586             "INSERT OR REPLACE INTO request_task (task_id, uid, mode, state, reason, action) VALUES ({task_id}, {uid}, {FRONTEND}, {RETRYING}, {fail_reason}, {DOWNLOAD})"
587         )).unwrap();
588         db.execute(&app_state_unavailable(uid)).unwrap();
589 
590         let (state, reason) = query_state_and_reason(task_id);
591         assert_eq!(state, WAITING);
592         assert_eq!(reason, APP_BACKGROUND_OR_TERMINATE);
593 
594         // other state
595         db.execute(&format!(
596             "INSERT OR REPLACE INTO request_task (task_id, uid, mode, state, reason) VALUES ({task_id}, {uid}, {FRONTEND}, {FAILED}, {fail_reason})"
597         )).unwrap();
598         db.execute(&app_state_unavailable(uid)).unwrap();
599 
600         let (state, reason) = query_state_and_reason(task_id);
601         assert_eq!(state, FAILED);
602         assert_eq!(reason, fail_reason);
603 
604         // waiting
605         db.execute(&format!(
606             "INSERT OR REPLACE INTO request_task (task_id, uid, mode, state, reason) VALUES ({task_id}, {uid}, {FRONTEND}, {WAITING}, {RUNNING_TASK_MEET_LIMITS})"
607         )).unwrap();
608         db.execute(&app_state_unavailable(uid)).unwrap();
609 
610         let (state, reason) = query_state_and_reason(task_id);
611         assert_eq!(state, WAITING);
612         assert_eq!(reason, APP_BACKGROUND_OR_TERMINATE);
613 
614         // running + donwload
615         db.execute(&format!(
616             "INSERT OR REPLACE INTO request_task (task_id, uid, mode, state, action) VALUES ({task_id}, {uid}, {FRONTEND}, {RUNNING}, {DOWNLOAD})"
617         )).unwrap();
618         db.execute(&app_state_unavailable(uid)).unwrap();
619 
620         let (state, reason) = query_state_and_reason(task_id);
621         assert_eq!(state, WAITING);
622         assert_eq!(reason, APP_BACKGROUND_OR_TERMINATE);
623 
624         // running + upload
625         db.execute(&format!(
626             "INSERT OR REPLACE INTO request_task (task_id, uid, mode, state, action) VALUES ({task_id}, {uid}, {FRONTEND}, {RUNNING}, {UPLOAD})"
627         )).unwrap();
628         db.execute(&app_state_unavailable(uid)).unwrap();
629 
630         let (state, reason) = query_state_and_reason(task_id);
631         assert_eq!(state, FAILED);
632         assert_eq!(reason, APP_BACKGROUND_OR_TERMINATE);
633 
634         // background
635         db.execute(&format!(
636             "INSERT OR REPLACE INTO request_task (task_id, uid, mode, state, action) VALUES ({task_id}, {uid}, {BACKGROUND}, {RUNNING}, {UPLOAD})"
637         )).unwrap();
638         db.execute(&app_state_unavailable(uid)).unwrap();
639 
640         let state: u8 = db.query_integer(&format!(
641             "SELECT state FROM request_task where task_id = {task_id}"
642         ))[0];
643         assert_eq!(state, RUNNING);
644     }
645 
646     #[test]
ut_app_state_available()647     fn ut_app_state_available() {
648         test_init();
649         let _lock = lock_database();
650         let db = RequestDb::get_instance();
651         let task_id = TaskIdGenerator::generate();
652         let uid = get_current_timestamp();
653 
654         db.execute(&format!(
655             "INSERT OR REPLACE INTO request_task (task_id, uid, state, reason) VALUES ({task_id}, {uid}, {WAITING}, {APP_BACKGROUND_OR_TERMINATE})"
656         )).unwrap();
657         db.execute(&app_state_available(uid)).unwrap();
658 
659         let (state, reason) = query_state_and_reason(task_id);
660         assert_eq!(state, WAITING);
661         assert_eq!(reason, RUNNING_TASK_MEET_LIMITS);
662     }
663 
664     #[test]
ut_account_unavailable()665     fn ut_account_unavailable() {
666         test_init();
667         let _lock = lock_database();
668         let db = RequestDb::get_instance();
669         let task_id = TaskIdGenerator::generate();
670         let uid = get_current_timestamp();
671         let user = uid / 200000;
672 
673         let mut hash_set = HashSet::new();
674         let states = [RUNNING, RETRYING, WAITING];
675         for (i, state) in states.into_iter().enumerate() {
676             db.execute(&format!(
677             "INSERT OR REPLACE INTO request_task (task_id, uid, state, reason) VALUES ({task_id}, {uid}, {state}, {RUNNING_TASK_MEET_LIMITS})"
678         )).unwrap();
679             db.execute(&account_unavailable(&hash_set)).unwrap();
680             let state: u8 = db.query_integer(&format!(
681                 "SELECT state FROM request_task where task_id = {task_id}"
682             ))[0];
683             let reason: u8 = db.query_integer(&format!(
684                 "SELECT reason FROM request_task where task_id = {task_id}"
685             ))[0];
686             assert_eq!(state, WAITING);
687             assert_eq!(reason, ACCOUNT_STOPPED);
688             hash_set.insert(user + i as u64 + 1);
689         }
690         let states = [COMPLETED, FAILED, PAUSED, INIT];
691         for state in states.into_iter() {
692             db.execute(&format!(
693             "INSERT OR REPLACE INTO request_task (task_id, uid, state, reason) VALUES ({task_id}, {uid}, {state}, {RUNNING_TASK_MEET_LIMITS})"
694         )).unwrap();
695             db.execute(&account_unavailable(&hash_set)).unwrap();
696             let change_state: u8 = db.query_integer(&format!(
697                 "SELECT state FROM request_task where task_id = {task_id}"
698             ))[0];
699 
700             assert_eq!(change_state, state);
701             let reason: u8 = db.query_integer(&format!(
702                 "SELECT reason FROM request_task where task_id = {task_id}"
703             ))[0];
704             assert_eq!(reason, RUNNING_TASK_MEET_LIMITS);
705         }
706     }
707 
708     #[test]
ut_account_available()709     fn ut_account_available() {
710         test_init();
711         let _lock = lock_database();
712         let db = RequestDb::get_instance();
713         let task_id = TaskIdGenerator::generate();
714         let uid = get_current_timestamp();
715         let user = uid / 200000;
716 
717         let mut hash_set = HashSet::new();
718 
719         db.execute(&format!(
720             "INSERT OR REPLACE INTO request_task (task_id, uid, state, reason) VALUES ({task_id}, {uid}, {WAITING}, {ACCOUNT_STOPPED})"
721         )).unwrap();
722         db.execute(&account_available(&hash_set)).unwrap();
723         let (state, reason) = query_state_and_reason(task_id);
724         assert_eq!(state, WAITING);
725         assert_eq!(reason, ACCOUNT_STOPPED);
726         hash_set.insert(user);
727         db.execute(&account_available(&hash_set)).unwrap();
728         let (state, reason) = query_state_and_reason(task_id);
729         assert_eq!(state, WAITING);
730         assert_eq!(reason, RUNNING_TASK_MEET_LIMITS);
731     }
732 
733     #[test]
ut_multi_reason_available()734     fn ut_multi_reason_available() {
735         test_init();
736         let _lock = lock_database();
737         let db = RequestDb::get_instance();
738         let task_id = TaskIdGenerator::generate();
739         let uid = get_current_timestamp();
740         let user = uid / 200000;
741 
742         let hash_set = HashSet::from([user]);
743         let info = NetworkInfo {
744             network_type: NetworkType::Cellular,
745             is_metered: true,
746             is_roaming: true,
747         };
748 
749         // account + network
750         db.execute(&format!(
751             "INSERT OR REPLACE INTO request_task (task_id, uid, state, reason, network, metered, roaming) VALUES ({task_id}, {uid}, {WAITING}, {NETWORK_APP_ACCOUNT}, {CELLULAR}, 1, 1)"
752         )).unwrap();
753 
754         db.execute(&account_available(&hash_set)).unwrap();
755         let (state, reason) = query_state_and_reason(task_id);
756         assert_eq!(state, WAITING);
757         assert_eq!(reason, NETWORK_APP);
758 
759         db.execute(&network_available(&info)).unwrap();
760         let (state, reason) = query_state_and_reason(task_id);
761         assert_eq!(state, WAITING);
762         assert_eq!(reason, APP_BACKGROUND_OR_TERMINATE);
763 
764         // account + app
765         db.execute(&format!(
766             "INSERT OR REPLACE INTO request_task (task_id, uid, state, reason, network, metered, roaming) VALUES ({task_id}, {uid}, {WAITING}, {NETWORK_APP_ACCOUNT}, {CELLULAR}, 1, 1)"
767         )).unwrap();
768 
769         db.execute(&account_available(&hash_set)).unwrap();
770         db.execute(&app_state_available(uid)).unwrap();
771         let (state, reason) = query_state_and_reason(task_id);
772         assert_eq!(state, WAITING);
773         assert_eq!(reason, NETWORK_OFFLINE);
774 
775         // network + app
776         db.execute(&format!(
777             "INSERT OR REPLACE INTO request_task (task_id, uid, state, reason, network, metered, roaming) VALUES ({task_id}, {uid}, {WAITING}, {NETWORK_APP_ACCOUNT}, {CELLULAR}, 1, 1)"
778         )).unwrap();
779         db.execute(&network_available(&info)).unwrap();
780         let (state, reason) = query_state_and_reason(task_id);
781         assert_eq!(state, WAITING);
782         assert_eq!(reason, APP_ACCOUNT);
783 
784         db.execute(&app_state_available(uid)).unwrap();
785         let (state, reason) = query_state_and_reason(task_id);
786         assert_eq!(state, WAITING);
787         assert_eq!(reason, ACCOUNT_STOPPED);
788 
789         // network + account
790         db.execute(&format!(
791             "INSERT OR REPLACE INTO request_task (task_id, uid, state, reason, network, metered, roaming) VALUES ({task_id}, {uid}, {WAITING}, {NETWORK_APP_ACCOUNT}, {CELLULAR}, 1, 1)"
792         )).unwrap();
793         db.execute(&network_available(&info)).unwrap();
794         db.execute(&account_available(&hash_set)).unwrap();
795         let (state, reason) = query_state_and_reason(task_id);
796         assert_eq!(state, WAITING);
797         assert_eq!(reason, APP_BACKGROUND_OR_TERMINATE);
798 
799         // app + network
800         db.execute(&format!(
801             "INSERT OR REPLACE INTO request_task (task_id, uid, state, reason, network, metered, roaming) VALUES ({task_id}, {uid}, {WAITING}, {NETWORK_APP_ACCOUNT}, {CELLULAR}, 1, 1)"
802         )).unwrap();
803         db.execute(&app_state_available(uid)).unwrap();
804         let (state, reason) = query_state_and_reason(task_id);
805         assert_eq!(state, WAITING);
806         assert_eq!(reason, NETWORK_ACCOUNT);
807 
808         db.execute(&network_available(&info)).unwrap();
809         let (state, reason) = query_state_and_reason(task_id);
810         assert_eq!(state, WAITING);
811         assert_eq!(reason, ACCOUNT_STOPPED);
812 
813         // app + account
814         db.execute(&format!(
815             "INSERT OR REPLACE INTO request_task (task_id, uid, state, reason, network, metered, roaming) VALUES ({task_id}, {uid}, {WAITING}, {NETWORK_APP_ACCOUNT}, {CELLULAR}, 1, 1)"
816         )).unwrap();
817         db.execute(&app_state_available(uid)).unwrap();
818         db.execute(&account_available(&hash_set)).unwrap();
819         let (state, reason) = query_state_and_reason(task_id);
820         assert_eq!(state, WAITING);
821         assert_eq!(reason, NETWORK_OFFLINE);
822     }
823 
824     #[test]
ut_multi_reason_unailable()825     fn ut_multi_reason_unailable() {
826         test_init();
827         let _lock = lock_database();
828         let db = RequestDb::get_instance();
829         let task_id = TaskIdGenerator::generate();
830         let uid = get_current_timestamp();
831         let hash_set = HashSet::new();
832         let info = NetworkInfo {
833             network_type: NetworkType::Wifi,
834             is_metered: true,
835             is_roaming: true,
836         };
837 
838         // account + offline
839         db.execute(&format!(
840             "INSERT OR REPLACE INTO request_task (task_id, uid, state, reason, network, metered, roaming, mode) VALUES ({task_id}, {uid}, {WAITING}, {ACCOUNT_STOPPED}, {CELLULAR}, 1, 1, {FRONTEND})"
841         )).unwrap();
842         db.execute(&network_offline()).unwrap();
843         let (state, reason) = query_state_and_reason(task_id);
844         assert_eq!(state, WAITING);
845         assert_eq!(reason, NETWORK_ACCOUNT);
846 
847         // account + unsupported_network
848         db.execute(&format!(
849             "INSERT OR REPLACE INTO request_task (task_id, uid, state, reason, network, metered, roaming, mode) VALUES ({task_id}, {uid}, {WAITING}, {ACCOUNT_STOPPED}, {CELLULAR}, 1, 1, {FRONTEND})"
850         )).unwrap();
851 
852         db.execute(&network_unavailable(&info).unwrap()).unwrap();
853         let (state, reason) = query_state_and_reason(task_id);
854         assert_eq!(state, WAITING);
855         assert_eq!(reason, NETWORK_ACCOUNT);
856 
857         // account + offline + app
858         db.execute(&format!(
859             "INSERT OR REPLACE INTO request_task (task_id, uid, state, reason, network, metered, roaming, mode) VALUES ({task_id}, {uid}, {WAITING}, {NETWORK_ACCOUNT}, {CELLULAR}, 1, 1, {FRONTEND})"
860         )).unwrap();
861         db.execute(&app_state_unavailable(uid)).unwrap();
862         let (state, reason) = query_state_and_reason(task_id);
863         assert_eq!(state, WAITING);
864         assert_eq!(reason, NETWORK_APP_ACCOUNT);
865 
866         // account + app
867         db.execute(&format!(
868             "INSERT OR REPLACE INTO request_task (task_id, uid, state, reason, network, metered, roaming, mode) VALUES ({task_id}, {uid}, {WAITING}, {ACCOUNT_STOPPED}, {CELLULAR}, 1, 1, {FRONTEND})"
869         )).unwrap();
870         db.execute(&app_state_unavailable(uid)).unwrap();
871         let (state, reason) = query_state_and_reason(task_id);
872         assert_eq!(state, WAITING);
873         assert_eq!(reason, APP_ACCOUNT);
874 
875         // account + app + offline
876         db.execute(&format!(
877             "INSERT OR REPLACE INTO request_task (task_id, uid, state, reason, network, metered, roaming, mode) VALUES ({task_id}, {uid}, {WAITING}, {APP_ACCOUNT}, {CELLULAR}, 1, 1, {FRONTEND})"
878         )).unwrap();
879         db.execute(&network_offline()).unwrap();
880         let (state, reason) = query_state_and_reason(task_id);
881         assert_eq!(state, WAITING);
882         assert_eq!(reason, NETWORK_APP_ACCOUNT);
883 
884         // account + app + unsupported_network
885         db.execute(&format!(
886             "INSERT OR REPLACE INTO request_task (task_id, uid, state, reason, network, metered, roaming, mode) VALUES ({task_id}, {uid}, {WAITING}, {APP_ACCOUNT}, {CELLULAR}, 1, 1, {FRONTEND})"
887         )).unwrap();
888         db.execute(&network_unavailable(&info).unwrap()).unwrap();
889         let (state, reason) = query_state_and_reason(task_id);
890         assert_eq!(state, WAITING);
891         assert_eq!(reason, NETWORK_APP_ACCOUNT);
892 
893         // network + account
894         db.execute(&format!(
895             "INSERT OR REPLACE INTO request_task (task_id, uid, state, reason, network, metered, roaming, mode) VALUES ({task_id}, {uid}, {WAITING}, {NETWORK_OFFLINE}, {CELLULAR}, 1, 1, {FRONTEND})"
896         )).unwrap();
897         db.execute(&account_unavailable(&hash_set)).unwrap();
898         let (state, reason) = query_state_and_reason(task_id);
899         assert_eq!(state, WAITING);
900         assert_eq!(reason, NETWORK_ACCOUNT);
901 
902         // unsupported_network + account
903         db.execute(&format!(
904             "INSERT OR REPLACE INTO request_task (task_id, uid, state, reason, network, metered, roaming, mode) VALUES ({task_id}, {uid}, {WAITING}, {UNSUPPORTED_NETWORK_TYPE}, {CELLULAR}, 1, 1, {FRONTEND})"
905         )).unwrap();
906         db.execute(&account_unavailable(&hash_set)).unwrap();
907         let (state, reason) = query_state_and_reason(task_id);
908         assert_eq!(state, WAITING);
909         assert_eq!(reason, NETWORK_ACCOUNT);
910 
911         // network + account + app
912         db.execute(&format!(
913             "INSERT OR REPLACE INTO request_task (task_id, uid, state, reason, network, metered, roaming, mode) VALUES ({task_id}, {uid}, {WAITING}, {NETWORK_ACCOUNT}, {CELLULAR}, 1, 1, {FRONTEND})"
914         )).unwrap();
915         db.execute(&app_state_unavailable(uid)).unwrap();
916         let (state, reason) = query_state_and_reason(task_id);
917         assert_eq!(state, WAITING);
918         assert_eq!(reason, NETWORK_APP_ACCOUNT);
919 
920         // network + app
921         db.execute(&format!(
922             "INSERT OR REPLACE INTO request_task (task_id, uid, state, reason, network, metered, roaming, mode) VALUES ({task_id}, {uid}, {WAITING}, {NETWORK_OFFLINE}, {CELLULAR}, 1, 1, {FRONTEND})"
923         )).unwrap();
924         db.execute(&app_state_unavailable(uid)).unwrap();
925         let (state, reason) = query_state_and_reason(task_id);
926         assert_eq!(state, WAITING);
927         assert_eq!(reason, NETWORK_APP);
928 
929         // unsupported_network + app
930         db.execute(&format!(
931             "INSERT OR REPLACE INTO request_task (task_id, uid, state, reason, network, metered, roaming, mode) VALUES ({task_id}, {uid}, {WAITING}, {UNSUPPORTED_NETWORK_TYPE}, {CELLULAR}, 1, 1, {FRONTEND})"
932         )).unwrap();
933         db.execute(&app_state_unavailable(uid)).unwrap();
934         let (state, reason) = query_state_and_reason(task_id);
935         assert_eq!(state, WAITING);
936         assert_eq!(reason, NETWORK_APP);
937 
938         // network + app + account
939         db.execute(&format!(
940             "INSERT OR REPLACE INTO request_task (task_id, uid, state, reason, network, metered, roaming, mode) VALUES ({task_id}, {uid}, {WAITING}, {NETWORK_APP}, {CELLULAR}, 1, 1, {FRONTEND})"
941         )).unwrap();
942         db.execute(&account_unavailable(&hash_set)).unwrap();
943         let (state, reason) = query_state_and_reason(task_id);
944         assert_eq!(state, WAITING);
945         assert_eq!(reason, NETWORK_APP_ACCOUNT);
946 
947         // app + offline
948         db.execute(&format!(
949             "INSERT OR REPLACE INTO request_task (task_id, uid, state, reason, network, metered, roaming, mode) VALUES ({task_id}, {uid}, {WAITING}, {APP_BACKGROUND_OR_TERMINATE}, {CELLULAR}, 1, 1, {FRONTEND})"
950         )).unwrap();
951         db.execute(&network_offline()).unwrap();
952         let (state, reason) = query_state_and_reason(task_id);
953         assert_eq!(state, WAITING);
954         assert_eq!(reason, NETWORK_APP);
955 
956         // app + unsupported_network
957         db.execute(&format!(
958             "INSERT OR REPLACE INTO request_task (task_id, uid, state, reason, network, metered, roaming, mode) VALUES ({task_id}, {uid}, {WAITING}, {APP_BACKGROUND_OR_TERMINATE}, {CELLULAR}, 1, 1, {FRONTEND})"
959         )).unwrap();
960         db.execute(&network_unavailable(&info).unwrap()).unwrap();
961         let (state, reason) = query_state_and_reason(task_id);
962         assert_eq!(state, WAITING);
963         assert_eq!(reason, NETWORK_APP);
964 
965         // app + network + account
966         db.execute(&format!(
967             "INSERT OR REPLACE INTO request_task (task_id, uid, state, reason, network, metered, roaming, mode) VALUES ({task_id}, {uid}, {WAITING}, {NETWORK_APP}, {CELLULAR}, 1, 1, {FRONTEND})"
968         )).unwrap();
969         db.execute(&account_unavailable(&hash_set)).unwrap();
970         let (state, reason) = query_state_and_reason(task_id);
971         assert_eq!(state, WAITING);
972         assert_eq!(reason, NETWORK_APP_ACCOUNT);
973 
974         // app + account
975         db.execute(&format!(
976             "INSERT OR REPLACE INTO request_task (task_id, uid, state, reason, network, metered, roaming, mode) VALUES ({task_id}, {uid}, {WAITING}, {APP_BACKGROUND_OR_TERMINATE}, {CELLULAR}, 1, 1, {FRONTEND})"
977         )).unwrap();
978         db.execute(&account_unavailable(&hash_set)).unwrap();
979         let (state, reason) = query_state_and_reason(task_id);
980         assert_eq!(state, WAITING);
981         assert_eq!(reason, APP_ACCOUNT);
982 
983         // app + account + offline
984         db.execute(&format!(
985             "INSERT OR REPLACE INTO request_task (task_id, uid, state, reason, network, metered, roaming, mode) VALUES ({task_id}, {uid}, {WAITING}, {APP_ACCOUNT}, {CELLULAR}, 1, 1, {FRONTEND})"
986         )).unwrap();
987         db.execute(&network_offline()).unwrap();
988         let (state, reason) = query_state_and_reason(task_id);
989         assert_eq!(state, WAITING);
990         assert_eq!(reason, NETWORK_APP_ACCOUNT);
991 
992         // app + account + unsupported_network
993         db.execute(&format!(
994             "INSERT OR REPLACE INTO request_task (task_id, uid, state, reason, network, metered, roaming, mode) VALUES ({task_id}, {uid}, {WAITING}, {APP_ACCOUNT}, {CELLULAR}, 1, 1, {FRONTEND})"
995         )).unwrap();
996         db.execute(&network_unavailable(&info).unwrap()).unwrap();
997         let (state, reason) = query_state_and_reason(task_id);
998         assert_eq!(state, WAITING);
999         assert_eq!(reason, NETWORK_APP_ACCOUNT);
1000     }
1001 }
1002