fix(learn): nothing of Learn's goes to Android's backup or to another phone (ADR 0019, D9)
The device keeps private studysets open in its database, which Android's backup (Google's, or Seedvault where turned on) and a transfer to a new phone took open. allowBackup and fullBackupContent are off, and from Android 12 on the data extraction rules exclude every folder from the transfer too, which allowBackup alone no longer stops. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
parent
f47c2767a3
commit
0ec15d2fb2
5 changed files with 116 additions and 3 deletions
File diff suppressed because one or more lines are too long
|
|
@ -1,4 +1,5 @@
|
|||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
<!-- The app syncs with its server: release builds need this too (Flutter's
|
||||
template grants it to debug and profile builds only). -->
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
|
|
@ -15,10 +16,18 @@
|
|||
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" android:maxSdkVersion="32"/>
|
||||
<uses-permission android:name="android.permission.ACCESS_NOTIFICATION_POLICY"/>
|
||||
<uses-permission android:name="android.permission.VIBRATE"/>
|
||||
<!-- Nothing of Learn's goes to Android's backup or to another phone (ADR 0019,
|
||||
D9): the device keeps private studysets open in its database. allowBackup
|
||||
and fullBackupContent stop both through Android 11; from 12 on the backup
|
||||
alone, and the rules stop the transfer. -->
|
||||
<application
|
||||
android:label="Halkyon Learn"
|
||||
android:name="${applicationName}"
|
||||
android:icon="@mipmap/ic_launcher">
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:allowBackup="false"
|
||||
android:fullBackupContent="false"
|
||||
android:dataExtractionRules="@xml/data_extraction_rules"
|
||||
tools:targetApi="s">
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:exported="true"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,29 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Nothing of Learn's goes to a backup or to another phone, from Android 12
|
||||
on (ADR 0019, D9): allowBackup="false" stops the backup, and only these
|
||||
rules stop the transfer. What Learn keeps open stays on this phone;
|
||||
what is sealed comes back from the instance on the next. -->
|
||||
<data-extraction-rules>
|
||||
<cloud-backup>
|
||||
<exclude domain="root" path="."/>
|
||||
<exclude domain="file" path="."/>
|
||||
<exclude domain="database" path="."/>
|
||||
<exclude domain="sharedpref" path="."/>
|
||||
<exclude domain="external" path="."/>
|
||||
<exclude domain="device_root" path="."/>
|
||||
<exclude domain="device_file" path="."/>
|
||||
<exclude domain="device_database" path="."/>
|
||||
<exclude domain="device_sharedpref" path="."/>
|
||||
</cloud-backup>
|
||||
<device-transfer>
|
||||
<exclude domain="root" path="."/>
|
||||
<exclude domain="file" path="."/>
|
||||
<exclude domain="database" path="."/>
|
||||
<exclude domain="sharedpref" path="."/>
|
||||
<exclude domain="external" path="."/>
|
||||
<exclude domain="device_root" path="."/>
|
||||
<exclude domain="device_file" path="."/>
|
||||
<exclude domain="device_database" path="."/>
|
||||
<exclude domain="device_sharedpref" path="."/>
|
||||
</device-transfer>
|
||||
</data-extraction-rules>
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
import 'dart:io';
|
||||
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
/// Nothing of Learn's leaves the phone by Android's backup or a transfer to
|
||||
/// another phone (ADR 0019, D9): what the device keeps of a private
|
||||
/// studyset is open in its database, and would go there open; what is
|
||||
/// sealed comes back from the instance anyway.
|
||||
void main() {
|
||||
final manifest = File(
|
||||
'android/app/src/main/AndroidManifest.xml',
|
||||
).readAsStringSync();
|
||||
final application = RegExp(
|
||||
'<application[^>]*>',
|
||||
).firstMatch(manifest)!.group(0)!;
|
||||
|
||||
test('backup is off, through Android 11 as from 12 on', () {
|
||||
expect(application, contains('android:allowBackup="false"'));
|
||||
expect(application, contains('android:fullBackupContent="false"'));
|
||||
expect(
|
||||
application,
|
||||
contains('android:dataExtractionRules="@xml/data_extraction_rules"'),
|
||||
);
|
||||
});
|
||||
|
||||
test('from Android 12 on, neither a backup nor a transfer takes any of the '
|
||||
"app's folders", () {
|
||||
// allowBackup="false" leaves a transfer to another phone on from
|
||||
// Android 12 on: the rules have to exclude it too, folder by folder.
|
||||
final rules = File(
|
||||
'android/app/src/main/res/xml/data_extraction_rules.xml',
|
||||
).readAsStringSync();
|
||||
for (final kind in ['cloud-backup', 'device-transfer']) {
|
||||
final section = RegExp(
|
||||
'<$kind>(.*?)</$kind>',
|
||||
dotAll: true,
|
||||
).firstMatch(rules)?.group(1);
|
||||
expect(section, isNotNull, reason: '$kind is missing');
|
||||
final excluded = {
|
||||
for (final match in RegExp(
|
||||
r'<exclude domain="(\w+)" path="\."\s*/>',
|
||||
).allMatches(section!))
|
||||
match.group(1),
|
||||
};
|
||||
expect(excluded, {
|
||||
'root',
|
||||
'file',
|
||||
'database',
|
||||
'sharedpref',
|
||||
'external',
|
||||
'device_root',
|
||||
'device_file',
|
||||
'device_database',
|
||||
'device_sharedpref',
|
||||
}, reason: '$kind excludes');
|
||||
expect(section, isNot(contains('<include')), reason: kind);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
@ -404,3 +404,19 @@ Residual risks, as they stand:
|
|||
- **Tag links aren't signed:** the instance can link a private tag to an
|
||||
item of a studyset being published; the Publish sheet shows every name
|
||||
before it goes.
|
||||
|
||||
## Android's backup (30 Oct 2026, the developer's)
|
||||
|
||||
- **D9 Nothing of Learn's in a backup or a transfer to another phone**, as Halkyon
|
||||
Train's ADR 0030, D53, has it: the device keeps a private studyset open in its
|
||||
local database (D1 seals it on the way to the instance, not on the device), so
|
||||
Android's backup (Google's, or Seedvault on GrapheneOS and other AOSP systems,
|
||||
where the person turned it on) and a transfer to a new phone took it open, beyond
|
||||
what sealing guards. `allowBackup` and `fullBackupContent` are off, which stops
|
||||
both through Android 11; from Android 12 on `allowBackup="false"` stops the backup
|
||||
alone, so `res/xml/data_extraction_rules.xml` excludes every folder from the
|
||||
transfer too (`test/app/android_backup_test.dart`, run with the app's tests). What
|
||||
a new phone lacks comes back from the instance as the person signs in; what is the
|
||||
device's alone starts afresh there: its settings, a timer running, the Japanese
|
||||
dictionaries, the Jellyfin sign-in and `privacy.json`, which a new device never had
|
||||
either (the residual risks above).
|
||||
|
|
|
|||
Loading…
Reference in a new issue