【本文正在参加 2023「盲盒」+码有奖征文活动】 https://ost.51cto.com/posts/25284
序言
之前在《OpenHarmony设备直装hap脱离电脑hdc的两种实现思路》一文中,我提到了两种实现直装hap的思路,那本篇文章我将详细讲解下第一种思路的具体实现方式,至于第二种另辟蹊径的实现思路,我会在后续的文章中继续分享实现原理。
第一种思路是用到了官方的包管理模块的API,由于包管理的installer模块接口的权限,需要 ohos.permission.INSTALL_BUNDLE
system_core
级别的系统权限,所以我们需要配置项目权限和访问控制权限
项目权限配置
entry/src/main/module.json5
{
"name": "ohos.permission.INSTALL_BUNDLE",
},
访问控制权限配置
openharmony-sdk\9\toolchains\lib\UnsgnedReleasedProfileTemplate.json
"apl":"system_core",
"app-feature":"hos_system_app"
安装应用
- hap包要放到设备真实路径:
/data/app/el2/用户ID/base/应用包名/haps/entry/files/
- hapFilePaths写沙箱路径:
/data/storage/el2/base/haps/entry/files/
import installer from '@ohos.bundle.installer';
import promptAction from '@ohos.promptAction';
function installApp() {
let hapFilePaths = ['/data/storage/el2/base/haps/entry/files/testapp.hap'];
let installParam = {
userId: 100,
isKeepData: false,
installFlag: 1,
};
try {
installer.getBundleInstaller().then(data => {
data.install(hapFilePaths, installParam, err => {
if (err) {
console.error('install failed:' + err.message);
promptAction.showToast({message: '安装失败:' + err.message, duration: ToastDuration});
} else {
console.info('install successfully.');
promptAction.showToast({message: '安装成功', duration: ToastDuration});
}
});
}).catch(error => {
console.error('getBundleInstaller failed. Cause: ' + error.message);
promptAction.showToast({message: '安装失败:' + error.message, duration: ToastDuration});
});
} catch (error) {
console.error('getBundleInstaller failed. Cause: ' + error.message);
promptAction.showToast({message: '安装失败:' + error.message, duration: ToastDuration});
}
}
打开应用
指定包名和abilityName,调用context.startAbility打开目标应用
import common from '@ohos.app.ability.common';
import promptAction from '@ohos.promptAction';
function openApp() {
let context = getContext(this) as common.UIAbilityContext;
context.startAbility({
bundleName: 'com.example.testapp',
abilityName: 'EntryAbility',
moduleName: ''
}).then(() => {
console.error('startApplication promise success');
promptAction.showToast({message: '打开成功', duration: ToastDuration});
}, (err) => {
console.error(`startApplication promise error: ${JSON.stringify(err)}`);
promptAction.showToast({message: '打开失败:应用不存在', duration: ToastDuration});
});
}
卸载应用
指定包名和参数卸载应用
import installer from '@ohos.bundle.installer';
import promptAction from '@ohos.promptAction';
function uninstallApp() {
let bundleName = 'com.example.testapp';
let installParam = {
userId: 100,
isKeepData: false,
installFlag: 1
};
try {
installer.getBundleInstaller().then(data => {
data.uninstall(bundleName, installParam, err => {
if (err) {
console.error('uninstall failed:' + err.message);
promptAction.showToast({message: '卸载失败:应用不存在', duration: ToastDuration});
} else {
console.info('uninstall successfully.');
promptAction.showToast({message: '卸载成功', duration: ToastDuration});
}
});
}).catch(error => {
console.error('getBundleInstaller failed. Cause: ' + error.message);
promptAction.showToast({message: '卸载失败:' + error.message, duration: ToastDuration});
});
} catch (error) {
console.error('getBundleInstaller failed. Cause: ' + error.message);
promptAction.showToast({message: '卸载失败:' + error.message, duration: ToastDuration});
}
}
第二种思路是:开发一个shell命令服务放到系统内,用于操作bm/aa等本地shell命令去安装和打开应用,并提供http或其他协议的接口给上层应用调用。很明显这种方式违背了OpenHarmony的访问控制权限机制,只需要申请一个基本的INTERNET权限用于调用本机接口即可,这种方式很不安全,只是探索研究和尝试。
开发shell服务
这里以go语言为例
myshell.go
package main
import (
"fmt"
"log"
"net/http"
"os/exec"
"strings"
)
func main() {
http.HandleFunc("/execCommand", ExecCommand)
err := http.ListenAndServe("0.0.0.0:23333", nil)
if err != nil {
log.Fatal("ListenAndServe: ", err)
}
}
func ExecCommand(w http.ResponseWriter, r *http.Request) {
name := r.FormValue("name")
args := r.FormValue("args")
argsArr := strings.Split(args, " ")
cmd := exec.Command(name, argsArr...)
out, err := cmd.CombinedOutput()
if err != nil {
_, err := fmt.Fprintf(w, err.Error())
if err != nil {
return
}
return
}
_, err = fmt.Fprintf(w, string(out))
if err != nil {
return
}
}
在 windows 平台上交叉编译生成 linux arm64 平台的的可执行程序
SET CGO_ENABLED=0
SET GOOS=linux
SET GOARCH=arm64
go build -o myshell myshell.go
配置shell服务开机自启
编写一个 myshell.cfg
文件,用于描述开机自启shell服务的信息
{
"import" : [],
"jobs" : [{
"name" : "init",
"cmds" : [
"start myshell"
]
}
],
"services" : [{
"name" : "myshell",
"path" : ["/system/bin/myshell"]
}
]
}
推送 myshell
和 myshell.cfg
到系统目录,然后重启设备,此服务将开机自启
hdc shell mount -o remount,rw /
hdc file send myshell /system/bin/myshell
hdc file send myshell.cfg /system/etc/init/myshell.cfg
hdc shell chmod 777 /system/bin/myshell
hdc shell reboot
项目权限配置
entry/src/main/module.json5
{
"name": "ohos.permission.INTERNET",
},
安装应用
调用刚才开发的shell服务提供的htpp接口,传递两个参数,第一个name是调用本地shell的绝对路径,第二个args是要执行命令的参数
import http from '@ohos.net.http';
function installAppByMyShell() {
let httpRequest = http.createHttp();
httpRequest.request("http://127.0.0.1:23333/execCommand", {
method: http.RequestMethod.POST,
header: {
'Content-Type': 'application/x-www-form-urlencoded'
},
extraData: "name=/bin/bm&args=install -r -p /data/local/tmp/testapp.hap",
}, (err, data) => {
if (!err) {
if (data.responseCode == 200) {
if (data.result.toString().startsWith("install bundle successfully.")) {
promptAction.showToast({message: '安装成功', duration: ToastDuration});
} else {
promptAction.showToast({message: '安装失败', duration: ToastDuration});
}
}
} else {
console.info('error:' + JSON.stringify(err));
httpRequest.destroy();
}
}
);
}
打开/卸载应用
由于这种实现方法的特殊性,你可以通过http接口调用任何本地的shell命令,因此,你可以调用bm/aa等本地shell去实现打开应用、卸载应用,甚至是其他的任何操作~
本文作者:westinyang