#夏日挑战赛#OpenHarmony源码解析之开关机流程分析 原创 精华
深开鸿
发布于 2022-7-29 14:17
浏览
3收藏
本文正在参加星光计划3.0–夏日挑战赛
作者:严明舟
本文以OpenHarmony 3.1 Release - powermgr_power_manager源码为基础进行分析。
1 简介
电源管理服务组件是OpenHarmony电源管理子系统的一个组件,主要负责如下功能:
- 系统关机
- 重启设备
- 检测当前设备的亮灭屏状态
- 查询系统是否支持该类型的锁
- 创建RunningLock锁
- 锁定和持有RunningLock锁
- 释放RunningLock锁
- 查询当前RunningLock锁是持有状态,还是释放状态
- 系统休眠和唤醒以及屏幕开关
本文主要分析开关机功能,包括NAPI接口以及功能实现的整个流程。
图1 电源管理子系统架构图
图2 电源管理服务组件架构图
2 电源管理服务组件代码目录
/base/powermgr/power_manager
├── figures # 架构图
├── frameworks # FrameWork层
│ ├── dialog # Dialog 资源文件
│ ├── include # 头文件
│ ├── napi # NAPI
│ └── native # Native层
├── interfaces # 接口层
│ ├── innerkits # 内部接口
│ └── kits # 外置接口
├── sa_profile # SA 配置文件
├── services # 服务层
│ ├── native # Native 层
│ └── zidl # zidl 接口层
├── test # 测试用例
└── utils # 工具和通用层
3 开关机整体流程
流程描述:
- JavaScript应用调用shutdownDevice执行系统关机功能;
- shutdownDevice调用power.cpp注册的NAPI接口ShutdownDevice,ShutdownDevice接口进一步调用NAPI封装的RebootOrShutdown方法,RebootOrShutdown会根据true或false参数,决定是调用PowerMgrClient的系统关机ShutDownDevice方法还是重启设备RebootDevice方法;
- ShutDownDevice首先会调用Connect方法,Connect会通过GetSystemAbilityManager方法获取SystemAbilityManager实例,然后通过CheckSystemAbility方法检查对应的SA是否存在,并返回remoteObject对象,最后通过iface_cast构造proxy对象,此时创建了PowerMgrProxy实例,最后ShutDownDevice调用PowerMgrProxy的ShutDownDevice方法;
- 电源管理服务代理请求端PowerMgrProxy的ShutDownDevice方法调用SendRequest方法向电源管理服务提供端PowerMgrStub发送系统关机请求;
- 电源管理服务提供端PowerMgrStub接收到系统关机请求后,调用ShutDownDeviceStub方法;
- PowerMgrService继承PowerMgrStub的ShutDownDeviceStub方法,并进一步调用PowerMgrService的ShutDownDevice方法;
- ShutDownDevice首先判断是否持有系统APP的uid权限,以及是否持有"ohos.permission.REBOOT"的权限,随后执行ShutdownService的Shutdown方法;
- Shutdown方法进一步执行ShutdownService的RebootOrShutdown方法,根据true或false决定是执行Reboot或Shutdown;
- 在Shutdown方法中会通过系统调用方法DoReboot执行shutdown命令;
日志
ShutdownDevice
01-01 06:36:32.772 1200 1209 D 02907/PowerMgrJSNAPI: [power.cpp] RebootOrShutdown# RebootOrShutdown: enter, reboot
01-01 06:36:32.772 1200 1209 E 02900/PowerMgrClient: [power_mgr_client.cpp] RebootDevice# ymz PowerMgrClient::RebootDevice called.
01-01 06:36:32.772 1200 1209 E 02900/PowerMgrClient: [power_mgr_client.cpp] Connect# ymz PowerMgrClient::Connect called.
01-01 06:36:32.772 1200 1209 E 02900/PowerMgrClient: [power_mgr_proxy.cpp] RebootDevice# ymz PowerMgrProxy::RebootDevice called!
01-01 06:45:13.576 373 373 D 02901/PowerMgrService: [power_mgr_stub.cpp] RebootDeviceStub# ymz PowerMgrStub::RebootDeviceStub called!
01-01 06:45:13.576 373 373 I 02901/PowerMgrService: [power_mgr_service.cpp] RebootDevice# ymz PowerMgrService::RebootDevice called!
01-01 06:45:13.576 373 373 I 02901/PowerMgrService: [shutdown_service.cpp] Reboot# ymz ShutdownService::Reboot called!
01-01 06:45:13.576 373 373 I 02901/PowerMgrService: [shutdown_service.cpp] RebootOrShutdown# ymz ShutdownService::RebootOrShutdown called!
3.1 电源管理服务注册
- 调用System Ability的MakeAndRegisterAbility接口注册Power Manager Service实例
//OpenHarmony/base/powermgr/power_manager/services/native/src/power_mgr_service.cpp
auto pms = DelayedSpSingleton<PowerMgrService>::GetInstance();
const bool G_REGISTER_RESULT = SystemAbility::MakeAndRegisterAbility(pms.GetRefPtr());
- System Ability调用Power Manager Service的OnStart函数实现电源管理服务组件的启动
//OpenHarmony/base/powermgr/power_manager/services/native/src/power_mgr_service.cpp
void PowerMgrService::OnStart()
{
POWER_HILOGI(MODULE_SERVICE, "OnStart enter.");
//如果Power Manager Service已启动,则返回(单实例)
if (//OpenHarmony/base/powermgr/power_manager/services/native/src/power_mgr_service.cppready_) {
POWER_HILOGE(MODULE_SERVICE, "OnStart is ready, nothing to do.");
return;
}
//初始化操作较复杂,需进一步展开解析
if (!Init()) {
POWER_HILOGE(MODULE_SERVICE, "OnStart call init fail");
return;
}
//将Power Manager Service服务发布到System Ability,此时PowerMgrService成为了电源管理服务提供端
if (!Publish(DelayedSpSingleton<PowerMgrService>::GetInstance())) {
POWER_HILOGE(MODULE_SERVICE, "OnStart register to system ability manager failed.");
return;
}
//标记Power Manager Service服务已启动
ready_ = true;
POWER_HILOGI(MODULE_SERVICE, "OnStart and add system ability success.");
}
- PowerMgrService的初始化
//OpenHarmony/base/powermgr/power_manager/services/native/src/power_mgr_service.cpp
bool PowerMgrService::Init()
{
POWER_HILOGI(MODULE_SERVICE, "Init start");
//创建eventRunner
if (!eventRunner_) {
eventRunner_ = AppExecFwk::EventRunner::Create(POWERMGR_SERVICE_NAME);
if (eventRunner_ == nullptr) {
POWER_HILOGE(MODULE_SERVICE, "Init failed due to create EventRunner");
return false;
}
}
//创建PowermsEventHandler实例
if (!handler_) {
handler_ = std::make_shared<PowermsEventHandler>(eventRunner_, pms);
std::string handlerName("PowerMgrEventHandler");
HiviewDFX::Watchdog::GetInstance().AddThread(handlerName, handler_, WATCH_DOG_DELAY_MS);
}
//创建RunningLockMgr实例
if (!runningLockMgr_) {
runningLockMgr_ = std::make_shared<RunningLockMgr>(pms);
}
//执行RunningLockMgr的初始化,并创建RunningLockAction实例
if (!runningLockMgr_->Init()) {
POWER_HILOGE(MODULE_SERVICE, "OnStart init fail");
return false;
}
//创建PowerStateMachine实例,执行PowerStateMachine的初始化,并创建PowerMgrNotify实例以及执行它的RegisterPublishEvents方法
if (!PowerStateMachineInit()) {
POWER_HILOGE(MODULE_SERVICE, "power state machine init fail!");
}
//创建PowerSaveMode实例
if (DelayedSpSingleton<PowerSaveMode>::GetInstance()) {
powerModeModule_.EnableMode(powerModeModule_.GetModeItem());
} else {
POWER_HILOGE(MODULE_SERVICE, "power mode init fail!");
}
//执行PowermsEventHandler的SendEvent方法
handler_->SendEvent(PowermsEventHandler::INIT_KEY_MONITOR_MSG, 0, INIT_KEY_MONITOR_DELAY_MS);
POWER_HILOGI(MODULE_SERVICE, "Init success");
return true;
}
3.2 开关机NAPI接口定义及实现
- NAPI接口声明
//OpenHarmony/base/powermgr/power_manager/interfaces/kits/js/@ohos.power.d.ts
import {AsyncCallback} from './basic';
declare namespace power {
...
function shutdownDevice(reason: string): void;
...
}
export default power;
- NAPI接口实现,NAPI接口shutdownDevice会进一步调用NAPI封装的RebootOrShutdown方法,RebootOrShutdown会根据true或false参数,决定是调用PowerMgrClient的系统关机ShutDownDevice方法还是重启设备RebootDevice方法
//OpenHarmony/base/powermgr/power_manager/frameworks/napi/power/power.cpp
//模块初始化
static napi_value PowerInit(napi_env env, napi_value exports)
{
POWER_HILOGD(MODULE_JS_NAPI, "%{public}s: enter", __func__);
napi_property_descriptor desc[] = {
DECLARE_NAPI_FUNCTION("shutdownDevice", ShutdownDevice),
...
};
NAPI_CALL(env, napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc));
POWER_HILOGD(MODULE_JS_NAPI, "%{public}s: exit", __func__);
return exports;
}
//开关机接口NAPI实现
static napi_value ShutdownDevice(napi_env env, napi_callback_info info)
{
POWER_HILOGE(MODULE_JS_NAPI, "ymz %{public}s called.", __func__);
return RebootOrShutdown(env, info, false);
}
static napi_value RebootOrShutdown(napi_env env, napi_callback_info info, bool isReboot)
{
POWER_HILOGD(MODULE_JS_NAPI, "ymz %{public}s: enter, %{public}s", __func__, isReboot ? "reboot" : "shutdown");
size_t argc = 1;
napi_value args[1] = { 0 };
napi_value jsthis;
void *data = nullptr;
napi_status status = napi_get_cb_info(env, info, &argc, args, &jsthis, &data);
NAPI_ASSERT(env, (status == napi_ok) && (argc >= 1), "failed to get cb info");
napi_valuetype type = napi_undefined;
NAPI_CALL(env, napi_typeof(env, args[0], &type));
//读取并检查入参
NAPI_ASSERT(env, type == napi_string, "wrong argument type. string expected.");
char reason[REASON_MAX] = { 0 };
size_t reasonLen = 0;
status = napi_get_value_string_utf8(env, args[0], reason, REASON_MAX - 1, &reasonLen);
//读取并检查入参
if (status != napi_ok) {
POWER_HILOGE(MODULE_JS_NAPI, "%{public}s: get reason failed", __func__);
return nullptr;
}
//根据true或false参数,决定是调用PowerMgrClient的系统关机ShutDownDevice方法还是重启设备RebootDevice方法
if (isReboot) {
g_powerMgrClient.RebootDevice(std::string(reason));
} else {
g_powerMgrClient.ShutDownDevice(std::string(reason));
}
POWER_HILOGD(MODULE_JS_NAPI, "%{public}s: reason %{public}s, exit", __func__, reason);
return nullptr;
}
3.3 开关机客户端native实现
- ShutDownDevice首先会调用Connect方法,Connect会通过GetSystemAbilityManager方法获取SystemAbilityManager实例,然后通过CheckSystemAbility方法检查对应的SA是否存在,并返回remoteObject对象,最后通过iface_cast构造proxy对象,此时创建了PowerMgrProxy实例
- 最后ShutDownDevice将开关机请求委托给电源管理服务代理PowerMgrProxy的ShutDownDevice方法去处理
//OpenHarmony/base/powermgr/power_manager/frameworks/native/power_mgr_client.cpp
// 调用方不提供id参数,id默认为0,表示本地显示设备
void PowerMgrClient::ShutDownDevice(const std::string& reason)
{
POWER_HILOGE(MODULE_INNERKIT, "ymz PowerMgrClient::%{public}s called.", __func__);
// 获取电源管理服务的代理端对象,这里返回的是PowerMgrProxy对象
// 查找POWER_MANAGER_SERVICE_ID,即上面注册的PowerMgrService服务
RETURN_IF(Connect() != ERR_OK);
POWER_HILOGE(MODULE_INNERKIT, "%{public}s called.", __func__);
// 调用PowerMgrProxy的ShutDownDevice,通过ipc方式执行开关机指令
proxy_->ShutDownDevice(reason);
}
// 获取电源管理服务的代理端对象
ErrCode PowerMgrClient::Connect()
{
POWER_HILOGE(MODULE_INNERKIT, "ymz PowerMgrClient::%{public}s called.", __func__);
std::lock_guard<std::mutex> lock(mutex_);
if (proxy_ != nullptr) {
return ERR_OK;
}
//获取SystemAbilityManager实例
sptr<ISystemAbilityManager> sam = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager();
if (sam == nullptr) {
POWER_HILOGE(MODULE_INNERKIT, "%{public}s:Failed to get Registry!", __func__);
return E_GET_SYSTEM_ABILITY_MANAGER_FAILED;
}
//调用CheckSystemAbility方法检查对应的SA是否已注册,并返回remoteObject对象
sptr<IRemoteObject> remoteObject_ = sam->CheckSystemAbility(POWER_MANAGER_SERVICE_ID);
if (remoteObject_ == nullptr) {
POWER_HILOGE(MODULE_INNERKIT, "GetSystemAbility failed!");
return E_GET_POWER_SERVICE_FAILED;
}
deathRecipient_ = sptr<IRemoteObject::DeathRecipient>(new PowerMgrDeathRecipient());
if (deathRecipient_ == nullptr) {
POWER_HILOGE(MODULE_INNERKIT, "%{public}s :Failed to create PowerMgrDeathRecipient!", __func__);
return ERR_NO_MEMORY;
}
if ((remoteObject_->IsProxyObject()) && (!remoteObject_->AddDeathRecipient(deathRecipient_))) {
POWER_HILOGE(MODULE_INNERKIT, "%{public}s :Add death recipient to PowerMgr service failed.", __func__);
return E_ADD_DEATH_RECIPIENT_FAILED;
}
//通过iface_cast构造proxy对象,将remoteObject_转换为具体类型,此时创建了PowerMgrProxy实例
proxy_ = iface_cast<IPowerMgr>(remoteObject_);
POWER_HILOGI(MODULE_INNERKIT, "%{public}s :Connecting PowerMgrService success.", __func__);
return ERR_OK;
}
3.4 开关机代理端实现
PowerMgrProxy对象会将开关机的请求以IPC方式发送给电源管理服务提供端PowerMgrStub对象处理
//OpenHarmony/base/powermgr/power_manager/services/zidl/src/power_mgr_proxy.cpp
// 电源管理服务代理端开关机功能实现
void PowerMgrProxy::ShutDownDevice(const std::string& reason)
{
POWER_HILOGE(MODULE_INNERKIT, "ymz PowerMgrProxy::%{public}s called!", __func__);
sptr<IRemoteObject> remote = Remote();
RETURN_IF(remote == nullptr);
MessageParcel data;
MessageParcel reply;
MessageOption option;
if (!data.WriteInterfaceToken(PowerMgrProxy::GetDescriptor())) {
POWER_HILOGE(MODULE_INNERKIT, "PowerMgrProxy::%{public}s write descriptor failed!", __func__);
return;
}
WRITE_PARCEL_NO_RET(data, String16, Str8ToStr16(reason));
// 将请求发送给电源管理服务的stub端,消息类型是SHUTDOWN_DEVICE
int ret = remote->SendRequest(static_cast<int>(IPowerMgr::SHUTDOWN_DEVICE), data, reply, option);
if (ret != ERR_OK) {
POWER_HILOGE(MODULE_INNERKIT, "PowerMgrProxy::%{public}s Transact is failed, error code: %d", __func__, ret);
}
}
3.5 开关机服务提供端实现
- 服务提供端(Stub),继承IRemoteStub,实现业务函数,重写OnRemoteRequest()方法用于接收客户端请求
- 通过消息码匹配对应的业务函数,并调用,这里匹配的是ShutDownDeviceStub
//OpenHarmony/base/powermgr/power_manager/services/zidl/src/power_mgr_stub.cpp
// 处理电源管理代理端发送的请求
int PowerMgrStub::OnRemoteRequest(uint32_t code, MessageParcel &data,
MessageParcel &reply, MessageOption &option)
{
POWER_HILOGD(MODULE_SERVICE,
"PowerMgrStub::OnRemoteRequest, cmd = %{public}u, flags= %{public}d", code, option.GetFlags());
std::u16string descripter = PowerMgrStub::GetDescriptor();
std::u16string remoteDescripter = data.ReadInterfaceToken();
if (descripter != remoteDescripter) {
POWER_HILOGE(MODULE_SERVICE,
"PowerMgrStub::OnRemoteRequest failed, descriptor is not matched!");
return E_GET_POWER_SERVICE_FAILED;
}
const int DFX_DELAY_MS = 10000;
int id = HiviewDFX::XCollie::GetInstance().SetTimer("PowerMgrStub", DFX_DELAY_MS, nullptr, nullptr,
HiviewDFX::XCOLLIE_FLAG_NOOP);
int ret = ERR_OK;
switch (code) {
...
// 消息类型为SHUTDOWN_DEVICE的处理函数
case static_cast<int>(IPowerMgr::SHUTDOWN_DEVICE):
ret = ShutDownDeviceStub(data);
break;
...
default:
ret = IPCObjectStub::OnRemoteRequest(code, data, reply, option);
}
HiviewDFX::XCollie::GetInstance().CancelTimer(id);
return ret;
}
// 电源管理stub端处理函数
int32_t PowerMgrStub::ShutDownDeviceStub(MessageParcel& data)
{
POWER_HILOGD(MODULE_SERVICE, "ymz PowerMgrStub::%{public}s called!", __func__);
std::string reason = Str16ToStr8(data.ReadString16());
ShutDownDevice(reason);
return ERR_OK;
}
- 服务提供端stub将开关机的请求转发给子类对象PowerMgrService的ShutDownDevice方法去处理,在ShutDownDevice方法中会判断相关的权限,以及调用ShutdownService对象的Shutdown方法
//OpenHarmony/base/powermgr/power_manager/services/native/src/power_mgr_service.cpp
// 电源管理管理服务端关机功能实现函数
void PowerMgrService::ShutDownDevice(const std::string& reason)
{
POWER_HILOGI(MODULE_SERVICE, "ymz PowerMgrService::%{public}s called!", __func__);
std::lock_guard lock(mutex_);
pid_t pid = IPCSkeleton::GetCallingPid();
auto uid = IPCSkeleton::GetCallingUid();
//判断执行权限
if (!Permission::CheckIsSystemAppByUid(uid)
&& !Permission::CheckCallingPermission("ohos.permission.REBOOT")) {
POWER_HILOGE(MODULE_SERVICE,
"%{public}s Request failed, %{public}d permission check fail",
__func__, pid);
return;
}
POWER_HILOGI(MODULE_SERVICE, "Cancel auto sleep timer");
powerStateMachine_->CancelDelayTimer(
PowermsEventHandler::CHECK_USER_ACTIVITY_TIMEOUT_MSG);
powerStateMachine_->CancelDelayTimer(
PowermsEventHandler::CHECK_USER_ACTIVITY_OFF_TIMEOUT_MSG);
powerStateMachine_->CancelDelayTimer(
PowermsEventHandler::CHECK_USER_ACTIVITY_SLEEP_TIMEOUT_MSG);
POWER_HILOGI(MODULE_SERVICE, "PID: %{public}d Call %{public}s !", pid, __func__);
//调用ShutdownService对象的Shutdown方法
shutdownService_.Shutdown(reason);
}
3.6 ShutdownService对象的Shutdown方法实现
- Shutdown进一步调用RebootOrShutdown方法,RebootOrShutdown根据true或false决定是执行系统关机Shutdown还是重启设备Reboot
//OpenHarmony/base/powermgr/power_manager/services/native/src/shutdown_service.cpp
void ShutdownService::Shutdown(const std::string& reason)
{
POWER_HILOGI(MODULE_SERVICE, "ymz ShutdownService::%{public}s called!", __func__);
RebootOrShutdown(reason, false);
}
void ShutdownService::RebootOrShutdown(const std::string& reason, bool isReboot)
{
POWER_HILOGI(MODULE_SERVICE, "ymz ShutdownService::%{public}s called!", __func__);
if (started_) {
POWER_HILOGE(MODULE_SERVICE, "Shutdown is already running.");
return;
}
started_ = true;
make_unique<thread>([=] {
Prepare();//发布COMMON_EVENT_SHUTDOWN公共事件
POWER_HILOGD(MODULE_SERVICE, "reason = %{public}s, reboot = %{public}d", reason.c_str(), isReboot);
//执行DevicePowerAction::Shutdown(const std::string& reason)
if (devicePowerAction_ != nullptr) {
isReboot ? devicePowerAction_->Reboot(reason) : devicePowerAction_->Shutdown(reason);
}
started_ = false;
})->detach();
}
3.7 电源管理action实现
执行系统调用,进行shutdown操作
//OpenHarmony/base/powermgr/power_manager/services/native/src/actions/default/device_power_action.cpp
void DevicePowerAction::Shutdown(const std::string& reason)
{
POWER_HILOGI(MODULE_SERVICE, "ymz DevicePowerAction::%{public}s called!", __func__);
POWER_HILOGI(MODULE_SERVICE, "Shutdown executing.");
//执行系统调用,进行shutdown操作
DoReboot(SHUTDOWN_CMD.c_str());
}
4 应用demo
import power from '@ohos.power'
@Entry
@Component
struct Index {
@State window: string = ''
//buttonText数组,包含:关机、重启、检查屏幕状态
private buttonText: Resource[] = [$r('app.string.turnOff'), $r('app.string.restart'), $r('app.string.checkState')]
dialog(title: Resource, message: Resource) {
//警告弹窗
AlertDialog.show(
{
title: title,
message: message,
//按钮的文本内容、文本色、按钮背景色和点击回调。
primaryButton: {
value: $r('app.string.cancel'),//取消
action: () => {
console.info(`Callback when the first button is clicked`)
}
},
//按钮的文本内容、文本色、按钮背景色和点击回调。
secondaryButton: {
value: $r('app.string.determine'),//确认
action: () => {
console.info(`Callback when the second button is clicked`)
if (title === $r('app.string.turnOff')) {
this.window = 'Is power off'
power.shutdownDevice('shutdown_test')//关机
console.info(`power_shutdown_device_test success`)
} else {
this.window = 'Is to restart'
power.rebootDevice('');//重启
console.info(`power_reboot_device_test success`)
}
}
},
cancel: () => {
console.info(`Closed callbacks`)
}
}
)
}
checkState() {
power.isScreenOn((error, screenOn) => {//检查屏幕亮灭
if (typeof error === 'undefined') {
this.window = 'screenOn status is' + screenOn
console.log(this.window);
} else {
console.log(`error: ${error}`);
}
})
}
build() {
//弹性布局容器
Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Start }) {
//沿水平方向布局容器
Row() {
//文本组件
Text($r('app.string.MainAbility_label'))
.fontColor(Color.White)
.fontSize(20)
}
.size({ width: '100%', height: 50 })
.padding({ left: 15 })
.backgroundColor('#0D9FFB')
//文本组件
Text(this.window)
.width('90%')
.height(100)
.border({ width: 2, radius: 10, color: Color.Black })
.margin({ top: 20 })
.padding({ left: 10 })
.fontSize(20)
.fontWeight(FontWeight.Bold)
ForEach(this.buttonText, (item, index) => {
//按钮组件
Button() {
Text(item)
.fontSize(25)
.fontWeight(FontWeight.Bold)
}
.width('80%')
.margin({ top: '20' })
.type(ButtonType.Capsule)
.backgroundColor('#0D9FFB')
.onClick(() => {
index = this.buttonText.indexOf(item)
switch (index) {
case 0:
this.dialog($r('app.string.turnOff'), $r('app.string.questionTurnOff'))//关机
break
case 1:
this.dialog($r('app.string.restart'), $r('app.string.questionRestart'))//重启
break
case 2:
this.checkState()//检查屏幕亮灭
break
default:
break
}
})
}, item => JSON.stringify(item))
}
.width('100%')
.height('100%')
}
}
总结
本文主要和大家分享了OpenHarmony电源管理子系统中开关机功能的实现细节,对NAPI接口、电源管理服务请求端及电源管理服务提供端流程等,做了较为详细的代码说明,希望通过本文您能初步掌握电源管理子系统的关键功能和核心流程。关于OpenHarmony其它子系统的分析,请关注后续文章。
更多原创内容请关注:深开鸿技术团队
入门到精通、技巧到案例,系统化分享OpenHarmony开发技术,欢迎投稿和订阅,让我们一起携手前行共建生态。
©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
标签
已于2022-11-10 15:40:55修改
赞
4
收藏 3
回复
相关推荐
老师图1好像挂掉了,方便补一下吗?
实现关机重启的话好像会有很多权限问题,博主大大能讲一下吗