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 16import BackupExtensionAbility, {BundleVersion} from '@ohos.application.BackupExtensionAbility'; 17import fs from '@ohos.file.fs'; 18// @ts-ignore 19import ringtonerestore from '@ohos.multimedia.ringtonerestore'; 20 21const TAG = 'RingtoneBackupExtAbility'; 22 23const ringtonePath = '/storage/media/local/files/Ringtone/'; 24 25const RESTORE_SCENE_TYPE_DUAL_UPGRADE : number = 0; 26const RESTORE_SCENE_TYPE_SINGLE_CLONE : number = 1; 27const RESTORE_SCENE_TYPE_DUAL_CLONE : number = 2; 28 29const UPGRADE_NAME = '0.0.0.0'; 30const DUAL_FRAME_CLONE_NAME = '99.99.99.999'; 31 32const ON_RESTORE_COMMON_CODE : string = '0'; 33const ON_RESTORE_ERROR_CODE : string = '13500099'; 34 35export interface IResultExInfo { 36 resultInfo: [IResultErrorInfo]; 37} 38 39export interface IResultErrorInfo { 40 type: string; 41 errorCode: string; 42 errorInfo: string; 43} 44 45export default class RingtoneBackupExtAbility extends BackupExtensionAbility { 46 async onBackup() : Promise<void> { 47 console.log(TAG, 'onBackup ok.'); 48 } 49 50 async onRestoreEx(bundleVersion: BundleVersion, restoreInfo: string): Promise<string> { 51 console.log(TAG, `onRestoreEx ok ${JSON.stringify(bundleVersion)}`); 52 console.time(TAG + ' RESTORE'); 53 const backupBasePath = this.context.backupDir + 'restore'; 54 const backupFilePath = backupBasePath + '/storage/media/local/files/Ringtone/'; 55 let srcPath:string = backupFilePath; 56 let destPath:string = ringtonePath; 57 let cloneType:number; 58 if (bundleVersion.name.startsWith(UPGRADE_NAME)) { 59 cloneType = RESTORE_SCENE_TYPE_DUAL_UPGRADE; 60 } else if (bundleVersion.name === DUAL_FRAME_CLONE_NAME && bundleVersion.code === 0) { 61 cloneType = RESTORE_SCENE_TYPE_DUAL_CLONE; 62 } else { 63 cloneType = RESTORE_SCENE_TYPE_SINGLE_CLONE; 64 } 65 let restoreResult:number = await ringtonerestore.startRestore(cloneType, backupBasePath); 66 console.log(TAG, `restoreResult:${restoreResult}`); 67 console.timeEnd(TAG + ' RESTORE'); 68 console.time(TAG + ' MOVE REST FILES'); 69 await this.moveRestFiles(srcPath, destPath); 70 console.timeEnd(TAG + ' MOVE REST FILES'); 71 let result:boolean = restoreResult === 0; 72 let resultExInfo: IResultExInfo = { 73 resultInfo: [ 74 { 75 type: 'ErrorInfo', 76 errorCode: result ? ON_RESTORE_COMMON_CODE : ON_RESTORE_ERROR_CODE, // 如果成功,错误码返回0,如果失败,返回约定的错误码 77 errorInfo: '', 78 } 79 ] 80 }; 81 let resultInfo: string = JSON.stringify(resultExInfo); 82 console.log(TAG, `restore end resultInfo:${resultInfo}`); 83 return resultInfo; 84 } 85 86 private isFileExist(filePath : string) : boolean { 87 try { 88 return fs.accessSync(filePath); 89 } catch (err) { 90 console.error(TAG, `accessSync failed, message = ${err.message}; code = ${err.code}`); 91 return false; 92 } 93 } 94 95 private async moveRestFiles(srcPath : string, destPath : string) : Promise<void> { 96 console.log(TAG, 'Start to move rest files.'); 97 const MOVE_ERR_CODE = 13900015; 98 let conflictList = []; 99 try { 100 console.log(TAG, `move dir from [${srcPath}] to [${destPath}]`); 101 await fs.copyDir(srcPath, destPath, 0); 102 } catch (err) { 103 console.log(TAG, `catch err: ${JSON.stringify(err)}`); 104 if (err.code === MOVE_ERR_CODE) { 105 conflictList = err.data; 106 } else { 107 console.error(TAG, `move directory failed, message = ${err.message}, code = ${err.code}`); 108 } 109 } 110 for (let i = 0; i < conflictList.length; i++) { 111 console.log(TAG, `move conflect file from [${conflictList[i].srcFile}] to [${conflictList[i].destFile}]`); 112 try { 113 await this.moveConflictFile(conflictList[i].srcFile, conflictList[i].destFile); 114 } catch (err) { 115 console.error(TAG, `MoveConflictFile failed, message = ${err.message}, code = ${err.code}`); 116 } 117 } 118 } 119 120 private async moveConflictFile(srcFile : string, dstFile : string) : Promise<void> { 121 let srcStat = fs.statSync(srcFile); 122 let dstStat = fs.statSync(dstFile); 123 if (srcStat.size === dstStat.size) { 124 console.log(TAG, `conflictFile is same, srcFile: [${srcFile}] size: (${srcStat.size}), dstFile: [${dstFile}] size: (${dstStat.size}), return.`); 125 return; 126 } 127 const srcArr = srcFile.split('/'); 128 const dstArr = dstFile.split('/'); 129 const srcFileName = srcArr[srcArr.length - 1]; 130 const dirPath = dstArr.splice(0, dstArr.length - 1).join('/'); 131 let fileExt : string = ''; 132 let fileNameWithoutExt = srcFileName; 133 if (srcFileName.lastIndexOf('.') !== -1) { 134 let tmpValue = srcFileName.split('.').pop(); 135 if (tmpValue !== undefined) { 136 fileExt = tmpValue; 137 fileNameWithoutExt = srcFileName.slice(0, srcFileName.length - fileExt.length - 1); 138 } 139 } 140 let newFileName = srcFileName; 141 let count = 1; 142 while (this.isFileExist(`${dirPath}/${newFileName}`)) { 143 if (fileExt === '') { 144 newFileName = `${fileNameWithoutExt}(${count})`; 145 } else { 146 newFileName = `${fileNameWithoutExt}(${count}).${fileExt}`; 147 } 148 count++; 149 } 150 try { 151 await fs.copyFile(srcFile, `${dirPath}/${newFileName}`); 152 } catch (err) { 153 console.error(TAG, `moveFile file failed, message = ${err.message}; code = ${err.code}`); 154 } 155 } 156}