HarmonyOS鸿蒙Next中同意隐私前获取设备和位置信息
HarmonyOS鸿蒙Next中同意隐私前获取设备和位置信息 提审有个新的问题,有一个未同意隐私前获取设备和位置信息,审核意见: 请在用户同意隐私政策后,再申请获取用户个人信息及权限。 您可参考《审核指南》第7.5项:https://developer.huawei.com/consumer/cn/doc/app/50104-07 APP常见个人信息保护问题FAQ您可参考: https://developer.huawei.com/consumer/cn/doc/app/FAQ-fag-01#h1-1683539557872-1
更多关于HarmonyOS鸿蒙Next中同意隐私前获取设备和位置信息的实战教程也可以访问 https://www.itying.com/category-93-b0.html
【背景知识】 获取位置信息需要得到相关权限,可以先学习应用权限管控概述。具体实现主要使用Location Kit提供的能力,请阅读Location Kit简介。
【解决方案】
-
打开设备定位开关。需要用户手动打开位置开关,开发者可以通过isLocationEnabled接口判断位置服务是否开启,然后引导用户手动开启。
-
声明并获取相关位置权限。主要涉及三种权限:
- ohos.permission.LOCATION用于获取精准位置。
- ohos.permission.APPROXIMATELY_LOCATION用于获取模糊位置。
- ohos.permission.LOCATION_IN_BACKGROUND用于后台定位的场景。
开发者根据需要在moudle.json5文件中配置进行声明,并获取用户明确授权方可使用,获取授权有两种方式:(1)通过requestPermissionsFromUser接口向用户弹窗以获得授权。(2)跳转到应用权限设置页面,打开开关进行授权,参考如下示例代码。
Button('跳转应用权限设置页面')
.onClick(() =>{
let context = getContext(this) as common.UIAbilityContext;
context.startAbility({
bundleName: 'com.huawei.hmos.settings',
abilityName: 'com.huawei.hmos.settings.MainAbility',
uri:'application_info_entry',
parameters: {
pushParams: "zouzou.Location_Kit.Location_Kit1" // 当前包名
}
})
})
-
获取当前位置有如下2种,参考获取设备的位置信息开发指导: (1)getLastLocation获取系统缓存的最新位置,可减少系统功耗,推荐使用。 (2)getCurrentLocation获取当前位置。
-
持续获取最新的位置,前台和后台需要做不同的调整。 (1)前台持续获取位置信息。直接调用geoLocationManager.on(‘locationChange’)周期性持续监听最新的位置。参考获取设备的位置信息开发指导。 (2)后台持续获取位置信息。需搭配长时任务使用,选择申请长时任务类型为LOCATION。参考长时任务开发指南。示例代码如下:
import { backgroundTaskManager } from '@kit.BackgroundTasksKit';
import { abilityAccessCtrl,common, Permissions,} from '@kit.AbilityKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { wantAgent, WantAgent } from '@kit.AbilityKit';
import { geoLocationManager } from '@kit.LocationKit';
function callback(info: backgroundTaskManager.ContinuousTaskCancelInfo) {
// 长时任务id
console.info('OnContinuousTaskCancel callback id ' + info.id);
// 长时任务取消原因
console.info('OnContinuousTaskCancel callback reason ' + info.reason);
}
const permissions: Array<Permissions> = ['ohos.permission.LOCATION','ohos.permission.APPROXIMATELY_LOCATION'];
// 使用UIExtensionAbility:将common.UIAbilityContext 替换为common.UIExtensionContext
function reqPermissionsFromUser(permissions: Array<Permissions>, context: common.UIAbilityContext): void {
let atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager();
// requestPermissionsFromUser会判断权限的授权状态来决定是否唤起弹窗
atManager.requestPermissionsFromUser(context, permissions).then((data) => {
let grantStatus: Array<number> = data.authResults;
let length: number = grantStatus.length;
for (let i = 0; i < length; i++) {
if (grantStatus[i] === 0) {
// 用户授权,可以继续访问目标操作
} else {
// 用户拒绝授权,提示用户必须授权才能访问当前页面的功能,并引导用户到系统设置中打开相应的权限
return;
}
}
// 授权成功
}).catch((err: BusinessError) => {
console.error(`Failed to request permissions from user. Code is ${err.code}, message is ${err.message}`);
})
}
@Entry
@Component
struct Index {
@State message: string = 'ContinuousTask';
// 通过getUIContext().getHostContext()方法,来获取page所在的UIAbility上下文
private context: Context | undefined = this.getUIContext().getHostContext();
aboutToAppear() {
// 使用UIExtensionAbility:将common.UIAbilityContext 替换为common.UIExtensionContext
const context: common.UIAbilityContext = this.getUIContext().getHostContext() as common.UIAbilityContext;
reqPermissionsFromUser(permissions, context);
}
OnContinuousTaskCancel() {
try {
backgroundTaskManager.on("continuousTaskCancel", callback);
console.info(`Succeeded in operationing OnContinuousTaskCancel.`);
} catch (error) {
console.error(`Operation OnContinuousTaskCancel failed. code is ${(error as BusinessError).code} message is ${(error as BusinessError).message}`);
}
}
OffContinuousTaskCancel() {
try {
// callback参数不传,则取消所有已注册的回调
backgroundTaskManager.off("continuousTaskCancel", callback);
console.info(`Succeeded in operationing OffContinuousTaskCancel.`);
} catch (error) {
console.error(`Operation OffContinuousTaskCancel failed. code is ${(error as BusinessError).code} message is ${(error as BusinessError).message}`);
}
}
startContinuousTask() {
let wantAgentInfo: wantAgent.WantAgentInfo = {
// 点击通知后,将要执行的动作列表
// 添加需要被拉起应用的bundleName和abilityName
wants: [
{
bundleName: "com.example.swiper",
abilityName: "EntryAbility"
}
],
// 指定点击通知栏消息后的动作是拉起ability
actionType: wantAgent.OperationType.START_ABILITY,
// 使用者自定义的一个私有值
requestCode: 0,
// 点击通知后,动作执行属性
actionFlags: [wantAgent.WantAgentFlags.UPDATE_PRESENT_FLAG],
};
try {
// 通过wantAgent模块下getWantAgent方法获取WantAgent对象
wantAgent.getWantAgent(wantAgentInfo).then((wantAgentObj: WantAgent) => {
try {
let list: Array<string> = ["location"];
// let list: Array<string> = ["bluetoothInteraction"]; 长时任务类型包含bluetoothInteraction,CAR_KEY子类型合法
backgroundTaskManager.startBackgroundRunning(this.context, list, wantAgentObj).then((res: backgroundTaskManager.ContinuousTaskNotification) => {
console.info("Operation startBackgroundRunning succeeded");
// 此处执行具体的长时任务逻辑,如录音,录制等。
let request: geoLocationManager.ContinuousLocationRequest= {
'interval': 1,
'locationScenario': geoLocationManager.UserActivityScenario.NAVIGATION
}
let locationCallback = (location:geoLocationManager.Location):void => {
console.info('locationCallback: data: ' + JSON.stringify(location));
};
try {
geoLocationManager.on('locationChange', request, locationCallback);
} catch (err) {
console.error("errCode:" + JSON.stringify(err));
}
}).catch((error: BusinessError) => {
console.error(`Failed to Operation startBackgroundRunning. code is ${error.code} message is ${error.message}`);
});
} catch (error) {
console.error(`Failed to Operation startBackgroundRunning. code is ${(error as BusinessError).code} message is ${(error as BusinessError).message}`);
}
});
} catch (error) {
console.error(`Failed to Operation getWantAgent. code is ${(error as BusinessError).code} message is ${(error as BusinessError).message}`);
}
}
stopContinuousTask() {
backgroundTaskManager.stopBackgroundRunning(this.context).then(() => {
console.info(`Succeeded in operationing stopBackgroundRunning.`);
}).catch((err: BusinessError) => {
console.error(`Failed to operation stopBackgroundRunning. Code is ${err.code}, message is ${err.message}`);
});
}
build() {
Row() {
Column() {
Text("Index")
.fontSize(50)
.fontWeight(FontWeight.Bold)
Button() {
Text('申请长时任务').fontSize(25).fontWeight(FontWeight.Bold)
}
.type(ButtonType.Capsule)
.margin({ top: 10 })
.backgroundColor('#0D9FFB')
.width(250)
.height(40)
.onClick(() => {
// 通过按钮申请长时任务
this.startContinuousTask();
})
Button() {
Text('取消长时任务').fontSize(25).fontWeight(FontWeight.Bold)
}
.type(ButtonType.Capsule)
.margin({ top: 10 })
.backgroundColor('#0D9FFB')
.width(250)
.height(40)
.onClick(() => {
// 此处结束具体的长时任务的执行
// 通过按钮取消长时任务
this.stopContinuousTask();
})
Button() {
Text('注册长时任务取消回调').fontSize(25).fontWeight(FontWeight.Bold)
}
.type(ButtonType.Capsule)
.margin({ top: 10 })
.backgroundColor('#0D9FFB')
.width(250)
.height(40)
.onClick(() => {
// 通过按钮注册长时任务取消回调
this.OnContinuousTaskCancel();
})
Button() {
Text('取消注册长时任务取消回调').fontSize(25).fontWeight(FontWeight.Bold)
}
.type(ButtonType.Capsule)
.margin({ top: 10 })
.backgroundColor('#0D9FFB')
.width(250)
.height(40)
.onClick(() => {
// 通过按钮取消注册长时任务取消回调
this.OffContinuousTaskCancel();
})
}
.width('100%')
}
.height('100%')
}
}
更多关于HarmonyOS鸿蒙Next中同意隐私前获取设备和位置信息的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html
【背景知识】
[@ohos.abilityAccessCtrl(程序访问控制管理)](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-abilityaccessctrl):程序访问控制提供应用程序的权限校验和管理能力。
【参考方案】:
可参考考勤打卡位置获取示例,基于位置服务实现地理位置获取的功能,以及用户权限申请的交互效果。
- 调用requestPermissionsFromUser接口第一次拉起弹框请求用户授权;若用户拒绝授权,调用requestPermissionOnSetting接口,二次拉起权限设置弹框请求用户授权;若用户再次拒绝授权,则拉起跳转到设置页面的权限申请弹窗。
// 申请用户授权
async requestPermissions(context: UIContext, permissions: Array<Permissions>): Promise<boolean | undefined> {
let atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager();
try {
let data = await atManager.requestPermissionsFromUser(context.getHostContext() as common.UIAbilityContext, permissions)
return data.dialogShownResults ? data.dialogShownResults[0] : undefined; // 返回请求是否有弹窗
} catch (e) {
return undefined;
}
}
// 2次申请用户授权
requestPermissionsOnSetting(context: UIContext, permissions: Array<Permissions>) {
let keyPerms = JSON.stringify(permissions)
let store = preferences.getPreferencesSync(context.getHostContext(), { name: 'permsHasOnSetting' });
if (!store.hasSync(keyPerms)) {
let atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager();
atManager.requestPermissionOnSetting(context.getHostContext(), permissions)
}
}
在HarmonyOS Next中,系统在用户同意隐私协议前不会获取设备或位置信息。所有涉及硬件和位置的数据访问必须经过用户明确授权,遵循隐私合规设计。系统通过权限管理机制确保在未获得同意时不采集任何敏感信息。
根据HarmonyOS Next的隐私合规要求,应用必须在用户明确同意隐私政策后,才能申请获取设备信息(如设备标识符)和位置权限。审核未通过是因为检测到应用在隐私协议弹窗展示前就调用了相关API。
建议检查代码中权限申请(如ohos.permission.LOCATION
)和设备信息获取(如getDeviceId
)的调用时机,确保这些操作仅在用户点击“同意”后执行。可参考华为提供的隐私合规开发指南,调整权限请求逻辑,确保符合规范。