128 lines
4.5 KiB
JavaScript
128 lines
4.5 KiB
JavaScript
/* eslint-disable @typescript-eslint/no-var-requires */
|
|
/**
|
|
* Expo Config-Plugin — wires the Android VpnService (DNS-Filter) +
|
|
* AccessibilityService (URL filter Layer 2) into AndroidManifest.xml at
|
|
* prebuild time.
|
|
*
|
|
* Was es macht:
|
|
* 1) Sorgt für `xmlns:tools` auf <manifest>.
|
|
* 2) Registriert <service .RebreakVpnService> mit
|
|
* foregroundServiceType="systemExempted" + intent-filter
|
|
* android.net.VpnService + permission BIND_VPN_SERVICE.
|
|
* (`systemExempted` ist seit Android 14 der korrekte Type für
|
|
* VPN-/Filter-Foreground-Services — vorher war `specialUse`+content_filter
|
|
* angedacht aber bringt mehr Probleme als Nutzen.)
|
|
* 3) Registriert <service .RebreakAccessibilityService> mit
|
|
* android:permission=BIND_ACCESSIBILITY_SERVICE + intent-filter
|
|
* android.accessibilityservice.AccessibilityService + meta-data
|
|
* android.accessibilityservice → @xml/accessibility_service_config.
|
|
*
|
|
* Wird aus app.config.ts via `plugins: ['./plugins/with-rebreak-protection-android']`
|
|
* registriert. Idempotent — kann beliebig oft via `expo prebuild` laufen.
|
|
*
|
|
* Native Source: `modules/rebreak-protection/android/src/main/java/expo/modules/rebreakprotection/`
|
|
* - VpnService: expo.modules.rebreakprotection.vpn.RebreakVpnService
|
|
* - AccessibilityService: expo.modules.rebreakprotection.accessibility.RebreakAccessibilityService
|
|
*/
|
|
|
|
const {
|
|
withAndroidManifest,
|
|
AndroidConfig,
|
|
} = require('@expo/config-plugins');
|
|
|
|
const VPN_SERVICE_CLASS =
|
|
'expo.modules.rebreakprotection.vpn.RebreakVpnService';
|
|
const A11Y_SERVICE_CLASS =
|
|
'expo.modules.rebreakprotection.accessibility.RebreakAccessibilityService';
|
|
|
|
// ─── 1) tools-Namespace auf <manifest> ──────────────────────────────────────
|
|
|
|
function ensureToolsNamespace(manifest) {
|
|
if (!manifest.manifest.$) manifest.manifest.$ = {};
|
|
if (!manifest.manifest.$['xmlns:tools']) {
|
|
manifest.manifest.$['xmlns:tools'] = 'http://schemas.android.com/tools';
|
|
}
|
|
}
|
|
|
|
// ─── 2) <service>-Tag für RebreakVpnService ─────────────────────────────────
|
|
|
|
function ensureVpnService(manifest) {
|
|
const application = AndroidConfig.Manifest.getMainApplicationOrThrow(manifest);
|
|
|
|
if (!application.service) application.service = [];
|
|
|
|
const alreadyDeclared = application.service.some(
|
|
(svc) => svc.$ && svc.$['android:name'] === VPN_SERVICE_CLASS,
|
|
);
|
|
if (alreadyDeclared) return;
|
|
|
|
application.service.push({
|
|
$: {
|
|
'android:name': VPN_SERVICE_CLASS,
|
|
'android:permission': 'android.permission.BIND_VPN_SERVICE',
|
|
'android:foregroundServiceType': 'systemExempted',
|
|
'android:exported': 'false',
|
|
},
|
|
'intent-filter': [
|
|
{
|
|
action: [{ $: { 'android:name': 'android.net.VpnService' } }],
|
|
},
|
|
],
|
|
});
|
|
}
|
|
|
|
// ─── 3) <service>-Tag für RebreakAccessibilityService ───────────────────────
|
|
|
|
function ensureAccessibilityService(manifest) {
|
|
const application = AndroidConfig.Manifest.getMainApplicationOrThrow(manifest);
|
|
|
|
if (!application.service) application.service = [];
|
|
|
|
const alreadyDeclared = application.service.some(
|
|
(svc) => svc.$ && svc.$['android:name'] === A11Y_SERVICE_CLASS,
|
|
);
|
|
if (alreadyDeclared) return;
|
|
|
|
application.service.push({
|
|
$: {
|
|
'android:name': A11Y_SERVICE_CLASS,
|
|
'android:permission': 'android.permission.BIND_ACCESSIBILITY_SERVICE',
|
|
'android:label': '@string/accessibility_service_summary',
|
|
'android:exported': 'true',
|
|
},
|
|
'intent-filter': [
|
|
{
|
|
action: [
|
|
{
|
|
$: {
|
|
'android:name':
|
|
'android.accessibilityservice.AccessibilityService',
|
|
},
|
|
},
|
|
],
|
|
},
|
|
],
|
|
'meta-data': [
|
|
{
|
|
$: {
|
|
'android:name': 'android.accessibilityservice',
|
|
'android:resource': '@xml/accessibility_service_config',
|
|
},
|
|
},
|
|
],
|
|
});
|
|
}
|
|
|
|
// ─── Composition ────────────────────────────────────────────────────────────
|
|
|
|
function withRebreakProtectionAndroid(config) {
|
|
return withAndroidManifest(config, (cfg) => {
|
|
ensureToolsNamespace(cfg.modResults);
|
|
ensureVpnService(cfg.modResults);
|
|
ensureAccessibilityService(cfg.modResults);
|
|
return cfg;
|
|
});
|
|
}
|
|
|
|
module.exports = withRebreakProtectionAndroid;
|