From 7af1d7e834e47d5fcdf04d52c886c26810411ee9 Mon Sep 17 00:00:00 2001 From: lanterngamescn Date: Thu, 25 Jun 2026 19:19:54 +0800 Subject: [PATCH] =?UTF-8?q?M3(=E6=89=B92):=20DeviceProvider=20+=20NetworkP?= =?UTF-8?q?rovider?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit T-M3-02 DeviceProvider:getTime/getphonestate(call.getCallState)/getmarketname/getothername(按名查本地配置)/ getOther/getcompareCode(暂返 '0' 待接版本决策) 同步返回;getbattery→getBattery(batteryInfo.batterySOC/100)、 getwifiLevel→getwifiLevel(wifiManager 信号 0~4)、getphoneInfo→getphoneinfo(deviceInfo;IMEI/IMSI/MAC 鸿蒙受限留空) 出站推送。 裸串 vs JSON、出站命名陷阱(getphoneinfo 小写/getBattery 大写)逐字对齐 T-M3-03 NetworkProvider:getnetwork 同步返回 1无网/2WiFi/3移动(connection.getDefaultNet+bearerTypes); connection 订阅 netAvailable/netLost/netCapabilitiesChange 变化时出站广播 getnetwork;onDestroy 注销 module.json5 增 GET_NETWORK_INFO/GET_WIFI_INFO;组装根装配二者 devecocli build 通过。 Co-Authored-By: Claude Opus 4.8 (1M context) --- entry/src/main/ets/di/AppModule.ets | 5 +- entry/src/main/module.json5 | 6 + feature_capabilities/Index.ets | 2 + .../src/main/ets/providers/DeviceProvider.ets | 118 ++++++++++++++++++ .../main/ets/providers/NetworkProvider.ets | 82 ++++++++++++ 5 files changed, 212 insertions(+), 1 deletion(-) create mode 100644 feature_capabilities/src/main/ets/providers/DeviceProvider.ets create mode 100644 feature_capabilities/src/main/ets/providers/NetworkProvider.ets diff --git a/entry/src/main/ets/di/AppModule.ets b/entry/src/main/ets/di/AppModule.ets index 9971120..81f7c18 100644 --- a/entry/src/main/ets/di/AppModule.ets +++ b/entry/src/main/ets/di/AppModule.ets @@ -6,7 +6,8 @@ * (RoomStubProvider/PayStubProvider,§6.5),契约 handler 名/数量不变。 */ import { CapabilityProvider, EchoSampleProvider, RoomStubProvider, PayStubProvider, - VibrateProvider, ClipboardProvider, AppSystemProvider } from 'feature_capabilities'; + VibrateProvider, ClipboardProvider, AppSystemProvider, DeviceProvider, + NetworkProvider } from 'feature_capabilities'; export function buildCapabilities(): CapabilityProvider[] { return [ @@ -15,6 +16,8 @@ export function buildCapabilities(): CapabilityProvider[] { new AppSystemProvider(), // orientation/browser/finsh/openApplyDownloadpath/notification new VibrateProvider(), // vibrator/repeatvibrator/canclevibrator new ClipboardProvider(), // gameCopytext/gamepastetext + new DeviceProvider(), // getTime/getphonestate/getbattery/getwifiLevel/getphoneInfo/getmarketname/getothername/getOther/getcompareCode + new NetworkProvider(), // getnetwork + 出站广播 // —— 暂缓能力占位桩(§6.5)—— new RoomStubProvider(), new PayStubProvider(), diff --git a/entry/src/main/module.json5 b/entry/src/main/module.json5 index 3ea8b00..dd31218 100644 --- a/entry/src/main/module.json5 +++ b/entry/src/main/module.json5 @@ -58,6 +58,12 @@ }, { "name": "ohos.permission.VIBRATE" + }, + { + "name": "ohos.permission.GET_NETWORK_INFO" + }, + { + "name": "ohos.permission.GET_WIFI_INFO" } ] } diff --git a/feature_capabilities/Index.ets b/feature_capabilities/Index.ets index d5eaa9f..8d7180b 100644 --- a/feature_capabilities/Index.ets +++ b/feature_capabilities/Index.ets @@ -12,3 +12,5 @@ export { RoomStubProvider, PayStubProvider } from './src/main/ets/providers/Stub export { VibrateProvider } from './src/main/ets/providers/VibrateProvider'; export { ClipboardProvider } from './src/main/ets/providers/ClipboardProvider'; export { AppSystemProvider } from './src/main/ets/providers/AppSystemProvider'; +export { DeviceProvider } from './src/main/ets/providers/DeviceProvider'; +export { NetworkProvider } from './src/main/ets/providers/NetworkProvider'; diff --git a/feature_capabilities/src/main/ets/providers/DeviceProvider.ets b/feature_capabilities/src/main/ets/providers/DeviceProvider.ets new file mode 100644 index 0000000..9ddd536 --- /dev/null +++ b/feature_capabilities/src/main/ets/providers/DeviceProvider.ets @@ -0,0 +1,118 @@ +/** + * 设备/系统信息能力(契约 §8.3,T-M3-02)。裸串 vs JSON 严格区分。 + * 同步返回(responseCallback):getTime/getphonestate/getmarketname/getothername/getOther/getcompareCode + * 异步出站推送:getbattery→getBattery、getwifiLevel→getwifiLevel、getphoneInfo→getphoneinfo(全小写) + * + * 注:HarmonyOS 隐私约束下 IMEI/IMSI/MAC 不可获取,PhoneInfoBean 对应字段留空(与平台能力一致)。 + * 需 module.json5 声明 GET_WIFI_INFO(getwifiLevel)。 + */ +import { deviceInfo } from '@kit.BasicServicesKit'; +import { batteryInfo } from '@kit.BasicServicesKit'; +import { call } from '@kit.TelephonyKit'; +import { wifiManager } from '@kit.ConnectivityKit'; +import { BusinessError } from '@kit.BasicServicesKit'; +import { BridgeController } from 'feature_bridge'; +import { InboundHandlers, OutboundHandlers, PhoneInfoBean, WifiLevelResp } from 'contracts'; +import { ConfigManager, AppConfig } from 'domain_resource'; +import { Logger } from 'common'; +import { CapabilityContext, CapabilityProvider } from '../core/CapabilityProvider'; + +export class DeviceProvider implements CapabilityProvider { + readonly name: string = 'device'; + private readonly log: Logger = Logger.tag('DeviceProvider'); + private bridge: BridgeController | undefined = undefined; + private config: ConfigManager | undefined = undefined; + + register(bridge: BridgeController, ctx: CapabilityContext): void { + this.bridge = bridge; + this.config = ctx.config; + + bridge.registerHandler(InboundHandlers.GetTime, (_d: string, cb: (resp: string) => void) => { + cb(Date.now().toString()); + }); + bridge.registerHandler(InboundHandlers.GetPhoneState, (_d: string, cb: (resp: string) => void) => { + call.getCallState().then((s: call.CallState) => cb(`${s}`)).catch(() => cb('0')); + }); + bridge.registerHandler(InboundHandlers.GetMarketName, (_d: string, cb: (resp: string) => void) => { + cb(this.local().market); + }); + bridge.registerHandler(InboundHandlers.GetOther, (_d: string, cb: (resp: string) => void) => { + cb(this.local().other); + }); + bridge.registerHandler(InboundHandlers.GetOtherName, (data: string, cb: (resp: string) => void) => { + cb(this.lookupConfig(data)); + }); + bridge.registerHandler(InboundHandlers.GetCompareCode, (_d: string, cb: (resp: string) => void) => { + // TODO(M3):1=本地 appversion>网络 app_version/0=否,需接 StartupOrchestrator 的版本决策;暂安全返回 '0' + cb('0'); + }); + bridge.registerHandler(InboundHandlers.GetBattery, (_d: string, _cb: (resp: string) => void) => { + this.pushBattery(); + }); + bridge.registerHandler(InboundHandlers.GetWifiLevel, (_d: string, _cb: (resp: string) => void) => { + this.pushWifiLevel(); + }); + bridge.registerHandler(InboundHandlers.GetPhoneInfo, (_d: string, _cb: (resp: string) => void) => { + this.pushPhoneInfo(); + }); + } + + private local(): AppConfig { + return this.config!.getLocal(); + } + + /** getothername:按配置名取本地配置值。 */ + private lookupConfig(name: string): string { + const c: AppConfig = this.local(); + switch (name) { + case 'agent': return c.agent; + case 'channel': return c.channel; + case 'gamedir': return c.gamedir; + case 'gamestart': return c.gamestart; + case 'appversion': return c.appversion; + case 'market': return c.market; + case 'gameid': return c.gameid; + case 'weburl': return c.weburl; + case 'gameconfig': return c.gameconfig; + case 'other': return c.other; + case 'tuiguang': return c.tuiguang; + default: return ''; + } + } + + /** getBattery:电量 float 0~1。 */ + private pushBattery(): void { + const soc: number = batteryInfo.batterySOC; // 0~100 + const level: number = Math.max(0, Math.min(100, soc)) / 100; + this.bridge?.callHandler(OutboundHandlers.GetBattery, `${level}`); + } + + /** getwifiLevel:{ssidname, signalLevel(0~4)}。失败回安全默认(H5 不卡死)。 */ + private pushWifiLevel(): void { + wifiManager.getLinkedInfo() + .then((info: wifiManager.WifiLinkedInfo) => { + const level: number = wifiManager.getSignalLevel(info.rssi, info.band); + const resp: WifiLevelResp = { ssidname: info.ssid ?? '', signalLevel: level }; + this.bridge?.callHandler(OutboundHandlers.GetWifiLevel, JSON.stringify(resp)); + }) + .catch((e: BusinessError) => { + this.log.w(`getwifiLevel failed: ${e.code} ${e.message}`); + const resp: WifiLevelResp = { ssidname: '', signalLevel: 0 }; + this.bridge?.callHandler(OutboundHandlers.GetWifiLevel, JSON.stringify(resp)); + }); + } + + /** getphoneinfo(出站全小写):PhoneInfoBean。IMEI/IMSI/MAC 受限留空。 */ + private pushPhoneInfo(): void { + const bean: PhoneInfoBean = { + PhoneVersion: deviceInfo.osFullName, + PhoneAdresseMAC: '', + PhoneModel: deviceInfo.productModel, + PhoneDeviceBrand: deviceInfo.brand, + PhoneProvidersName: '', + PhoneIMEI: '', + PhoneIMSI: '', + }; + this.bridge?.callHandler(OutboundHandlers.GetPhoneInfo, JSON.stringify(bean)); + } +} diff --git a/feature_capabilities/src/main/ets/providers/NetworkProvider.ets b/feature_capabilities/src/main/ets/providers/NetworkProvider.ets new file mode 100644 index 0000000..4fae914 --- /dev/null +++ b/feature_capabilities/src/main/ets/providers/NetworkProvider.ets @@ -0,0 +1,82 @@ +/** + * 网络状态能力(契约 §8.3/§9,T-M3-03)。@ohos.net.connection。 + * - getnetwork:同步返回 int 裸串(1 无网 / 2 WiFi / 3 移动)。 + * - 出站 getnetwork:网络变化时广播同样的 int 裸串。 + * 需 module.json5 声明 GET_NETWORK_INFO。 + */ +import { connection } from '@kit.NetworkKit'; +import { BusinessError } from '@kit.BasicServicesKit'; +import { BridgeController } from 'feature_bridge'; +import { InboundHandlers, OutboundHandlers } from 'contracts'; +import { Logger } from 'common'; +import { CapabilityContext, CapabilityProvider } from '../core/CapabilityProvider'; + +/** 网络码(契约:1 无网 / 2 WiFi / 3 移动)。 */ +const NET_NONE: string = '1'; +const NET_WIFI: string = '2'; +const NET_MOBILE: string = '3'; + +export class NetworkProvider implements CapabilityProvider { + readonly name: string = 'network'; + private readonly log: Logger = Logger.tag('NetworkProvider'); + private bridge: BridgeController | undefined = undefined; + private conn: connection.NetConnection | undefined = undefined; + + register(bridge: BridgeController, _ctx: CapabilityContext): void { + this.bridge = bridge; + bridge.registerHandler(InboundHandlers.GetNetwork, (_d: string, cb: (resp: string) => void) => { + this.currentNetwork().then((code: string) => cb(code)).catch(() => cb(NET_NONE)); + }); + this.subscribe(); + } + + onDestroy(): void { + const c = this.conn; + if (c !== undefined) { + c.unregister((_e: BusinessError) => { }); + this.conn = undefined; + } + } + + /** 取当前网络码。 */ + private async currentNetwork(): Promise { + const has: boolean = await connection.hasDefaultNet(); + if (!has) { + return NET_NONE; + } + const netHandle: connection.NetHandle = await connection.getDefaultNet(); + const cap: connection.NetCapabilities = await connection.getNetCapabilities(netHandle); + const bearers: number[] = cap.bearerTypes ?? []; + if (bearers.includes(connection.NetBearType.BEARER_WIFI)) { + return NET_WIFI; + } + if (bearers.includes(connection.NetBearType.BEARER_CELLULAR)) { + return NET_MOBILE; + } + return NET_NONE; + } + + /** 订阅网络变化,变化时出站推送 getnetwork。 */ + private subscribe(): void { + try { + const conn: connection.NetConnection = connection.createNetConnection(); + this.conn = conn; + conn.on('netAvailable', () => this.broadcast()); + conn.on('netLost', () => this.bridge?.callHandler(OutboundHandlers.GetNetwork, NET_NONE)); + conn.on('netCapabilitiesChange', () => this.broadcast()); + conn.register((e: BusinessError) => { + if (e) { + this.log.w(`net register failed: ${e.code} ${e.message}`); + } + }); + } catch (e) { + this.log.w(`subscribe failed: ${(e as BusinessError).message}`); + } + } + + private broadcast(): void { + this.currentNetwork() + .then((code: string) => this.bridge?.callHandler(OutboundHandlers.GetNetwork, code)) + .catch(() => { }); + } +}