mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-10-06 01:18:11 +00:00
android: prevent access to chat list when accepting call in locked state (#7627)
* android: prevent access to chat list when accepting call in locked state * android: request auth on resume during a call --------- Co-authored-by: Evgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com> Co-authored-by: shum <github.shum@liber.li>
This commit is contained in:
co-authored by
Evgeny @ SimpleX Chat
shum
parent
be05a46628
commit
a9322ff32c
+5
-1
@@ -36,6 +36,7 @@ import chat.simplex.app.R
|
||||
import chat.simplex.app.TAG
|
||||
import chat.simplex.app.model.NtfManager
|
||||
import chat.simplex.app.model.NtfManager.AcceptCallAction
|
||||
import chat.simplex.common.AppLock
|
||||
import chat.simplex.common.helpers.applyAppLocale
|
||||
import chat.simplex.common.model.*
|
||||
import chat.simplex.common.model.ChatController.appPrefs
|
||||
@@ -342,7 +343,10 @@ fun IncomingCallLockScreenAlert(invitation: RcvCallInvitation, chatModel: ChatMo
|
||||
chatModel.activeCallInvitation.value = null
|
||||
ntfManager.cancelCallNotification()
|
||||
},
|
||||
acceptCall = { cm.acceptIncomingCall(invitation = invitation) },
|
||||
acceptCall = {
|
||||
AppLock.recheckAuthState()
|
||||
cm.acceptIncomingCall(invitation = invitation)
|
||||
},
|
||||
openApp = {
|
||||
val intent = Intent(context, MainActivity::class.java)
|
||||
.setAction(NtfManager.OpenChatAction)
|
||||
|
||||
@@ -207,7 +207,7 @@ fun MainScreen() {
|
||||
SwitchingUsersView()
|
||||
}
|
||||
|
||||
if (unauthorized.value && !(chatModel.activeCallViewIsVisible.value && chatModel.showCallView.value)) {
|
||||
if (unauthorized.value) {
|
||||
LaunchedEffect(Unit) {
|
||||
// With these constrains when user presses back button while on ChatList, activity destroys and shows auth request
|
||||
// while the screen moves to a launcher. Detect it and prevent showing the auth
|
||||
@@ -221,7 +221,8 @@ fun MainScreen() {
|
||||
SplashView(true)
|
||||
ModalManager.fullscreen.showPasscodeInView()
|
||||
}
|
||||
} else {
|
||||
}
|
||||
if (!unauthorized.value || chatModel.activeCallViewIsVisible.value) {
|
||||
if (chatModel.showCallView.value) {
|
||||
if (appPlatform.isAndroid) {
|
||||
LaunchedEffect(Unit) {
|
||||
@@ -235,6 +236,8 @@ fun MainScreen() {
|
||||
ActiveCallView()
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!unauthorized.value) {
|
||||
ModalManager.fullscreen.showOneTimePasscodeInView()
|
||||
AlertManager.privacySensitive.showInView()
|
||||
if (onboarding == OnboardingStage.OnboardingComplete) {
|
||||
|
||||
@@ -269,7 +269,11 @@ object AppLock {
|
||||
fun elapsedRealtime(): Long = System.nanoTime() / 1_000_000
|
||||
|
||||
fun recheckAuthState() {
|
||||
if (ChatModel.showCallView.value) return
|
||||
if (ChatModel.showCallView.value) {
|
||||
// BiometricPrompt drops a prompt requested while MainActivity is stopped (call on lock screen), so request it again
|
||||
if (userAuthorized.value == false) runAuthenticate()
|
||||
return
|
||||
}
|
||||
val enteredBackgroundVal = enteredBackground.value
|
||||
val delay = ChatController.appPrefs.laLockDelay.get()
|
||||
if (enteredBackgroundVal == null || elapsedRealtime() - enteredBackgroundVal >= delay * 1000) {
|
||||
|
||||
+2
@@ -1,5 +1,6 @@
|
||||
package chat.simplex.common.platform
|
||||
|
||||
import chat.simplex.common.AppLock
|
||||
import chat.simplex.common.model.*
|
||||
import chat.simplex.common.views.call.RcvCallInvitation
|
||||
import chat.simplex.common.views.chatlist.acceptContactRequest
|
||||
@@ -96,6 +97,7 @@ abstract class NtfManager {
|
||||
}
|
||||
|
||||
fun acceptCallAction(chatId: ChatId) {
|
||||
AppLock.recheckAuthState()
|
||||
chatModel.clearOverlays.value = true
|
||||
val invitation = chatModel.callInvitations[chatId]
|
||||
if (invitation == null) {
|
||||
|
||||
@@ -128,7 +128,7 @@ Common Module (commonMain)
|
||||
| Chat Model | [`ChatModel.kt`](../common/src/commonMain/kotlin/chat/simplex/common/model/ChatModel.kt#L86) | `object ChatModel` | 86 |
|
||||
| App Preferences | [`SimpleXAPI.kt`](../common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt#L102) | `class AppPreferences` | 102 |
|
||||
| Platform Interface | [`Platform.kt`](../common/src/commonMain/kotlin/chat/simplex/common/platform/Platform.kt#L15) | `interface PlatformInterface` | 15 |
|
||||
| Notification Manager | [`NtfManager.kt`](../common/src/commonMain/kotlin/chat/simplex/common/platform/NtfManager.kt#L19) | `abstract class NtfManager` | 19 |
|
||||
| Notification Manager | [`NtfManager.kt`](../common/src/commonMain/kotlin/chat/simplex/common/platform/NtfManager.kt#L20) | `abstract class NtfManager` | 20 |
|
||||
| Theme Manager | [`ThemeManager.kt`](../common/src/commonMain/kotlin/chat/simplex/common/ui/theme/ThemeManager.kt#L18) | `object ThemeManager` | 18 |
|
||||
| Android Haskell Init | [`AppCommon.android.kt`](../common/src/androidMain/kotlin/chat/simplex/common/platform/AppCommon.android.kt#L33) | `fun initHaskell(packageName: String)` | 33 |
|
||||
| Common Migrations | [`AppCommon.kt`](../common/src/commonMain/kotlin/chat/simplex/common/platform/AppCommon.kt#L41) | `fun runMigrations()` | 41 |
|
||||
|
||||
@@ -115,8 +115,8 @@ When onboarding is complete:
|
||||
| `SwitchingUsersView` | User switch in progress | Loading overlay |
|
||||
| Auth gate | `userAuthorized != true` | `AuthView` or `SplashView` + passcode |
|
||||
| Active call | `showCallView == true` | `ActiveCallView` (desktop) or call activity (Android) |
|
||||
| One-time passcode | Always | `ModalManager.fullscreen.showOneTimePasscodeInView` |
|
||||
| Privacy alerts | Always | `AlertManager.privacySensitive` |
|
||||
| One-time passcode | `userAuthorized == true` | `ModalManager.fullscreen.showOneTimePasscodeInView` |
|
||||
| Privacy alerts | `userAuthorized == true` | `AlertManager.privacySensitive` |
|
||||
| Incoming call | `activeCallInvitation != null` | `IncomingCallAlertView` |
|
||||
| Shared alerts | Always | `AlertManager.shared` |
|
||||
|
||||
@@ -294,7 +294,7 @@ object AppLock {
|
||||
### Authentication Flow
|
||||
|
||||
1. **MainScreen** checks `unauthorized` (derived: `userAuthorized.value != true`) at line ~135.
|
||||
2. If unauthorized and not in an active call:
|
||||
2. If unauthorized, including during an active call:
|
||||
- Launches `AppLock.runAuthenticate()` which triggers platform-specific biometric/passcode prompt.
|
||||
- On Android with system auth finishing during activity destruction, authentication is skipped.
|
||||
3. If `performLA` preference is set and `laFailed` is true: shows `AuthView` with "Unlock" button.
|
||||
@@ -302,7 +302,7 @@ object AppLock {
|
||||
|
||||
### Lock Delay
|
||||
|
||||
The `laLockDelay` preference controls how long after backgrounding the app requires re-authentication. When `laLockDelay == 0`, screen rotation triggers a 3-second grace period (line ~270) to prevent unnecessary re-auth. A call counts as activity: `recheckAuthState()` is a no-op while `showCallView` is true, and `CallManager.endCall()` resets `enteredBackground`, so the delay is counted from the end of the call.
|
||||
The `laLockDelay` preference controls how long after backgrounding the app requires re-authentication. When `laLockDelay == 0`, screen rotation triggers a 3-second grace period (line ~270) to prevent unnecessary re-auth. A call counts as activity: `recheckAuthState()` is a no-op while `showCallView` is true, and `CallManager.endCall()` resets `enteredBackground`, so the delay is counted from the end of the call. When a call is accepted from a notification or from the Android lock screen, `recheckAuthState()` is called before `acceptIncomingCall()`, so an expired delay still locks the app.
|
||||
|
||||
### Lock Modes
|
||||
|
||||
|
||||
@@ -59,18 +59,18 @@ State transitions are driven by `WCallResponse` messages from the WebRTC layer.
|
||||
|
||||
## 3. Android Implementation
|
||||
|
||||
### 3.1 CallActivity.kt (464 lines)
|
||||
### 3.1 CallActivity.kt (468 lines)
|
||||
|
||||
[`CallActivity.kt`](../../android/src/main/java/chat/simplex/app/views/call/CallActivity.kt)
|
||||
|
||||
A dedicated `ComponentActivity` that hosts the call UI. Key responsibilities:
|
||||
|
||||
- **Intent handling** ([line 64](../../android/src/main/java/chat/simplex/app/views/call/CallActivity.kt#L64)): On `AcceptCallAction` intent, looks up the matching `RcvCallInvitation` and calls `callManager.acceptIncomingCall()`.
|
||||
- **Lock screen support** ([line 160](../../android/src/main/java/chat/simplex/app/views/call/CallActivity.kt#L160)): `unlockForIncomingCall()` uses `setShowWhenLocked(true)` / `setTurnScreenOn(true)` on API 27+, falls back to window flags on older versions. `lockAfterIncomingCall()` reverses these settings.
|
||||
- **Picture-in-Picture** ([line 99](../../android/src/main/java/chat/simplex/app/views/call/CallActivity.kt#L99)): `setPipParams()` configures PiP aspect ratio and source rect hint. On Android 12+ (`Build.VERSION_CODES.S`), auto-enter PiP is enabled for video calls. `onPictureInPictureModeChanged` toggles `activeCallViewIsCollapsed` and sends a `WCallCommand.Layout` command.
|
||||
- **Permission checks** ([line 122](../../android/src/main/java/chat/simplex/app/views/call/CallActivity.kt#L122)): Checks `RECORD_AUDIO` and conditionally `CAMERA` permissions.
|
||||
- **Service binding** ([line 181](../../android/src/main/java/chat/simplex/app/views/call/CallActivity.kt#L181)): Binds to `CallService` as a workaround for Android 12 background activity launch restrictions.
|
||||
- **CallActivityView composable** ([line 208](../../android/src/main/java/chat/simplex/app/views/call/CallActivity.kt#L208)): Renders `ActiveCallView()` when permissions are granted and a call is active. Shows `CallPermissionsView` when permissions are needed. Shows `IncomingCallLockScreenAlert` for incoming calls on the lock screen.
|
||||
- **Intent handling** ([line 65](../../android/src/main/java/chat/simplex/app/views/call/CallActivity.kt#L65)): On `AcceptCallAction` intent, looks up the matching `RcvCallInvitation` and calls `callManager.acceptIncomingCall()`.
|
||||
- **Lock screen support** ([line 161](../../android/src/main/java/chat/simplex/app/views/call/CallActivity.kt#L161)): `unlockForIncomingCall()` uses `setShowWhenLocked(true)` / `setTurnScreenOn(true)` on API 27+, falls back to window flags on older versions. `lockAfterIncomingCall()` reverses these settings.
|
||||
- **Picture-in-Picture** ([line 100](../../android/src/main/java/chat/simplex/app/views/call/CallActivity.kt#L100)): `setPipParams()` configures PiP aspect ratio and source rect hint. On Android 12+ (`Build.VERSION_CODES.S`), auto-enter PiP is enabled for video calls. `onPictureInPictureModeChanged` toggles `activeCallViewIsCollapsed` and sends a `WCallCommand.Layout` command.
|
||||
- **Permission checks** ([line 123](../../android/src/main/java/chat/simplex/app/views/call/CallActivity.kt#L123)): Checks `RECORD_AUDIO` and conditionally `CAMERA` permissions.
|
||||
- **Service binding** ([line 182](../../android/src/main/java/chat/simplex/app/views/call/CallActivity.kt#L182)): Binds to `CallService` as a workaround for Android 12 background activity launch restrictions.
|
||||
- **CallActivityView composable** ([line 209](../../android/src/main/java/chat/simplex/app/views/call/CallActivity.kt#L209)): Renders `ActiveCallView()` when permissions are granted and a call is active. Shows `CallPermissionsView` when permissions are needed. Shows `IncomingCallLockScreenAlert` for incoming calls on the lock screen.
|
||||
|
||||
### 3.2 CallService.kt (207 lines)
|
||||
|
||||
@@ -169,7 +169,7 @@ An in-app notification banner shown when a call invitation arrives while the app
|
||||
| `CallView.kt` | [`common/src/commonMain/.../views/call/CallView.kt`](../../common/src/commonMain/kotlin/chat/simplex/common/views/call/CallView.kt) | 28 | `expect fun ActiveCallView()`, delivery receipt waiting |
|
||||
| `CallView.android.kt` | [`common/src/androidMain/.../views/call/CallView.android.kt`](../../common/src/androidMain/kotlin/chat/simplex/common/views/call/CallView.android.kt) | 891 | Android WebView WebRTC, overlay, permissions |
|
||||
| `CallView.desktop.kt` | [`common/src/desktopMain/.../views/call/CallView.desktop.kt`](../../common/src/desktopMain/kotlin/chat/simplex/common/views/call/CallView.desktop.kt) | 308 | Desktop browser WebRTC via NanoWSD |
|
||||
| `CallActivity.kt` | [`android/src/main/java/.../views/call/CallActivity.kt`](../../android/src/main/java/chat/simplex/app/views/call/CallActivity.kt) | 464 | Android call Activity, PiP, lock screen |
|
||||
| `CallActivity.kt` | [`android/src/main/java/.../views/call/CallActivity.kt`](../../android/src/main/java/chat/simplex/app/views/call/CallActivity.kt) | 472 | Android call Activity, PiP, lock screen |
|
||||
| `CallService.kt` | [`android/src/main/java/.../CallService.kt`](../../android/src/main/java/chat/simplex/app/CallService.kt) | 207 | Android foreground service for calls |
|
||||
| `CallManager.kt` | [`common/src/commonMain/.../views/call/CallManager.kt`](../../common/src/commonMain/kotlin/chat/simplex/common/views/call/CallManager.kt) | 119 | Call lifecycle management |
|
||||
| `WebRTC.kt` | [`common/src/commonMain/.../views/call/WebRTC.kt`](../../common/src/commonMain/kotlin/chat/simplex/common/views/call/WebRTC.kt) | -- | `CallState` enum, `WCallCommand`, `WCallResponse` types |
|
||||
|
||||
@@ -35,16 +35,16 @@ The architecture uses an abstract `NtfManager` in common code with platform-spec
|
||||
|
||||
[`NtfManager.kt`](../../common/src/commonMain/kotlin/chat/simplex/common/platform/NtfManager.kt) (139 lines, commonMain)
|
||||
|
||||
The global `ntfManager` instance is declared at [line 17](../../common/src/commonMain/kotlin/chat/simplex/common/platform/NtfManager.kt#L17) and initialized by each platform at startup.
|
||||
The global `ntfManager` instance is declared at [line 18](../../common/src/commonMain/kotlin/chat/simplex/common/platform/NtfManager.kt#L18) and initialized by each platform at startup.
|
||||
|
||||
### Concrete methods
|
||||
|
||||
| Method | Line | Description |
|
||||
|---|---|---|
|
||||
| `notifyContactConnected` | [L20](../../common/src/commonMain/kotlin/chat/simplex/common/platform/NtfManager.kt#L20) | Displays "contact connected" notification for a `Contact` |
|
||||
| `notifyContactRequestReceived` | [L27](../../common/src/commonMain/kotlin/chat/simplex/common/platform/NtfManager.kt#L27) | Shows contact request notification with an "Accept" action button |
|
||||
| `notifyMessageReceived` | [L38](../../common/src/commonMain/kotlin/chat/simplex/common/platform/NtfManager.kt#L38) | Conditionally shows message notification based on `ntfsEnabled`, `showNotification`, and whether user is viewing that chat |
|
||||
| `acceptContactRequestAction` | [L51](../../common/src/commonMain/kotlin/chat/simplex/common/platform/NtfManager.kt#L51) | Accepts a contact request from a notification action |
|
||||
| `notifyContactConnected` | [L21](../../common/src/commonMain/kotlin/chat/simplex/common/platform/NtfManager.kt#L21) | Displays "contact connected" notification for a `Contact` |
|
||||
| `notifyContactRequestReceived` | [L28](../../common/src/commonMain/kotlin/chat/simplex/common/platform/NtfManager.kt#L28) | Shows contact request notification with an "Accept" action button |
|
||||
| `notifyMessageReceived` | [L39](../../common/src/commonMain/kotlin/chat/simplex/common/platform/NtfManager.kt#L39) | Conditionally shows message notification based on `ntfsEnabled`, `showNotification`, and whether user is viewing that chat |
|
||||
| `acceptContactRequestAction` | [L52](../../common/src/commonMain/kotlin/chat/simplex/common/platform/NtfManager.kt#L52) | Accepts a contact request from a notification action |
|
||||
| `openChatAction` | [L59](../../common/src/commonMain/kotlin/chat/simplex/common/platform/NtfManager.kt#L59) | Opens a specific chat from a notification tap, switching user if needed |
|
||||
| `showChatsAction` | [L74](../../common/src/commonMain/kotlin/chat/simplex/common/platform/NtfManager.kt#L74) | Opens the chat list, switching user if needed |
|
||||
| `acceptCallAction` | [L88](../../common/src/commonMain/kotlin/chat/simplex/common/platform/NtfManager.kt#L88) | Accepts a call invitation from a notification action |
|
||||
|
||||
Reference in New Issue
Block a user