mirror of
https://github.com/kuhyx/testsAndMisc.git
synced 2026-11-11 22:14:01 +01:00
Two gaps left from the device owner work. The geofence had no way to be provisioned. push_home_location.sh stages the file through `run-as`, which returns "package not debuggable" on the release build device owner requires, and making the build debuggable to fix that would hand anyone with adb the ability to edit this app's state -- the bypass the whole design exists to close. So the app writes its own filesDir instead, from a button. Deliberately not an exported intent: anything that can set home from outside the app can set it somewhere else, and the geofence would then report AWAY forever with enforcement off. The coordinates are never logged either, since that is a home address and logcat is readable by anyone with adb. DISALLOW_CONFIG_VPN is now applied from the app, after the filter was confirmed working rather than before -- locking VPN configuration over a broken setup would pin the broken state in place. The open question was whether it also freezes RethinkDNS's own settings; it does not. Its blocklist selection was changed under the restriction without trouble, which is the answer that matters, because the alternative meant every domain rule had to be final before locking. The status page gained a Geofence row. "no home set - enforcing everywhere" is a large behavioural difference from a working geofence and was previously visible only by inspecting logcat. The widget tests caught a real bug rather than a test-only one: the two new buttons overflowed the column by 4px, and the control that must never be clipped off the bottom of the screen is "Release device owner". The body scrolls now. Verified after a reboot: youtube hidden, all 14 youtube domains at 127.0.0.1, google/gmail/play resolving normally, no_config_vpn still enforced, uninstall still refused. The geofence itself stays untested on hardware until the phone has a GPS fix to record. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014DqEd1uUhyT52KjaNeLanC
100 lines
3.6 KiB
Dart
100 lines
3.6 KiB
Dart
import 'package:flutter/services.dart';
|
|
|
|
/// Provisioning state reported by the native side.
|
|
class DevicePolicyStatus {
|
|
const DevicePolicyStatus({
|
|
required this.packageName,
|
|
required this.isDeviceOwner,
|
|
required this.isAdminActive,
|
|
required this.sdkInt,
|
|
required this.restrictionsApplied,
|
|
});
|
|
|
|
/// Builds a status from the raw platform-channel map.
|
|
factory DevicePolicyStatus.fromMap(Map<Object?, Object?> map) {
|
|
return DevicePolicyStatus(
|
|
packageName: map['packageName']! as String,
|
|
isDeviceOwner: map['isDeviceOwner']! as bool,
|
|
isAdminActive: map['isAdminActive']! as bool,
|
|
sdkInt: map['sdkInt']! as int,
|
|
restrictionsApplied: map['restrictionsApplied']! as bool,
|
|
);
|
|
}
|
|
|
|
final String packageName;
|
|
final bool isDeviceOwner;
|
|
final bool isAdminActive;
|
|
final int sdkInt;
|
|
|
|
/// Whether any user restriction is currently enforced.
|
|
///
|
|
/// Always false in this build: the app is provisioning-capable but inert
|
|
/// until the release path has been verified on a real device.
|
|
final bool restrictionsApplied;
|
|
}
|
|
|
|
/// Dart side of the device-policy platform channel.
|
|
class DevicePolicy {
|
|
const DevicePolicy([this.channel = _defaultChannel]);
|
|
|
|
static const MethodChannel _defaultChannel =
|
|
MethodChannel('com.kuhy.focus_owner/device_policy');
|
|
|
|
final MethodChannel channel;
|
|
|
|
/// Reads the current provisioning state.
|
|
Future<DevicePolicyStatus> status() async {
|
|
final result =
|
|
await channel.invokeMethod<Map<Object?, Object?>>('status');
|
|
return DevicePolicyStatus.fromMap(result!);
|
|
}
|
|
|
|
/// Runs one enforcement pass now and arms the schedule.
|
|
///
|
|
/// The service schedules the following run at the end of each pass, so this
|
|
/// is also how the chain is started on a device that has not rebooted since
|
|
/// the app was installed.
|
|
Future<bool> runEnforcementNow() async =>
|
|
await channel.invokeMethod<bool>('runEnforcementNow') ?? false;
|
|
|
|
/// Cancels any pending scheduled evaluation.
|
|
Future<bool> cancelEnforcement() async =>
|
|
await channel.invokeMethod<bool>('cancelEnforcement') ?? false;
|
|
|
|
/// Relinquishes device ownership without wiping the device.
|
|
///
|
|
/// This is the escape hatch. It is deliberately reachable from the app's
|
|
/// main screen with no PC attached, because the situation it exists for is
|
|
/// one where ADB may already be disallowed.
|
|
Future<bool> releaseDeviceOwner() async {
|
|
final released = await channel.invokeMethod<bool>('releaseDeviceOwner');
|
|
return released ?? false;
|
|
}
|
|
|
|
/// Records the current location as home, enabling the geofence.
|
|
///
|
|
/// Returns null on success, or a message explaining the failure. Lives in
|
|
/// the app rather than a script because `run-as` does not work on the
|
|
/// release build that device owner requires.
|
|
Future<String?> setHomeToCurrentLocation() async =>
|
|
channel.invokeMethod<String>('setHomeToCurrentLocation');
|
|
|
|
/// Whether home coordinates are provisioned.
|
|
///
|
|
/// Without them every pass decides LOCATION_UNKNOWN and fails closed, so
|
|
/// enforcement applies everywhere rather than only at home.
|
|
Future<bool> hasHomeLocation() async =>
|
|
await channel.invokeMethod<bool>('hasHomeLocation') ?? false;
|
|
|
|
/// Prevents the user from turning the always-on VPN off in Settings.
|
|
///
|
|
/// Applied only after the VPN filter is confirmed working: pinning it over
|
|
/// a broken configuration would lock the broken state in.
|
|
Future<bool> setVpnConfigBlocked({required bool blocked}) async =>
|
|
await channel.invokeMethod<bool>(
|
|
'setVpnConfigBlocked',
|
|
{'blocked': blocked},
|
|
) ??
|
|
false;
|
|
}
|