Privacy Management
1 Privacy Permission Check
1.1 Interface Prototype
See Host Privacy Policy Access for details
gameHandle.setPrivacyChecker(checker)
1.2 Parameter Description
| parameter | Data Type | Required | Attribute Description |
|---|---|---|---|
checker | Object | Yes | Privacy Permission Checker Object |
2 Host Privacy Policy Access
2.1 Function Introduction
To standardize the behavior of developers when invoking system capabilities, such as camera, Bluetooth, sensors, etc., the host provides an asynchronous call interface for permission checking to third-party access platforms. Third-party platforms can check or guide users to grant relevant permissions through this interface.
2.2 Access Instructions
1. Implement the privacy checker object
Privacy Checker needs to implement the requirePrivacy method, where the possible values of methodName are shown in the next section Related APIs.
const privacyChecker = {
/**
* Get system capability related permissions
* @param {string} methodName - Method name that needs permission check
* @param {Function} callback - Permission check result callback
*/
requirePrivacy: (methodName, callback) => {
// Implement your permission check logic here
// callback should be called with result object
}
};
2. Definition of Callback Result Object
callback function receives a result object, which contains the following fields:
{
passed: Boolean, // Whether permission check passed
errorNo: Number, // Error code (0 for success)
errorMessage: String // Error message
}
Parameter Description:
-
passed: Indicates whether the check has passed -
errorNo: Error code, 0 when passed -
errorMessage: Error message, which will be returned to the js call in the game
3. Set the privacy checker via RuntimeGameHandle
const gameHandle = /* RuntimeGameHandle instance */;
gameHandle.setPrivacyChecker(privacyChecker);
3 Related APIs
The following are the system capabilities that require privacy permission checks:
| Information Type | methodName | Description |
|---|---|---|
| Microphone | startRecorderManager | RecorderManager.start |
| Camera | startCameraManager | tj.createCamera |
| Bluetooth | openBluetoothAdapter, createBLEPeripheralServer | tj.openBluetoothAdapter, tj.createBLEPeripheralServer |
| Gyroscope Sensor | startGyroscope | tj.startGyroscope |
| Direction Sensor | startDeviceMotionListening | tj.startDeviceMotionListening |
| Magnetic Field Sensor | startCompass | tj.startCompass |
| Acceleration Sensor | startAccelerometer | tj.startAccelerometer |
4 Example
4.1 Basic Example
const privacyChecker = {
requirePrivacy: (methodName, callback) => {
console.log(`Permission check requested for: ${methodName}`);
// Example: Always grant permission
callback({
passed: true,
errorNo: 0,
errorMessage: ''
});
}
};
gameHandle.setPrivacyChecker(privacyChecker);
4.2 Example of Random Results
A privacy checker that returns random results, used for testing:
class RandomPrivacyChecker {
requirePrivacy(methodName, callback) {
console.log(`Checking privacy for: ${methodName}`);
// Simulate async permission check
setTimeout(() => {
const result = this.getRandomResult();
console.log(`Privacy check result: ${result.passed ? 'GRANTED' : 'DENIED'}`);
callback(result);
}, 500);
}
getRandomResult() {
const random = Math.random();
if (random < 0.7) {
return {
passed: true,
errorNo: 0,
errorMessage: 'Permission granted'
};
} else {
return {
passed: false,
errorNo: 1,
errorMessage: 'Permission denied by user'
};
}
}
}
// Usage
const checker = new RandomPrivacyChecker();
gameHandle.setPrivacyChecker({
requirePrivacy: (methodName, callback) => {
checker.requirePrivacy(methodName, callback);
}
});
4.3 Permission Check Based on Method Name
class MethodBasedPrivacyChecker {
constructor() {
// Define which permissions are granted by default
this.grantedPermissions = new Set([
'startAccelerometer',
'startGyroscope'
]);
}
requirePrivacy(methodName, callback) {
console.log(`Permission check for: ${methodName}`);
if (this.grantedPermissions.has(methodName)) {
// Permission already granted
callback({
passed: true,
errorNo: 0,
errorMessage: ''
});
} else {
// Need to request permission
this.requestPermission(methodName, callback);
}
}
requestPermission(methodName, callback) {
// Simulate user permission dialog
console.log(`Requesting permission for: ${methodName}`);
setTimeout(() => {
// Simulate user response
const userGranted = Math.random() > 0.3;
if (userGranted) {
this.grantedPermissions.add(methodName);
callback({
passed: true,
errorNo: 0,
errorMessage: ''
});
} else {
callback({
passed: false,
errorNo: 10001,
errorMessage: 'User denied permission'
});
}
}, 1000);
}
}
// Usage
const checker = new MethodBasedPrivacyChecker();
gameHandle.setPrivacyChecker({
requirePrivacy: (methodName, callback) => {
checker.requirePrivacy(methodName, callback);
}
});
4.4 Integration with System Permissions
const { systemPreferences } = require('electron');
class ElectronPrivacyChecker {
async requirePrivacy(methodName, callback) {
try {
const granted = await this.checkSystemPermission(methodName);
if (granted) {
callback({
passed: true,
errorNo: 0,
errorMessage: ''
});
} else {
callback({
passed: false,
errorNo: 10002,
errorMessage: `System permission denied for ${methodName}`
});
}
} catch (error) {
callback({
passed: false,
errorNo: 10003,
errorMessage: error.message
});
}
}
async checkSystemPermission(methodName) {
// Map method names to system permission types
const permissionMap = {
'startRecorderManager': 'microphone',
'startCameraManager': 'camera',
// Add more mappings as needed
};
const permissionType = permissionMap[methodName];
if (!permissionType) {
// No system permission required
return true;
}
// Check if running on macOS (systemPreferences is mainly for macOS)
if (process.platform === 'darwin') {
const status = systemPreferences.getMediaAccessStatus(permissionType);
if (status === 'granted') {
return true;
} else if (status === 'not-determined') {
// Request permission
return await systemPreferences.askForMediaAccess(permissionType);
} else {
return false;
}
}
// For other platforms, assume granted
return true;
}
}
// Usage
const checker = new ElectronPrivacyChecker();
gameHandle.setPrivacyChecker({
requirePrivacy: (methodName, callback) => {
checker.requirePrivacy(methodName, callback);
}
});
5 Complete Example
const { TJHostHandle } = require('webglhost-runtime-pc');
class PrivacyManager {
constructor() {
this.gameHandle = null;
this.permissionCache = new Map();
this.permissionListeners = new Map();
}
async initialize(hostHandle) {
this.gameHandle = await hostHandle.createGameHandle();
this.setupPrivacyChecker();
}
setupPrivacyChecker() {
const checker = {
requirePrivacy: (methodName, callback) => {
this.handlePrivacyCheck(methodName, callback);
}
};
this.gameHandle.setPrivacyChecker(checker);
console.log('Privacy checker configured');
}
handlePrivacyCheck(methodName, callback) {
console.log(`[Privacy] Check requested: ${methodName}`);
// Check cache first
if (this.permissionCache.has(methodName)) {
const cached = this.permissionCache.get(methodName);
console.log(`[Privacy] Using cached result: ${cached.passed}`);
callback(cached);
return;
}
// Perform permission check
this.checkPermission(methodName)
.then(result => {
// Cache the result
this.permissionCache.set(methodName, result);
// Notify listeners
this.notifyListeners(methodName, result);
// Return result
callback(result);
})
.catch(error => {
console.error(`[Privacy] Check failed:`, error);
callback({
passed: false,
errorNo: 10000,
errorMessage: error.message
});
});
}
async checkPermission(methodName) {
// Implement your permission checking logic here
// This could involve:
// - Checking system permissions (Electron)
// - Showing user dialogs
// - Checking user preferences
// - etc.
console.log(`[Privacy] Checking permission for: ${methodName}`);
// Simulate async permission check
await new Promise(resolve => setTimeout(resolve, 300));
// Example: Camera and microphone require explicit permission
const requiresExplicitPermission = [
'startRecorderManager',
'startCameraManager'
];
if (requiresExplicitPermission.includes(methodName)) {
// In a real app, show permission dialog to user
const granted = await this.showPermissionDialog(methodName);
return {
passed: granted,
errorNo: granted ? 0 : 10001,
errorMessage: granted ? '' : 'Permission denied by user'
};
}
// Other permissions are granted by default
return {
passed: true,
errorNo: 0,
errorMessage: ''
};
}
async showPermissionDialog(methodName) {
// Simulate permission dialog
console.log(`[Privacy] Showing permission dialog for: ${methodName}`);
// In a real app, you would use Electron dialog or custom UI
// For this example, we'll simulate user response
await new Promise(resolve => setTimeout(resolve, 500));
return Math.random() > 0.2; // 80% chance of granting
}
onPermissionChange(methodName, listener) {
if (!this.permissionListeners.has(methodName)) {
this.permissionListeners.set(methodName, []);
}
this.permissionListeners.get(methodName).push(listener);
}
notifyListeners(methodName, result) {
const listeners = this.permissionListeners.get(methodName) || [];
for (const listener of listeners) {
try {
listener(result);
} catch (error) {
console.error('[Privacy] Listener error:', error);
}
}
}
revokePermission(methodName) {
console.log(`[Privacy] Revoking permission: ${methodName}`);
this.permissionCache.delete(methodName);
}
clearAllPermissions() {
console.log('[Privacy] Clearing all permissions');
this.permissionCache.clear();
}
getPermissionStatus(methodName) {
return this.permissionCache.get(methodName) || null;
}
}
// Usage
async function main() {
// Initialize SDK
const hostHandle = await new Promise((resolve, reject) => {
TJHostHandle.initialize('sdk-key', 'sdk-secret', (handle, error) => {
if (error) reject(error);
else resolve(handle);
});
});
// Setup privacy manager
const privacyManager = new PrivacyManager();
await privacyManager.initialize(hostHandle);
// Listen for permission changes
privacyManager.onPermissionChange('startCameraManager', (result) => {
console.log('Camera permission changed:', result);
});
// Start game
await privacyManager.gameHandle.start();
privacyManager.gameHandle.play();
// Later: check permission status
setTimeout(() => {
const cameraStatus = privacyManager.getPermissionStatus('startCameraManager');
console.log('Camera permission status:', cameraStatus);
}, 5000);
}
main().catch(console.error);
6 Best Practices
-
Cache permission results: Avoid repeated requests for the same permission
-
Asynchronous Processing : Permission checks may involve user interaction and should be asynchronous
-
Friendly reminder : Explain to the user why the permission is needed when requesting it
-
Handle Rejection: When the user rejects permissions, provide alternative solutions or friendly prompts
-
Minimize Permissions: Only request permissions that the app truly needs
-
Platform Differences: Permission models of different operating systems may vary and need to be adapted
- Error handling : Properly handle various error situations during the permission check process