expo-app-blocker 0.1.76 → 0.1.77
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -131,6 +131,43 @@ class ExpoAppBlockerModule : Module() {
|
|
|
131
131
|
TemporaryUnlockController.remainingSeconds(context)
|
|
132
132
|
}
|
|
133
133
|
|
|
134
|
+
// Last-resort recovery for an unrecoverable native state (observed:
|
|
135
|
+
// expo-sqlite's Android connection degrading after enough reuse in a
|
|
136
|
+
// long-lived process — a fresh `openDatabaseAsync` + first pragma NPEs
|
|
137
|
+
// even after a full DB rebuild). Apps that keep this module's
|
|
138
|
+
// AppBlockerService running as a foreground service can end up with an
|
|
139
|
+
// unusually long-lived process (the OS won't kill it just because the
|
|
140
|
+
// Activity was "closed"), which surfaces exactly this kind of
|
|
141
|
+
// native-module degradation far more than a normal app would. Only a
|
|
142
|
+
// genuine process kill clears it: queue the relaunch intent (optionally
|
|
143
|
+
// straight back into `deepLink`, e.g. the blocker intercept URL the
|
|
144
|
+
// caller was trying to reach) — `startActivity` only needs to queue the
|
|
145
|
+
// intent with ActivityManagerService, which survives our process dying
|
|
146
|
+
// right after — then kill this process outright. No AlarmManager/
|
|
147
|
+
// PendingIntent needed (and SCHEDULE_EXACT_ALARM would require a
|
|
148
|
+
// permission apps otherwise don't need just for this).
|
|
149
|
+
Function("restartApp") { deepLink: String? ->
|
|
150
|
+
val intent = if (!deepLink.isNullOrEmpty()) {
|
|
151
|
+
Intent(Intent.ACTION_VIEW, Uri.parse(deepLink))
|
|
152
|
+
} else {
|
|
153
|
+
context.packageManager.getLaunchIntentForPackage(context.packageName)
|
|
154
|
+
}
|
|
155
|
+
if (intent == null) {
|
|
156
|
+
Log.e(TAG, "restartApp: no launch intent resolvable, cannot self-heal")
|
|
157
|
+
return@Function
|
|
158
|
+
}
|
|
159
|
+
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
|
|
160
|
+
|
|
161
|
+
context.startActivity(intent)
|
|
162
|
+
|
|
163
|
+
Log.w(
|
|
164
|
+
TAG,
|
|
165
|
+
"restartApp: relaunch queued, killing process ${Process.myPid()} to recover " +
|
|
166
|
+
"from an unrecoverable native state"
|
|
167
|
+
)
|
|
168
|
+
Process.killProcess(Process.myPid())
|
|
169
|
+
}
|
|
170
|
+
|
|
134
171
|
AsyncFunction("getInstalledApps") {
|
|
135
172
|
val pm = context.packageManager
|
|
136
173
|
val intent = Intent(Intent.ACTION_MAIN).apply {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "expo-app-blocker",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.77",
|
|
4
4
|
"description": "Expo module for cross-platform app blocking. Android: UsageStatsManager + Overlay. iOS: Screen Time API (FamilyControls + ManagedSettings + DeviceActivity).",
|
|
5
5
|
"main": "src/index.ts",
|
|
6
6
|
"types": "src/index.ts",
|
package/src/index.ts
CHANGED
|
@@ -222,6 +222,35 @@ export function checkAndClearPendingUnlock(): boolean {
|
|
|
222
222
|
return NativeModule.checkAndClearPendingUnlock();
|
|
223
223
|
}
|
|
224
224
|
|
|
225
|
+
/**
|
|
226
|
+
* Android-only, last-resort recovery: forces a genuine process kill and
|
|
227
|
+
* relaunch. Some native-layer failures (observed: an expo-sqlite connection
|
|
228
|
+
* that NPEs on every operation, even a fresh `openDatabaseAsync` against a
|
|
229
|
+
* freshly-rebuilt database file) can only be cleared by a real process
|
|
230
|
+
* restart — closing/reopening the JS-side handle doesn't reach far enough.
|
|
231
|
+
* Apps that run this module's `AppBlockerService` as a foreground service
|
|
232
|
+
* can end up with an unusually long-lived Android process (the OS won't
|
|
233
|
+
* kill it just because the Activity was "closed"), which surfaces this kind
|
|
234
|
+
* of native-module degradation far more than it would in a normal app.
|
|
235
|
+
*
|
|
236
|
+
* Queues a relaunch (optionally straight back into `deepLink`, e.g. the
|
|
237
|
+
* blocker intercept URL the caller was trying to reach), then calls
|
|
238
|
+
* `Process.killProcess`. If that succeeds the process is gone before the
|
|
239
|
+
* call would otherwise return. The native call itself can still reject
|
|
240
|
+
* (e.g. a missing Android permission) — this is already the last-resort
|
|
241
|
+
* path, so that failure is swallowed rather than left as an unhandled
|
|
242
|
+
* rejection.
|
|
243
|
+
*
|
|
244
|
+
* No-op on iOS: that platform's process model doesn't exhibit this failure
|
|
245
|
+
* mode, and there is no equivalent restart primitive.
|
|
246
|
+
*/
|
|
247
|
+
export function restartAppForRecovery(deepLink?: string): void {
|
|
248
|
+
if (Platform.OS !== "android") return;
|
|
249
|
+
NativeModule.restartApp(deepLink ?? null).catch((err: unknown) => {
|
|
250
|
+
console.warn("[expo-app-blocker] restartAppForRecovery failed", err);
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
|
|
225
254
|
/**
|
|
226
255
|
* One OS-level block event: the blocker intercepted a blocked app (iOS
|
|
227
256
|
* shield render / Android foreground block). `interceptedAt` is epoch
|