diff --git a/services/BUILD.gn b/services/BUILD.gn index 263eb6dd3cabcebbd75de44c742252e8111fdd28..7cc0fe5f1165ba6817ad54b21838d7a910536257 100644 --- a/services/BUILD.gn +++ b/services/BUILD.gn @@ -17,6 +17,7 @@ FMS_BASE_DIR = "//foundation/filemanagement/user_file_service/services" group("fms") { deps = [ + ":external_file_manager_hap", ":fms_server", ":fms_service.cfg", ] @@ -91,3 +92,33 @@ ohos_shared_library("fms_server") { "samgr_standard:samgr_proxy", ] } + +ohos_hap("external_file_manager_hap") { + hap_profile = "file_extension_hap/entry/src/main/module.json" + deps = [ + ":external_file_manager_js_assets", + ":external_file_manager_resources", + ] + certificate_profile = "signature/fileextension.p7b" + hap_name = "external_file_manager" + subsystem_name = "filemanagement" + part_name = "user_file_service" + module_install_dir = "app/com.ohos.UserFile.ExternalFileManager" +} + +ohos_js_assets("external_file_manager_js_assets") { + hap_profile = "file_extension_hap/entry/src/main/module.json" + ets2abc = true + source_dir = "file_extension_hap/entry/src/main/ets" +} + +ohos_app_scope("external_file_manager_app_profile") { + app_profile = "file_extension_hap/AppScope/app.json" + sources = [ "file_extension_hap/AppScope/resources" ] +} + +ohos_resources("external_file_manager_resources") { + sources = [ "file_extension_hap/entry/src/main/resources" ] + deps = [ ":external_file_manager_app_profile" ] + hap_profile = "file_extension_hap/entry/src/main/module.json" +} diff --git a/services/file_extension_hap/AppScope/app.json b/services/file_extension_hap/AppScope/app.json new file mode 100644 index 0000000000000000000000000000000000000000..9ed7e1acf9559e97986f90dcbbec33f3cfc6bd63 --- /dev/null +++ b/services/file_extension_hap/AppScope/app.json @@ -0,0 +1,11 @@ +{ + "app": { + "bundleName": "com.ohos.UserFile.ExternalFileManager", + "versionCode": 100, + "versionName": "1.0.0", + "minAPIVersion": 9, + "targetAPIVersion": 9, + "icon": "$media:app_icon", + "label": "$string:app_name" + } +} diff --git a/services/file_extension_hap/AppScope/resources/base/element/string.json b/services/file_extension_hap/AppScope/resources/base/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..bdb9ac3627d877c9908763446d259fc514e50c41 --- /dev/null +++ b/services/file_extension_hap/AppScope/resources/base/element/string.json @@ -0,0 +1,8 @@ +{ + "string": [ + { + "name": "app_name", + "value": "ExternalFileManager" + } + ] +} diff --git a/services/file_extension_hap/AppScope/resources/base/media/app_icon.png b/services/file_extension_hap/AppScope/resources/base/media/app_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..ce307a8827bd75456441ceb57d530e4c8d45d36c Binary files /dev/null and b/services/file_extension_hap/AppScope/resources/base/media/app_icon.png differ diff --git a/services/file_extension_hap/build-profile.json5 b/services/file_extension_hap/build-profile.json5 new file mode 100644 index 0000000000000000000000000000000000000000..8fac20813cb081694e75ab50d19a5faac524c66c --- /dev/null +++ b/services/file_extension_hap/build-profile.json5 @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +{ + "app": { + "compileSdkVersion": 9, + "compatibleSdkVersion": 9, + "products": [ + { + "name": "default", + "signingConfig": "default", + } + ] + }, + "modules": [ + { + "name": "entry", + "srcPath": "./entry", + "targets": [ + { + "name": "default", + "applyToProducts": [ + "default" + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/services/file_extension_hap/entry/build-profile.json5 b/services/file_extension_hap/entry/build-profile.json5 new file mode 100644 index 0000000000000000000000000000000000000000..d3089f7ba484c6385983234a1f147c161a5d9cdd --- /dev/null +++ b/services/file_extension_hap/entry/build-profile.json5 @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +{ + "apiType": 'stageMode', + "buildOption": { + }, + "targets": [ + { + "name": "default", + } + ] +} \ No newline at end of file diff --git a/services/file_extension_hap/entry/hvigorfile.js b/services/file_extension_hap/entry/hvigorfile.js new file mode 100644 index 0000000000000000000000000000000000000000..d7720ee6a7aad5c617d1fd2f6fc8c87067bfa32c --- /dev/null +++ b/services/file_extension_hap/entry/hvigorfile.js @@ -0,0 +1,2 @@ +// Script for compiling build behavior. It is built in the build plug-in and cannot be modified currently. +module.exports = require('@ohos/hvigor-ohos-plugin').hapTasks diff --git a/services/file_extension_hap/entry/src/main/ets/Application/AbilityStage.ts b/services/file_extension_hap/entry/src/main/ets/Application/AbilityStage.ts new file mode 100644 index 0000000000000000000000000000000000000000..c4851529e91e899ca93c1f43cbbd1259decafd1f --- /dev/null +++ b/services/file_extension_hap/entry/src/main/ets/Application/AbilityStage.ts @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import AbilityStage from "@ohos.application.AbilityStage" + +export default class MyAbilityStage extends AbilityStage { + onCreate() { + } +} \ No newline at end of file diff --git a/services/file_extension_hap/entry/src/main/ets/FileExtensionAbility/FileExtensionAbility.ts b/services/file_extension_hap/entry/src/main/ets/FileExtensionAbility/FileExtensionAbility.ts new file mode 100644 index 0000000000000000000000000000000000000000..667ec29ce0d338bfd75eff5244387f46744756a2 --- /dev/null +++ b/services/file_extension_hap/entry/src/main/ets/FileExtensionAbility/FileExtensionAbility.ts @@ -0,0 +1,259 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import Extension from '@ohos.application.FileAccessExtensionAbility' +import fileio from '@ohos.fileio' +import { init, delVolumeInfo, getVolumeInfoList } from './VolumeManager' +import { onReceiveEvent } from './Subcriber' +import fileExtensionInfo from "@ohos.fileExtensionInfo" +import appManager from '@ohos.application.appManager'; + +const FLAG = fileExtensionInfo.FLAG; +const BUNDLE_NAME = 'com.ohos.UserFile.ExternalFileManager'; +const DEFAULT_MODE = 0o666; +const CREATE_FILE_FLAGS = 0o100; + +export default class FileExtAbility extends Extension { + onCreate(want) { + init(); + onReceiveEvent(function (data) { + if (data.event == 'usual.event.data.VOLUME_MOUNTED') { + appManager.killProcessesByBundleName(BUNDLE_NAME); + } else { + delVolumeInfo(data.parameters.id); + } + }); + } + + getPath(uri) { + let sep = '://'; + let arr = uri.split(sep); + if (arr.length < 2) { + return uri; + } + let path = uri.replace(arr[0] + sep, ''); + if (arr[1].indexOf('/') > 0) { + path = path.replace(arr[1].split('/')[0], ''); + } + return path; + } + + genNewFileUri(uri, displayName) { + let arr = uri.split('/'); + let newFileUri = uri; + if (arr.pop() == '') { + newFileUri += displayName; + } else { + newFileUri += '/' + displayName; + } + return newFileUri; + } + + getFileName(uri) { + let arr = uri.split('/'); + let name = arr.pop(); + if (name == '') { + name = arr.pop(); + } + return name; + } + + renameUri(uri, displayName) { + let arr = uri.split('/'); + let newFileUri = ''; + if (arr.pop() == '') { + arr.pop(); + arr.push(displayName) + arr.push(''); + } else { + arr.push(displayName) + } + for (var index = 0; index < arr.length; index++) { + if (arr[index] == '') { + newFileUri += '/'; + } else if (index == arr.length - 1) { + newFileUri += arr[index]; + } else { + newFileUri += arr[index] + '/'; + } + } + return newFileUri; + } + + listDir(path, cb) { + try { + let stat = fileio.statSync(path); + if (stat.isDirectory()) { + let dir = fileio.opendirSync(path); + let hasNextFile = true; + while (hasNextFile) { + try { + let dirent = dir.readSync(); + this.listDir(path + '/' + dirent.name, cb); + } catch (e) { + hasNextFile = false; + cb(path, true); + } + } + } else { + cb(path, false); + } + } catch (e) { + cb(path, true); + } + } + + openFile(sourceFileUri, flags) { + let fd = 0; + try { + let path = this.getPath(sourceFileUri); + fd = fileio.openSync(path, flags, DEFAULT_MODE); + } catch (e) { + fd = -1; + } + + return fd; + } + + closeFile(fd, uri) { + try { + fileio.closeSync(fd); + return true; + } catch (e) { + return false; + } + } + + createFile(parentUri, displayName) { + try { + let newFileUri = this.genNewFileUri(parentUri, displayName); + let path = this.getPath(newFileUri); + fileio.openSync(path, CREATE_FILE_FLAGS, DEFAULT_MODE); + return newFileUri; + } catch (e) { + return ''; + } + } + + mkdir(parentUri, displayName) { + try { + let newFileUri = this.genNewFileUri(parentUri, displayName); + let path = this.getPath(newFileUri); + fileio.mkdirSync(path); + return newFileUri; + } catch (e) { + return ''; + } + } + + delete(selectFileUri) { + let path = this.getPath(selectFileUri); + let code = 0; + this.listDir(path, function (filePath, isDirectory) { + try { + if (isDirectory) { + fileio.rmdirSync(filePath); + } else { + fileio.unlinkSync(filePath); + } + } catch (e) { + code = -1; + } + }); + + return code; + } + + move(sourceFileUri, targetParentUri) { + try { + let displayName = this.getFileName(sourceFileUri); + let newFileUri = this.genNewFileUri(targetParentUri, displayName); + let oldPath = this.getPath(sourceFileUri); + let newPath = this.getPath(newFileUri); + fileio.renameSync(oldPath, newPath); + return newFileUri; + } catch (e) { + return ''; + } + } + + rename(sourceFileUri, displayName) { + try { + let newFileUri = this.renameUri(sourceFileUri, displayName); + let oldPath = this.getPath(sourceFileUri); + let newPath = this.getPath(newFileUri); + fileio.renameSync(oldPath, newPath); + return newFileUri; + } catch (e) { + return ''; + } + } + + query(sourceFileUri) { + try { + let path = this.getPath(sourceFileUri); + let stat = fileio.statSync(path); + return { + uri: sourceFileUri, + fileName: this.getFileName(sourceFileUri), + mode: '' + stat.mode, + size: stat.size, + mtime: stat.mtime, + mimeType: '', + }; + } catch (e) { + return null; + } + } + + listFile(sourceFileUri) { + let infos = []; + try { + let path = this.getPath(sourceFileUri); + let dir = fileio.opendirSync(path); + let hasNextFile = true; + while (hasNextFile) { + try { + let dirent = dir.readSync(); + let stat = fileio.statSync(path + '/' + dirent.name); + infos.push({ + uri: this.genNewFileUri(sourceFileUri, dirent.name), + fileName: dirent.name, + mode: '' + stat.mode, + size: stat.size, + mtime: stat.mtime, + mimeType: '', + }); + } catch (e) { + hasNextFile = false; + } + } + } catch (e) { + infos = []; + } + + return infos; + } + + getRoots() { + let roots = getVolumeInfoList().concat({ + uri: 'fileAccess:///data/storage/el1/bundle/storage_daemon', + displayName: 'storage_daemon', + deviceId: '', + flags: FLAG.SUPPORTS_WRITE | FLAG.SUPPORTS_DELETE | FLAG.SUPPORTS_RENAME | FLAG.SUPPORTS_COPY | + FLAG.SUPPORTS_MOVE | FLAG.SUPPORTS_REMOVE | FLAG.DIR_SUPPORTS_CREATE | FLAG.DIR_PREFERS_LAST_MODIFIED, + }); + return roots; + } +}; \ No newline at end of file diff --git a/services/file_extension_hap/entry/src/main/ets/FileExtensionAbility/Subcriber.ts b/services/file_extension_hap/entry/src/main/ets/FileExtensionAbility/Subcriber.ts new file mode 100644 index 0000000000000000000000000000000000000000..a2e8acec2c7f994d5fc3b806ba862065901f984d --- /dev/null +++ b/services/file_extension_hap/entry/src/main/ets/FileExtensionAbility/Subcriber.ts @@ -0,0 +1,32 @@ +/* +* Copyright (c) 2022 Huawei Device Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +import CommonEvent from '@ohos.commonevent' +async function onReceiveEvent(callBack) { + var subscribeInfo = { + events: [ + "usual.event.data.VOLUME_REMOVED", + "usual.event.data.VOLUME_UNMOUNTED", + "usual.event.data.VOLUME_MOUNTED", + "usual.event.data.VOLUME_BAD_REMOVAL", + "usual.event.data.VOLUME_EJECT" + ] + }; + let subscriber = await CommonEvent.createSubscriber(subscribeInfo); + CommonEvent.subscribe(subscriber, function (err, data) { + callBack(data) + }) +} + +export { onReceiveEvent } \ No newline at end of file diff --git a/services/file_extension_hap/entry/src/main/ets/FileExtensionAbility/VolumeManager.ts b/services/file_extension_hap/entry/src/main/ets/FileExtensionAbility/VolumeManager.ts new file mode 100644 index 0000000000000000000000000000000000000000..63c5bf912c828ccb245e84b466ae52a82840c959 --- /dev/null +++ b/services/file_extension_hap/entry/src/main/ets/FileExtensionAbility/VolumeManager.ts @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import volumeManager from '@ohos.volumeManager' +import fileExtensionInfo from "@ohos.fileExtensionInfo" +if (!globalThis.volumeInfoList) { + globalThis.volumeInfoList = []; +} + +const FLAG = fileExtensionInfo.FLAG; + +function init() { + volumeManager.getAllVolumes().then((volumes) => { + let flags = FLAG.SUPPORTS_WRITE | FLAG.SUPPORTS_DELETE | FLAG.SUPPORTS_RENAME | FLAG.SUPPORTS_COPY | + FLAG.SUPPORTS_MOVE | FLAG.SUPPORTS_REMOVE | FLAG.DIR_SUPPORTS_CREATE | FLAG.DIR_PREFERS_LAST_MODIFIED; + for (let i = 0; i < volumes.length; i++) { + let volume = volumes[i]; + let volumeInfo = { + 'volumeId': volume.id, + 'fsUuid': volume.uuid, + 'path': volume.path, + 'uri': path2uri('', volume.path), + 'displayName': volume.id, + 'deviceId': '', + 'flags': flags, + 'type': 'SD' + } + globalThis.volumeInfoList.push(volumeInfo); + } + }); +} + +function addVolumeInfo(volumeInfo) { + globalThis.volumeInfoList.push(volumeInfo); +} + +function path2uri(id, path) { + return `fileAccess://${id}${path}`; +} + +function delVolumeInfo(volumeId) { + globalThis.volumeInfoList = globalThis.volumeInfoList.filter((volume) => volume.volumeId !== volumeId); +} + +function getVolumeInfoList() { + return globalThis.volumeInfoList; +} + +export { init, addVolumeInfo, delVolumeInfo, getVolumeInfoList, path2uri } \ No newline at end of file diff --git a/services/file_extension_hap/entry/src/main/module.json b/services/file_extension_hap/entry/src/main/module.json new file mode 100644 index 0000000000000000000000000000000000000000..3908c63b3421cbcc5c56f8a048201758823e3c5b --- /dev/null +++ b/services/file_extension_hap/entry/src/main/module.json @@ -0,0 +1,33 @@ +{ + "module": { + "name": "entry", + "type": "entry", + "srcEntrance": "./ets/Application/AbilityStage.ts", + "mainElement": "FileExtensionAbility", + "deviceTypes": [ + "phone", + "tablet" + ], + "deliveryWithInstall": true, + "installationFree": false, + "pages": "$profile:main_pages", + "uiSyntax": "ets", + "abilities": [], + "extensionAbilities": [ + { + "srcEntrance": "./ets/FileExtensionAbility/FileExtensionAbility.ts", + "name": "FileExtensionAbility", + "type": "fileAccess", + "visible": true + } + ], + "requestPermissions": [ + { + "name": "ohos.permission.CLEAN_BACKGROUND_PROCESSES" + }, + { + "name": "ohos.permission.STORAGE_MANAGER" + } + ] + } +} \ No newline at end of file diff --git a/services/file_extension_hap/entry/src/main/resources/base/element/string.json b/services/file_extension_hap/entry/src/main/resources/base/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..c4e8611bfbd8bb3177924f05e7e22a7ca3dcf5dd --- /dev/null +++ b/services/file_extension_hap/entry/src/main/resources/base/element/string.json @@ -0,0 +1,12 @@ +{ + "string": [ + { + "name": "entry_desc", + "value": "description" + }, + { + "name": "description_serviceability", + "value": "description_serviceability" + } + ] +} \ No newline at end of file diff --git a/services/file_extension_hap/entry/src/main/resources/base/media/icon.png b/services/file_extension_hap/entry/src/main/resources/base/media/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..ce307a8827bd75456441ceb57d530e4c8d45d36c Binary files /dev/null and b/services/file_extension_hap/entry/src/main/resources/base/media/icon.png differ diff --git a/services/file_extension_hap/entry/src/main/resources/base/profile/main_pages.json b/services/file_extension_hap/entry/src/main/resources/base/profile/main_pages.json new file mode 100644 index 0000000000000000000000000000000000000000..b12d9e9faa33835d362c73333632d70b7c61328a --- /dev/null +++ b/services/file_extension_hap/entry/src/main/resources/base/profile/main_pages.json @@ -0,0 +1,3 @@ +{ + "src": [] +} \ No newline at end of file diff --git a/services/file_extension_hap/hvigorfile.js b/services/file_extension_hap/hvigorfile.js new file mode 100644 index 0000000000000000000000000000000000000000..5f2735e3deeaf655828407544bbed9365c258278 --- /dev/null +++ b/services/file_extension_hap/hvigorfile.js @@ -0,0 +1,2 @@ +// Script for compiling build behavior. It is built in the build plug-in and cannot be modified currently. +module.exports = require('@ohos/hvigor-ohos-plugin').appTasks \ No newline at end of file diff --git a/services/signature/fileextension.p7b b/services/signature/fileextension.p7b new file mode 100644 index 0000000000000000000000000000000000000000..66b4457a8a81fb8d3356cf46d67226c850944858 Binary files /dev/null and b/services/signature/fileextension.p7b differ