diff --git a/build-profile.json5 b/build-profile.json5 index 2a22fd2..d9644af 100644 --- a/build-profile.json5 +++ b/build-profile.json5 @@ -1,32 +1,32 @@ { "app": { - "signingConfigs": [ - { - "name": "default", - "type": "HarmonyOS", - "material": { - "certpath": "C:\\Users\\wps\\.ohos\\config\\default_DataMigration_nuljKEfmRXHB8PLGEfuYN4YV1lGSGbA9v0wpbuwfmqk=.cer", - "storePassword": "0000001B5E9448F6AD9BADDA4C83D493A5E5F48217711679002A2C5CD29AC48C63630FE6471EB67921842A", - "keyAlias": "debugKey", - "keyPassword": "0000001B0EAED5D90BF4B127B068A11A8BABCB79E7B088CF931A3B521BC9C308CE7E18162699E1E1A760DC", - "profile": "C:\\Users\\wps\\.ohos\\config\\default_DataMigration_nuljKEfmRXHB8PLGEfuYN4YV1lGSGbA9v0wpbuwfmqk=.p7b", - "signAlg": "SHA256withECDSA", - "storeFile": "C:\\Users\\wps\\.ohos\\config\\default_DataMigration_nuljKEfmRXHB8PLGEfuYN4YV1lGSGbA9v0wpbuwfmqk=.p12" - } - } - ], "products": [ { "name": "default", "signingConfig": "default", "compatibleSdkVersion": "5.0.0(12)", - "runtimeOS": "HarmonyOS", + "runtimeOS": "HarmonyOS" } ], "buildModeSet": [ { "name": "release" } + ], + "signingConfigs": [ + { + "name": "default", + "type": "HarmonyOS", + "material": { + "certpath": "C:\\Users\\wps\\.ohos\\config\\default_data-migration_S_iyoHO2Pe6xZO7IdhIarmJTXCDCp-PziZ7vAZHB4rE=.cer", + "storePassword": "0000001B0DD271B6C65A9B107348E6ABEBD224452AC1A56999CE4DA489364C4F3E5325F88275CCC4B7BDFE", + "keyAlias": "debugKey", + "keyPassword": "0000001B0A39C50188A1F19EA6524A1E411EFA6758940BDC65B8DBD0365861504659BD66440F0CFAAB96AE", + "profile": "C:\\Users\\wps\\.ohos\\config\\default_data-migration_S_iyoHO2Pe6xZO7IdhIarmJTXCDCp-PziZ7vAZHB4rE=.p7b", + "signAlg": "SHA256withECDSA", + "storeFile": "C:\\Users\\wps\\.ohos\\config\\default_data-migration_S_iyoHO2Pe6xZO7IdhIarmJTXCDCp-PziZ7vAZHB4rE=.p12" + } + } ] }, "modules": [ @@ -43,4 +43,4 @@ ] } ] -} \ No newline at end of file +} diff --git a/entry/src/main/ets/backupExtension/BackupExtension.ets b/entry/src/main/ets/backupExtension/BackupExtension.ets index 706bdad..4c9fae1 100644 --- a/entry/src/main/ets/backupExtension/BackupExtension.ets +++ b/entry/src/main/ets/backupExtension/BackupExtension.ets @@ -1,9 +1,9 @@ import { BackupExtensionAbility, BundleVersion, fileUri } from '@kit.CoreFileKit'; -import fs from '@ohos.file.fs'; -import { preferences, relationalStore } from '@kit.ArkData'; import { bundleManager } from '@kit.AbilityKit'; -import { JSON, util } from '@kit.ArkTS'; -import File from '@system.file'; +import { UserDataTransfer } from './transfers/UserDataTransfer'; +import { SettingDataTransfer } from './transfers/SettingDataTransfer'; +import { SpTransferManager } from './SpTransferManager'; +import { MmkvTransferManager } from './MmkvTransferManager'; const TAG = `BackupExtensionAbility1` const BundleName = bundleManager.getBundleInfoForSelfSync(bundleManager.BundleFlag.GET_BUNDLE_INFO_DEFAULT).name @@ -31,26 +31,36 @@ export default class BackupExtension extends BackupExtensionAbility { } async restoreData() { - const databasePath = `/data/storage/el2/backup/restore/${BundleName}` + '/ce/databases/qingsdk2.db' - console.log(TAG, '1' + fs.statSync(databasePath).size) - if (!fs.accessSync(DatabaseDir)) { - fs.mkdirSync(DatabaseDir, true) - } - console.log(TAG, '2' + fs.accessSync(DatabaseDir)) - const dbFile = fs.openSync(databasePath, fs.OpenMode.READ_ONLY) - // fs.copyFileSync(dbFile.fd, DatabaseDir + '/qingsdk2.db') - // fs.closeSync(dbFile.fd) - console.log(TAG, '3' + fs.accessSync(DatabaseDir + '/qingsdk2.db')) - const rdbStore = await relationalStore.getRdbStore(this.context.getApplicationContext(), { - name: 'qingsdk2.db', - securityLevel: relationalStore.SecurityLevel.S1, - customDir: 'qingsdk2.db' - }) - const pre = new relationalStore.RdbPredicates('roaming_list') - pre.equalTo('userid', '1510872069') - rdbStore.query(pre, (err, res) => { - console.log(TAG, JSON.stringify(res)) - }) + const context = this.context.getApplicationContext() + const mSpTransferManager = new SpTransferManager() + const mMmkvTransferManager = new MmkvTransferManager() + + const mUserDataTransfer = new UserDataTransfer(context, mMmkvTransferManager, mSpTransferManager) + const mSettingDataTransfer = new SettingDataTransfer(context, mMmkvTransferManager, mSpTransferManager) + + mUserDataTransfer.execute() + mSettingDataTransfer.execute() + + // const databasePath = `/data/storage/el2/backup/restore/${BundleName}` + '/ce/databases/qingsdk2.db' + // console.log(TAG, '1' + fs.statSync(databasePath).size) + // if (!fs.accessSync(DatabaseDir)) { + // fs.mkdirSync(DatabaseDir, true) + // } + // console.log(TAG, '2' + fs.accessSync(DatabaseDir)) + // const dbFile = fs.openSync(databasePath, fs.OpenMode.READ_ONLY) + // // fs.copyFileSync(dbFile.fd, DatabaseDir + '/qingsdk2.db') + // // fs.closeSync(dbFile.fd) + // console.log(TAG, '3' + fs.accessSync(DatabaseDir + '/qingsdk2.db')) + // const rdbStore = await relationalStore.getRdbStore(this.context.getApplicationContext(), { + // name: 'qingsdk2.db', + // securityLevel: relationalStore.SecurityLevel.S1, + // customDir: 'qingsdk2.db' + // }) + // const pre = new relationalStore.RdbPredicates('roaming_list') + // pre.equalTo('userid', '1510872069') + // rdbStore.query(pre, (err, res) => { + // console.log(TAG, JSON.stringify(res)) + // }) // const fileRoot = backupDir + '/A/data/.Cloud/cn/' // const userIds = fs.listFileSync(fileRoot) // const docDir = this.context.filesDir + '/document' @@ -95,7 +105,7 @@ export default class BackupExtension extends BackupExtensionAbility { // }) } - async transferDb(){ + async transferDb() { } diff --git a/entry/src/main/ets/backupExtension/MmkvTransferManager.ets b/entry/src/main/ets/backupExtension/MmkvTransferManager.ets index 6bba519..4fa6ba1 100644 --- a/entry/src/main/ets/backupExtension/MmkvTransferManager.ets +++ b/entry/src/main/ets/backupExtension/MmkvTransferManager.ets @@ -6,8 +6,10 @@ import { MMKV } from '@ohos/MMKV'; // mmkv值类型定义 type MmkvValueType = number | Set | string | boolean; - export class MmkvTransferManager extends BaseTransferManager { + private TAG: string = "MmkvTransferManager"; + private crpKey = 'DEj%^34g' + /** * 移动双框架mmkv文件到单框架指定目录 * 文件参考@link FileTransferManager.moveFiles @@ -15,7 +17,7 @@ export class MmkvTransferManager extends BaseTransferManager { * @param context context上下文 * @return 成功返回true, 否则false */ - private async moveMmkvFile(context: common.Context): Promise { + moveMmkvFile(context: common.Context) { try { // 双升单数据迁移后,mmkv默认在files/mmkv目录下 let sourceMmkvPath: string = `/data/storage/el2/backup/restore/${this.BundleName}/ce/files/mmkv`; @@ -51,7 +53,6 @@ export class MmkvTransferManager extends BaseTransferManager { } } - /** * 解析双框架mmkv文件 * 当前例子中: @@ -72,17 +73,19 @@ export class MmkvTransferManager extends BaseTransferManager { // mmkv文件名 let mmapID: string = 'source_mmkv'; // 秘钥,根据实际情况指定秘钥, 当前未设置秘钥 - let crpKey: string = ''; + let crpKey: string = '44456a255e333467'; mmkv = MMKV.getBackedUpMMKVWithID(mmapID, MMKV.SINGLE_PROCESS_MODE, crpKey, ''); // 注:当前mmkv对于双框架mmkv设置的int long float的类型暂不支持,待后续更新 // dataMap.set('intData', mmkv.decodeNumber('intData', 0)); // dataMap.set('longData', mmkv.decodeNumber('longData', 0)); // dataMap.set('floatData', mmkv.decodeNumber('floatData', 0)); + dataMap.set('doubleData', mmkv.decodeNumber('doubleData', 0)); dataMap.set('setData', mmkv.decodeSet('setData', new Set())); dataMap.set('boolData', mmkv.decodeBool('boolData', true)); dataMap.set('stringData', mmkv.decodeString('stringData', '')); + } catch (error) { // 异常处理逻辑,自行实现 } finally { @@ -91,6 +94,21 @@ export class MmkvTransferManager extends BaseTransferManager { return dataMap; } + + /** + * 解析双框架 单个mmkv文件 + */ + parseSingleSourceMmkvInfo(context: common.Context, mmapID: string): MMKV { + let mmkv: MMKV; + const rootPath = `/data/storage/el2/backup/restore/${this.BundleName}/ce/files/mmkv`; + // const rootPath = context.filesDir + '/mmkv' + const cacheDir = context.cacheDir + '/mmkv' + + MMKV.initialize(rootPath, cacheDir) + mmkv = MMKV.getMMKVWithMMapID(mmapID, MMKV.SINGLE_PROCESS_MODE, this.crpKey) + return mmkv; + } + /** * 插入数据到单的mmkv中 * @@ -106,7 +124,7 @@ export class MmkvTransferManager extends BaseTransferManager { // mmkv文件名 let mmapID: string = 'target_mmkv'; // 秘钥,根据实际情况指定秘钥, 当前未设置秘钥 - let crpKey: string = ''; + let crpKey: string = '44456a255e333467'; mmkv = MMKV.getMMKVWithMMapID(mmapID, MMKV.SINGLE_PROCESS_MODE, crpKey, ''); // 设置请求参数中的值 @@ -143,7 +161,7 @@ export class MmkvTransferManager extends BaseTransferManager { // mmkv文件名 let mmapID: string = 'target_mmkv'; // 秘钥,根据实际情况指定秘钥, 当前未设置秘钥 - let crpKey: string = ''; + let crpKey: string = '44456a255e333467'; mmkv = MMKV.getMMKVWithMMapID(mmapID, MMKV.SINGLE_PROCESS_MODE, crpKey, ''); // 取值,根据mmkv中实际存在的key来取值 diff --git a/entry/src/main/ets/backupExtension/SpTransferManager.ets b/entry/src/main/ets/backupExtension/SpTransferManager.ets index f6858bf..36d1297 100644 --- a/entry/src/main/ets/backupExtension/SpTransferManager.ets +++ b/entry/src/main/ets/backupExtension/SpTransferManager.ets @@ -21,6 +21,8 @@ interface XmlElement { } export class SpTransferManager extends BaseTransferManager { + private TAG: string = 'SpTransferManager'; + /** * 双框架sp文件移动到单框架 * 文件迁移参考@link FileTransferManager.moveFiles @@ -28,7 +30,7 @@ export class SpTransferManager extends BaseTransferManager { * @param context context上下文 * @return 成功返回true, 否则false */ - private moveSpFile(context: common.Context): boolean { + moveSpFile(context: common.Context): boolean { try { // 双升单数据迁移后,sp文件默认在shared_prefs目录下 let sourceSpPath = `/data/storage/el2/backup/restore/${this.BundleName}/ce/shared_prefs`; @@ -60,7 +62,6 @@ export class SpTransferManager extends BaseTransferManager { } } - /** * 读取双的sp xml文件的文本内容 * 当前例子中: @@ -115,6 +116,53 @@ export class SpTransferManager extends BaseTransferManager { return xmlContent; } + private getSourceXmlStrFromPath(context: common.Context, sourcePath: string): string { + let xmlContent: string = ''; + try { + // 双sp经过moveSpFile后的文件路径 + let sourceSpFilePath: string = `${sourcePath}`; + // 得到双sp xml文本内容 + let fileStream = fs.createStreamSync(sourceSpFilePath, 'r'); + if (!fileStream) { + return xmlContent; + } + + let bufArray: ArrayBuffer[] = []; + let totalLength = 0; + const READ_DATA_SIZE = 1024 * 1024; + let buffer = new ArrayBuffer(READ_DATA_SIZE); + let len = fileStream.readSync(buffer); + while (len !== 0) { + totalLength += len; + if (len < READ_DATA_SIZE) { + buffer = buffer.slice(0, len); + bufArray.push(buffer); + break; + } + bufArray.push(buffer); + buffer = new ArrayBuffer(READ_DATA_SIZE); + len = fs.readSync(Number(fileStream), buffer); + } + let contentBuffer = new Uint8Array(totalLength); + let offset = 0; + bufArray.forEach(bufferArr => { + let uInt8Arr = new Uint8Array(bufferArr); + contentBuffer.set(uInt8Arr, offset); + offset += uInt8Arr.byteLength; + }); + let textDecoder = util.TextDecoder.create('utf-8', { + ignoreBOM: false + }) + xmlContent = textDecoder.decodeWithStream(new Uint8Array(contentBuffer), { + stream: false + }); + } catch (error) { + // 异常处理 + } + return xmlContent; + } + + /** * 转换双框架sp xml的文本内容为XmlElement对象 * 详情请参考:https://docs.openharmony.cn/pages/v4.0/zh-cn/application-dev/arkts-utils/xmlconversion.md @@ -226,34 +274,6 @@ export class SpTransferManager extends BaseTransferManager { return dataMap; } - /** - * 将数据插入到单的sp中 - * 当前例子中,单的sp文件名为:target_sp.xml - * - * @param context context上下文 - * @param dataMap 待插入的数据 - * @return boolean 成功返回true, 否则false - */ - private async insertDataToTargetSp(context: Context, dataMap: Map): Promise { - try { - // 获取单框架sp (可以自行提供工具类获取sp对象用于初始化,进行读、写),当前例子中单的sp名为: - // target_sp.xml,没有则创建;以实际为准 - let targetSp = await preferences.getPreferences(context, 'target_sp.xml'); - dataMap.forEach((value, key) => { - targetSp.put(key, value); - }) - - // 插入sp后需要调用flush,保存到文件 - targetSp.flush(() => { - // flush 成功的回调逻辑,自行实现 - }); - return true; - } catch (error) { - // 异常处理 - return false; - } - } /** * 从单的sp中读取数据 @@ -304,4 +324,42 @@ export class SpTransferManager extends BaseTransferManager { const androidId = preMap.get(key) as string return androidId } + + getDataFromSourceSp(context: Context, spName: string) { + const sourceSpPath = `/data/storage/el2/backup/restore/${this.BundleName}/ce/shared_prefs`; + const spPath = `${sourceSpPath}/${spName}` + const spContent = this.getSourceXmlStrFromPath(context, spPath) + console.log(this.TAG, 'spContent: ' + spContent) + const spXmlElement = this.transferSourceXmlToXmlElement(spContent) + return this.parseXmlElement(spXmlElement) + } + + /** + * 将数据插入到单的sp中 + * 当前例子中,单的sp文件名为:target_sp.xml + * + * @param context context上下文 + * @param dataMap 待插入的数据 + * @return boolean 成功返回true, 否则false + */ + insertDataToTargetSp(context: Context, spName: string, dataMap: Map) { + try { + // 获取单框架sp (可以自行提供工具类获取sp对象用于初始化,进行读、写),当前例子中单的sp名为: + // target_sp.xml,没有则创建;以实际为准 + const targetSp = preferences.getPreferencesSync(context, { name: spName }); + dataMap.forEach((value, key) => { + targetSp.putSync(key, value); + }) + + // 插入sp后需要调用flush,保存到文件 + targetSp.flush(() => { + // flush 成功的回调逻辑,自行实现 + }); + return true; + } catch (error) { + // 异常处理 + return false; + } + } + } \ No newline at end of file diff --git a/entry/src/main/ets/backupExtension/transfers/SettingDataTransfer.ets b/entry/src/main/ets/backupExtension/transfers/SettingDataTransfer.ets new file mode 100644 index 0000000..7236826 --- /dev/null +++ b/entry/src/main/ets/backupExtension/transfers/SettingDataTransfer.ets @@ -0,0 +1,38 @@ +import { MmkvTransferManager } from '../MmkvTransferManager' +import { common } from '@kit.AbilityKit' +import { SpTransferManager } from '../SpTransferManager' + +export class SettingDataTransfer { + private TAG = 'SettingDataTransfer' + private mMmkvTransferManager: MmkvTransferManager + private mSpTransferManager: SpTransferManager + private context: common.Context + + constructor(context: common.Context, mMmkvTransferManager: MmkvTransferManager, + mSpTransferManager: SpTransferManager) { + this.context = context + + this.mSpTransferManager = mSpTransferManager + this.mMmkvTransferManager = mMmkvTransferManager + } + + execute() { + const mmkv_public_default = this.mMmkvTransferManager.parseSingleSourceMmkvInfo(this.context, 'public_default') + const fileradar_auto_upload_only_wifi_USERID = mmkv_public_default?.decodeBool('fileradar_auto_upload_only_wifi_USERID', false) + + const sp_auto_upload_switch = this.mSpTransferManager.getDataFromSourceSp(this.context, 'auto_upload_switch.xml') + // write local preferences + const spMap = new Map() + + for (const key of sp_auto_upload_switch.keys()) { + const userId = key + const auto_upload_switch: boolean = sp_auto_upload_switch.get(userId) as boolean + spMap.set(`${userId}/docSync`, auto_upload_switch) + spMap.set(`${userId}/uploadOnlyWlan`, fileradar_auto_upload_only_wifi_USERID) + } + + this.mSpTransferManager.insertDataToTargetSp(this.context, 'setting', spMap) + + mmkv_public_default?.close() + } +} diff --git a/entry/src/main/ets/backupExtension/transfers/UserDataTransfer.ets b/entry/src/main/ets/backupExtension/transfers/UserDataTransfer.ets new file mode 100644 index 0000000..896d5ac --- /dev/null +++ b/entry/src/main/ets/backupExtension/transfers/UserDataTransfer.ets @@ -0,0 +1,122 @@ +import { common } from '@kit.AbilityKit' +import { MmkvTransferManager } from '../MmkvTransferManager' +import { SpTransferManager } from '../SpTransferManager' +import { preferences } from '@kit.ArkData' +import { buffer } from '@kit.ArkTS' + +export class UserDataTransfer { + private TAG = 'UserDataTransfer' + private mMmkvTransferManager: MmkvTransferManager + private mSpTransferManager: SpTransferManager + private context: common.Context + + constructor(context: common.Context, mMmkvTransferManager: MmkvTransferManager, + mSpTransferManager: SpTransferManager) { + this.context = context + this.mMmkvTransferManager = mMmkvTransferManager + this.mSpTransferManager = mSpTransferManager + } + + execute() { + // 读取mmkv + const mmkv_login_config = this.mMmkvTransferManager.parseSingleSourceMmkvInfo(this.context, 'login_config') + + console.log(this.TAG, 'keys ' + mmkv_login_config.getAllKeys()) + + const CLOUD_QING_USER_WORKSPACE = mmkv_login_config.decodeString('CLOUD_QING_USER_WORKSPACE', '') + const CLOUD_QING_USER_ID = mmkv_login_config.decodeString('CLOUD_QING_USER_ID', '') + const CLOUD_QING_USER_WORKSPACE_INFO_xxxx = mmkv_login_config.decodeString('CLOUD_QING_USER_WORKSPACE_INFO_41000207', '') // company id + const CLOUD_QING_SESSION = mmkv_login_config.decodeString('CLOUD_QING_SESSION', '') + const CLOUD_QING_ACCOUNT_TYPE = mmkv_login_config.decodeString('CLOUD_QING_ACCOUNT_TYPE', '') + const CLOUD_QING_WPS_USERINFO = mmkv_login_config.decodeString('CLOUD_QING_WPS_USERINFO', '') + const CLOUD_LOGIN_USERS = mmkv_login_config.decodeString('CLOUD_LOGIN_USERS', '') + const CLOUD_QING_MULTI_ACCOUNT = mmkv_login_config.decodeString('CLOUD_QING_MULTI_ACCOUNT', '') + + const user_name = mmkv_login_config.decodeString('user_name', '') + const user_source_login_type = mmkv_login_config.decodeString('user_source_login_type', '') + const user_id = mmkv_login_config.decodeString('user_id', '') + const user_login_type = mmkv_login_config.decodeString('user_login_type', '') + const user_wpssids = mmkv_login_config.decodeString('user_wpssids', '') + const user_wpssid = mmkv_login_config.decodeString('user_wpssid', '') + const user_avatar_url = mmkv_login_config.decodeString('user_avatar_url', '') + + const not_account_request_time = mmkv_login_config.decodeString('not_account_request_time', '') + const not_account_request_num = mmkv_login_config.decodeString('not_account_request_num', '') + const login_limit_account = mmkv_login_config.decodeNumber('login_limit_account', 1) + + mmkv_login_config.close() + + // process data + // get session + const sessionRow = CLOUD_QING_SESSION + const sessionJsonStr = buffer.from(sessionRow, "base64").toString("utf-8"); + const sessionJsonObj: Record = JSON.parse(sessionJsonStr) + const sessionStr = this.session2String(sessionJsonObj) + console.log(this.TAG, 'sessionStr ' + JSON.stringify(sessionStr)) + // get userInfo + const loginUsers: Record[] = JSON.parse(CLOUD_LOGIN_USERS) + const currentUser = loginUsers.find(user => user['is_current']) ?? {} + console.log(this.TAG, 'currentUser ' + JSON.stringify(currentUser)) + const userInfo: Record = { + "account": "", + "account_num": 1, + "address": "", + "city": "", + "companyid": currentUser['company_id'], + "country": "", + "current_companyid": 0, + "departmentid": "", + "email": "", + "firstname": "", + "is_company_account": currentUser['is_company_account'], + "is_plus": true, + "lastname": "", + "loginmode": "phone_sms:+86***862", + "nickname": user_name, + "phonenumber": "130***862", + "pic": user_avatar_url, + "postal": "", + "province": "", + "regtime": 1566368398, + "result": "ok", + "role": ["user"], + "sex": "male", + "status": "active", + "userid": user_id, + "uzone": "CN-HN" + } + + // write into preferences + const accountSpData: Map = new Map() + accountSpData.set('session', sessionStr) + accountSpData.set('userInfo', JSON.stringify(userInfo)) + const authSpData = new Map() + authSpData.set('auth_policy', true) + + this.mSpTransferManager.insertDataToTargetSp(this.context, 'account', accountSpData) + this.mSpTransferManager.insertDataToTargetSp(this.context, 'auth', authSpData) + } + + private session2String(session: Record) { + const authkeypair: Record = session['authkeypair'] as Record + session['accessid'] = authkeypair['access_id'] + session['secretkey'] = authkeypair['secret_key'] + session['authkeypair'] = '' + + const strings: string[] = [] + Object.getOwnPropertyNames(session).forEach(key => { + strings.push(`${key}=${session[key]}`) + }) + + return strings.join('; ') + } + + private processUserWpssids(wpssids: string) { + const wpssidList = wpssids.trim().split('\n') + return wpssidList.join(',') + } + + private processUserWpsid(wpssid: string) { + return wpssid.trim() + } +} diff --git a/oh-package.json5 b/oh-package.json5 index 688e9d6..0db7e81 100644 --- a/oh-package.json5 +++ b/oh-package.json5 @@ -7,7 +7,7 @@ "author": "", "license": "", "dependencies": { - "@ohos/mmkv": "^2.0.3" + "@ohos/mmkv": "2.0.4-rc.4" }, "devDependencies": { "@ohos/hypium": "1.0.17",