Skip to main content

Privacy Management

1 Privacy Permission Check​

1.1 Interface Prototype​

See Host Privacy Policy Access for details

gameHandle.setPrivacyChecker(checker)

1.2 Parameter Description​

parameterData TypeRequiredAttribute Description
checkerObjectYesPrivacy 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);

The following are the system capabilities that require privacy permission checks:

Information TypemethodNameDescription
MicrophonestartRecorderManagerRecorderManager.start
CamerastartCameraManagertj.createCamera
BluetoothopenBluetoothAdapter, createBLEPeripheralServertj.openBluetoothAdapter, tj.createBLEPeripheralServer
Gyroscope SensorstartGyroscopetj.startGyroscope
Direction SensorstartDeviceMotionListeningtj.startDeviceMotionListening
Magnetic Field SensorstartCompasstj.startCompass
Acceleration SensorstartAccelerometertj.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​

  1. Cache permission results: Avoid repeated requests for the same permission

  2. Asynchronous Processing : Permission checks may involve user interaction and should be asynchronous

  1. Friendly reminder : Explain to the user why the permission is needed when requesting it

  2. Handle Rejection: When the user rejects permissions, provide alternative solutions or friendly prompts

  1. Minimize Permissions: Only request permissions that the app truly needs

  2. Platform Differences: Permission models of different operating systems may vary and need to be adapted

  1. Error handling : Properly handle various error situations during the permission check process