From 232ae788563451eaea712682bee856fbf85149e4 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Fri, 1 Aug 2025 13:56:51 +0000 Subject: [PATCH 1/7] feat: Add runtime permission check for RECORD_AUDIO --- .../com/google/firebase/ai/LiveGenerativeModel.kt | 6 +++++- .../com/google/firebase/ai/common/APIController.kt | 2 +- .../com/google/firebase/ai/type/Exceptions.kt | 3 +++ .../com/google/firebase/ai/type/LiveSession.kt | 14 ++++++++++++-- 4 files changed, 21 insertions(+), 4 deletions(-) diff --git a/firebase-ai/src/main/kotlin/com/google/firebase/ai/LiveGenerativeModel.kt b/firebase-ai/src/main/kotlin/com/google/firebase/ai/LiveGenerativeModel.kt index 45e10114f72..424837280ef 100644 --- a/firebase-ai/src/main/kotlin/com/google/firebase/ai/LiveGenerativeModel.kt +++ b/firebase-ai/src/main/kotlin/com/google/firebase/ai/LiveGenerativeModel.kt @@ -113,7 +113,11 @@ internal constructor( val receivedJson = JSON.parseToJsonElement(receivedJsonStr) return if (receivedJson is JsonObject && "setupComplete" in receivedJson) { - LiveSession(session = webSession, blockingDispatcher = blockingDispatcher) + LiveSession( + session = webSession, + blockingDispatcher = blockingDispatcher, + firebaseApp = controller.firebaseApp + ) } else { webSession.close() throw ServiceConnectionHandshakeFailedException("Unable to connect to the server") diff --git a/firebase-ai/src/main/kotlin/com/google/firebase/ai/common/APIController.kt b/firebase-ai/src/main/kotlin/com/google/firebase/ai/common/APIController.kt index f1f51dabab9..ec96e2941c2 100644 --- a/firebase-ai/src/main/kotlin/com/google/firebase/ai/common/APIController.kt +++ b/firebase-ai/src/main/kotlin/com/google/firebase/ai/common/APIController.kt @@ -97,7 +97,7 @@ internal constructor( private val requestOptions: RequestOptions, httpEngine: HttpClientEngine, private val apiClient: String, - private val firebaseApp: FirebaseApp, + internal val firebaseApp: FirebaseApp, private val appVersion: Int = 0, private val googleAppId: String, private val headerProvider: HeaderProvider?, diff --git a/firebase-ai/src/main/kotlin/com/google/firebase/ai/type/Exceptions.kt b/firebase-ai/src/main/kotlin/com/google/firebase/ai/type/Exceptions.kt index 57a27f241a0..fc583030a45 100644 --- a/firebase-ai/src/main/kotlin/com/google/firebase/ai/type/Exceptions.kt +++ b/firebase-ai/src/main/kotlin/com/google/firebase/ai/type/Exceptions.kt @@ -222,3 +222,6 @@ public class ServiceConnectionHandshakeFailedException(message: String, cause: T /** Catch all case for exceptions not explicitly expected. */ public class UnknownException internal constructor(message: String, cause: Throwable? = null) : FirebaseAIException(message, cause) + +/** A required permission is missing. */ +public class PermissionMissingException(message: String) : FirebaseAIException(message) diff --git a/firebase-ai/src/main/kotlin/com/google/firebase/ai/type/LiveSession.kt b/firebase-ai/src/main/kotlin/com/google/firebase/ai/type/LiveSession.kt index 1f84c18a53b..d010c778f39 100644 --- a/firebase-ai/src/main/kotlin/com/google/firebase/ai/type/LiveSession.kt +++ b/firebase-ai/src/main/kotlin/com/google/firebase/ai/type/LiveSession.kt @@ -24,6 +24,8 @@ import androidx.annotation.RequiresPermission import com.google.firebase.ai.common.JSON import com.google.firebase.ai.common.util.CancelledCoroutineScope import com.google.firebase.ai.common.util.accumulateUntil +import com.google.firebase.FirebaseApp +import android.content.pm.PackageManager import com.google.firebase.ai.common.util.childJob import com.google.firebase.annotations.concurrent.Blocking import io.ktor.client.plugins.websocket.ClientWebSocketSession @@ -58,7 +60,8 @@ public class LiveSession internal constructor( private val session: ClientWebSocketSession, @Blocking private val blockingDispatcher: CoroutineContext, - private var audioHelper: AudioHelper? = null + private var audioHelper: AudioHelper? = null, + private val firebaseApp: FirebaseApp, ) { /** * Coroutine scope that we batch data on for [startAudioConversation]. @@ -93,12 +96,19 @@ internal constructor( public suspend fun startAudioConversation( functionCallHandler: ((FunctionCallPart) -> FunctionResponsePart)? = null ) { + val context = firebaseApp.applicationContext + if (context.checkSelfPermission(RECORD_AUDIO) != + android.content.pm.PackageManager.PERMISSION_GRANTED + ) { + throw PermissionMissingException("Missing RECORD_AUDIO permission.") + } + FirebaseAIException.catchAsync { if (scope.isActive) { Log.w( TAG, "startAudioConversation called after the recording has already started. " + - "Call stopAudioConversation to close the previous connection." + "Call stopAudioAudioConversation to close the previous connection." ) return@catchAsync } From c078a92e9908135eb2cc6896c7ba6e5a3da39aa3 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Fri, 1 Aug 2025 14:54:24 +0000 Subject: [PATCH 2/7] test: Add test for runtime permission check --- firebase-ai/firebase-ai.gradle.kts | 1 + .../firebase/ai/type/LiveSessionTest.kt | 33 +++++++++++++++++++ 2 files changed, 34 insertions(+) create mode 100644 firebase-ai/src/test/java/com/google/firebase/ai/type/LiveSessionTest.kt diff --git a/firebase-ai/firebase-ai.gradle.kts b/firebase-ai/firebase-ai.gradle.kts index ba7f21b56fb..955479d7026 100644 --- a/firebase-ai/firebase-ai.gradle.kts +++ b/firebase-ai/firebase-ai.gradle.kts @@ -123,6 +123,7 @@ dependencies { testImplementation(libs.robolectric) testImplementation(libs.truth) testImplementation(libs.mockito.core) + testImplementation(libs.mockito.kotlin) androidTestImplementation(libs.androidx.espresso.core) androidTestImplementation(libs.androidx.test.junit) diff --git a/firebase-ai/src/test/java/com/google/firebase/ai/type/LiveSessionTest.kt b/firebase-ai/src/test/java/com/google/firebase/ai/type/LiveSessionTest.kt new file mode 100644 index 00000000000..dab9a00e2aa --- /dev/null +++ b/firebase-ai/src/test/java/com/google/firebase/ai/type/LiveSessionTest.kt @@ -0,0 +1,33 @@ +package com.google.firebase.ai.type + +import android.content.Context +import android.content.pm.PackageManager +import com.google.firebase.FirebaseApp +import com.google.firebase.ai.util.doBlocking +import io.kotest.assertions.throwables.shouldThrow +import io.ktor.client.plugins.websocket.testing.* +import kotlinx.coroutines.Dispatchers +import org.junit.Test +import org.mockito.kotlin.* + +class LiveSessionTest { + + @Test + fun `startAudioConversation without permission throws exception`() = doBlocking { + val mockContext = mock { + on { checkSelfPermission(android.Manifest.permission.RECORD_AUDIO) } doReturn PackageManager.PERMISSION_DENIED + } + val mockFirebaseApp = mock { + on { applicationContext } doReturn mockContext + } + val session = LiveSession( + session = EmptyWebSockets.client.session, + blockingDispatcher = Dispatchers.IO, + firebaseApp = mockFirebaseApp + ) + + shouldThrow { + session.startAudioConversation() + } + } +} From ccec89014d1c5da6a0f7fc31ff2c3d0c1f51e8e1 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Fri, 1 Aug 2025 17:54:53 +0000 Subject: [PATCH 3/7] refactor: Use standard Mockito syntax in test --- firebase-ai/firebase-ai.gradle.kts | 1 - .../com/google/firebase/ai/type/LiveSessionTest.kt | 14 ++++++-------- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/firebase-ai/firebase-ai.gradle.kts b/firebase-ai/firebase-ai.gradle.kts index 955479d7026..ba7f21b56fb 100644 --- a/firebase-ai/firebase-ai.gradle.kts +++ b/firebase-ai/firebase-ai.gradle.kts @@ -123,7 +123,6 @@ dependencies { testImplementation(libs.robolectric) testImplementation(libs.truth) testImplementation(libs.mockito.core) - testImplementation(libs.mockito.kotlin) androidTestImplementation(libs.androidx.espresso.core) androidTestImplementation(libs.androidx.test.junit) diff --git a/firebase-ai/src/test/java/com/google/firebase/ai/type/LiveSessionTest.kt b/firebase-ai/src/test/java/com/google/firebase/ai/type/LiveSessionTest.kt index dab9a00e2aa..c62a4a53f82 100644 --- a/firebase-ai/src/test/java/com/google/firebase/ai/type/LiveSessionTest.kt +++ b/firebase-ai/src/test/java/com/google/firebase/ai/type/LiveSessionTest.kt @@ -5,21 +5,19 @@ import android.content.pm.PackageManager import com.google.firebase.FirebaseApp import com.google.firebase.ai.util.doBlocking import io.kotest.assertions.throwables.shouldThrow -import io.ktor.client.plugins.websocket.testing.* +import io.ktor.client.plugins.websocket.testing.EmptyWebSockets import kotlinx.coroutines.Dispatchers import org.junit.Test -import org.mockito.kotlin.* +import org.mockito.Mockito class LiveSessionTest { @Test fun `startAudioConversation without permission throws exception`() = doBlocking { - val mockContext = mock { - on { checkSelfPermission(android.Manifest.permission.RECORD_AUDIO) } doReturn PackageManager.PERMISSION_DENIED - } - val mockFirebaseApp = mock { - on { applicationContext } doReturn mockContext - } + val mockContext = Mockito.mock(Context::class.java) + Mockito.`when`(mockContext.checkSelfPermission(android.Manifest.permission.RECORD_AUDIO)).thenReturn(PackageManager.PERMISSION_DENIED) + val mockFirebaseApp = Mockito.mock(FirebaseApp::class.java) + Mockito.`when`(mockFirebaseApp.applicationContext).thenReturn(mockContext) val session = LiveSession( session = EmptyWebSockets.client.session, blockingDispatcher = Dispatchers.IO, From 28f43e6a37123bb59a3bbae7da7806535ba4dd23 Mon Sep 17 00:00:00 2001 From: Ryan Wilson Date: Fri, 1 Aug 2025 13:55:21 -0400 Subject: [PATCH 4/7] Formatting --- .../google/firebase/ai/type/LiveSession.kt | 9 +++--- .../firebase/ai/type/LiveSessionTest.kt | 30 +++++++++---------- 2 files changed, 20 insertions(+), 19 deletions(-) diff --git a/firebase-ai/src/main/kotlin/com/google/firebase/ai/type/LiveSession.kt b/firebase-ai/src/main/kotlin/com/google/firebase/ai/type/LiveSession.kt index d010c778f39..03ddb56cc59 100644 --- a/firebase-ai/src/main/kotlin/com/google/firebase/ai/type/LiveSession.kt +++ b/firebase-ai/src/main/kotlin/com/google/firebase/ai/type/LiveSession.kt @@ -17,15 +17,15 @@ package com.google.firebase.ai.type import android.Manifest.permission.RECORD_AUDIO +import android.content.pm.PackageManager import android.media.AudioFormat import android.media.AudioTrack import android.util.Log import androidx.annotation.RequiresPermission +import com.google.firebase.FirebaseApp import com.google.firebase.ai.common.JSON import com.google.firebase.ai.common.util.CancelledCoroutineScope import com.google.firebase.ai.common.util.accumulateUntil -import com.google.firebase.FirebaseApp -import android.content.pm.PackageManager import com.google.firebase.ai.common.util.childJob import com.google.firebase.annotations.concurrent.Blocking import io.ktor.client.plugins.websocket.ClientWebSocketSession @@ -97,8 +97,9 @@ internal constructor( functionCallHandler: ((FunctionCallPart) -> FunctionResponsePart)? = null ) { val context = firebaseApp.applicationContext - if (context.checkSelfPermission(RECORD_AUDIO) != - android.content.pm.PackageManager.PERMISSION_GRANTED + if ( + context.checkSelfPermission(RECORD_AUDIO) != + android.content.pm.PackageManager.PERMISSION_GRANTED ) { throw PermissionMissingException("Missing RECORD_AUDIO permission.") } diff --git a/firebase-ai/src/test/java/com/google/firebase/ai/type/LiveSessionTest.kt b/firebase-ai/src/test/java/com/google/firebase/ai/type/LiveSessionTest.kt index c62a4a53f82..d64dfa58575 100644 --- a/firebase-ai/src/test/java/com/google/firebase/ai/type/LiveSessionTest.kt +++ b/firebase-ai/src/test/java/com/google/firebase/ai/type/LiveSessionTest.kt @@ -12,20 +12,20 @@ import org.mockito.Mockito class LiveSessionTest { - @Test - fun `startAudioConversation without permission throws exception`() = doBlocking { - val mockContext = Mockito.mock(Context::class.java) - Mockito.`when`(mockContext.checkSelfPermission(android.Manifest.permission.RECORD_AUDIO)).thenReturn(PackageManager.PERMISSION_DENIED) - val mockFirebaseApp = Mockito.mock(FirebaseApp::class.java) - Mockito.`when`(mockFirebaseApp.applicationContext).thenReturn(mockContext) - val session = LiveSession( - session = EmptyWebSockets.client.session, - blockingDispatcher = Dispatchers.IO, - firebaseApp = mockFirebaseApp - ) + @Test + fun `startAudioConversation without permission throws exception`() = doBlocking { + val mockContext = Mockito.mock(Context::class.java) + Mockito.`when`(mockContext.checkSelfPermission(android.Manifest.permission.RECORD_AUDIO)) + .thenReturn(PackageManager.PERMISSION_DENIED) + val mockFirebaseApp = Mockito.mock(FirebaseApp::class.java) + Mockito.`when`(mockFirebaseApp.applicationContext).thenReturn(mockContext) + val session = + LiveSession( + session = EmptyWebSockets.client.session, + blockingDispatcher = Dispatchers.IO, + firebaseApp = mockFirebaseApp + ) - shouldThrow { - session.startAudioConversation() - } - } + shouldThrow { session.startAudioConversation() } + } } From eb2a4ac29067ea52c01449fb7d95e28fc40c578e Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Fri, 1 Aug 2025 19:23:32 +0000 Subject: [PATCH 5/7] fix: Re-add test dependencies and revert to mockito-kotlin syntax --- firebase-ai/firebase-ai.gradle.kts | 2 ++ .../google/firebase/ai/type/LiveSession.kt | 9 +++-- .../firebase/ai/type/LiveSessionTest.kt | 36 ++++++++++--------- 3 files changed, 25 insertions(+), 22 deletions(-) diff --git a/firebase-ai/firebase-ai.gradle.kts b/firebase-ai/firebase-ai.gradle.kts index ba7f21b56fb..104dc555633 100644 --- a/firebase-ai/firebase-ai.gradle.kts +++ b/firebase-ai/firebase-ai.gradle.kts @@ -123,6 +123,8 @@ dependencies { testImplementation(libs.robolectric) testImplementation(libs.truth) testImplementation(libs.mockito.core) + testImplementation(libs.mockito.kotlin) + testImplementation(libs.ktor.client.mock) androidTestImplementation(libs.androidx.espresso.core) androidTestImplementation(libs.androidx.test.junit) diff --git a/firebase-ai/src/main/kotlin/com/google/firebase/ai/type/LiveSession.kt b/firebase-ai/src/main/kotlin/com/google/firebase/ai/type/LiveSession.kt index 03ddb56cc59..d010c778f39 100644 --- a/firebase-ai/src/main/kotlin/com/google/firebase/ai/type/LiveSession.kt +++ b/firebase-ai/src/main/kotlin/com/google/firebase/ai/type/LiveSession.kt @@ -17,15 +17,15 @@ package com.google.firebase.ai.type import android.Manifest.permission.RECORD_AUDIO -import android.content.pm.PackageManager import android.media.AudioFormat import android.media.AudioTrack import android.util.Log import androidx.annotation.RequiresPermission -import com.google.firebase.FirebaseApp import com.google.firebase.ai.common.JSON import com.google.firebase.ai.common.util.CancelledCoroutineScope import com.google.firebase.ai.common.util.accumulateUntil +import com.google.firebase.FirebaseApp +import android.content.pm.PackageManager import com.google.firebase.ai.common.util.childJob import com.google.firebase.annotations.concurrent.Blocking import io.ktor.client.plugins.websocket.ClientWebSocketSession @@ -97,9 +97,8 @@ internal constructor( functionCallHandler: ((FunctionCallPart) -> FunctionResponsePart)? = null ) { val context = firebaseApp.applicationContext - if ( - context.checkSelfPermission(RECORD_AUDIO) != - android.content.pm.PackageManager.PERMISSION_GRANTED + if (context.checkSelfPermission(RECORD_AUDIO) != + android.content.pm.PackageManager.PERMISSION_GRANTED ) { throw PermissionMissingException("Missing RECORD_AUDIO permission.") } diff --git a/firebase-ai/src/test/java/com/google/firebase/ai/type/LiveSessionTest.kt b/firebase-ai/src/test/java/com/google/firebase/ai/type/LiveSessionTest.kt index d64dfa58575..dab9a00e2aa 100644 --- a/firebase-ai/src/test/java/com/google/firebase/ai/type/LiveSessionTest.kt +++ b/firebase-ai/src/test/java/com/google/firebase/ai/type/LiveSessionTest.kt @@ -5,27 +5,29 @@ import android.content.pm.PackageManager import com.google.firebase.FirebaseApp import com.google.firebase.ai.util.doBlocking import io.kotest.assertions.throwables.shouldThrow -import io.ktor.client.plugins.websocket.testing.EmptyWebSockets +import io.ktor.client.plugins.websocket.testing.* import kotlinx.coroutines.Dispatchers import org.junit.Test -import org.mockito.Mockito +import org.mockito.kotlin.* class LiveSessionTest { - @Test - fun `startAudioConversation without permission throws exception`() = doBlocking { - val mockContext = Mockito.mock(Context::class.java) - Mockito.`when`(mockContext.checkSelfPermission(android.Manifest.permission.RECORD_AUDIO)) - .thenReturn(PackageManager.PERMISSION_DENIED) - val mockFirebaseApp = Mockito.mock(FirebaseApp::class.java) - Mockito.`when`(mockFirebaseApp.applicationContext).thenReturn(mockContext) - val session = - LiveSession( - session = EmptyWebSockets.client.session, - blockingDispatcher = Dispatchers.IO, - firebaseApp = mockFirebaseApp - ) + @Test + fun `startAudioConversation without permission throws exception`() = doBlocking { + val mockContext = mock { + on { checkSelfPermission(android.Manifest.permission.RECORD_AUDIO) } doReturn PackageManager.PERMISSION_DENIED + } + val mockFirebaseApp = mock { + on { applicationContext } doReturn mockContext + } + val session = LiveSession( + session = EmptyWebSockets.client.session, + blockingDispatcher = Dispatchers.IO, + firebaseApp = mockFirebaseApp + ) - shouldThrow { session.startAudioConversation() } - } + shouldThrow { + session.startAudioConversation() + } + } } From 4eee7d3a8f44ce11814c423332bec8781c81e6fa Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Fri, 1 Aug 2025 19:47:28 +0000 Subject: [PATCH 6/7] fix: Use standard Mockito syntax in test --- firebase-ai/firebase-ai.gradle.kts | 1 - .../com/google/firebase/ai/type/LiveSessionTest.kt | 14 ++++++-------- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/firebase-ai/firebase-ai.gradle.kts b/firebase-ai/firebase-ai.gradle.kts index 104dc555633..d935fae55f4 100644 --- a/firebase-ai/firebase-ai.gradle.kts +++ b/firebase-ai/firebase-ai.gradle.kts @@ -123,7 +123,6 @@ dependencies { testImplementation(libs.robolectric) testImplementation(libs.truth) testImplementation(libs.mockito.core) - testImplementation(libs.mockito.kotlin) testImplementation(libs.ktor.client.mock) androidTestImplementation(libs.androidx.espresso.core) diff --git a/firebase-ai/src/test/java/com/google/firebase/ai/type/LiveSessionTest.kt b/firebase-ai/src/test/java/com/google/firebase/ai/type/LiveSessionTest.kt index dab9a00e2aa..c62a4a53f82 100644 --- a/firebase-ai/src/test/java/com/google/firebase/ai/type/LiveSessionTest.kt +++ b/firebase-ai/src/test/java/com/google/firebase/ai/type/LiveSessionTest.kt @@ -5,21 +5,19 @@ import android.content.pm.PackageManager import com.google.firebase.FirebaseApp import com.google.firebase.ai.util.doBlocking import io.kotest.assertions.throwables.shouldThrow -import io.ktor.client.plugins.websocket.testing.* +import io.ktor.client.plugins.websocket.testing.EmptyWebSockets import kotlinx.coroutines.Dispatchers import org.junit.Test -import org.mockito.kotlin.* +import org.mockito.Mockito class LiveSessionTest { @Test fun `startAudioConversation without permission throws exception`() = doBlocking { - val mockContext = mock { - on { checkSelfPermission(android.Manifest.permission.RECORD_AUDIO) } doReturn PackageManager.PERMISSION_DENIED - } - val mockFirebaseApp = mock { - on { applicationContext } doReturn mockContext - } + val mockContext = Mockito.mock(Context::class.java) + Mockito.`when`(mockContext.checkSelfPermission(android.Manifest.permission.RECORD_AUDIO)).thenReturn(PackageManager.PERMISSION_DENIED) + val mockFirebaseApp = Mockito.mock(FirebaseApp::class.java) + Mockito.`when`(mockFirebaseApp.applicationContext).thenReturn(mockContext) val session = LiveSession( session = EmptyWebSockets.client.session, blockingDispatcher = Dispatchers.IO, From 03de7388c8829230b4628441ff6fa03b8b45447e Mon Sep 17 00:00:00 2001 From: Ryan Wilson Date: Fri, 1 Aug 2025 17:07:08 -0400 Subject: [PATCH 7/7] Formatting --- .../google/firebase/ai/type/LiveSession.kt | 9 +++--- .../firebase/ai/type/LiveSessionTest.kt | 30 +++++++++---------- 2 files changed, 20 insertions(+), 19 deletions(-) diff --git a/firebase-ai/src/main/kotlin/com/google/firebase/ai/type/LiveSession.kt b/firebase-ai/src/main/kotlin/com/google/firebase/ai/type/LiveSession.kt index d010c778f39..03ddb56cc59 100644 --- a/firebase-ai/src/main/kotlin/com/google/firebase/ai/type/LiveSession.kt +++ b/firebase-ai/src/main/kotlin/com/google/firebase/ai/type/LiveSession.kt @@ -17,15 +17,15 @@ package com.google.firebase.ai.type import android.Manifest.permission.RECORD_AUDIO +import android.content.pm.PackageManager import android.media.AudioFormat import android.media.AudioTrack import android.util.Log import androidx.annotation.RequiresPermission +import com.google.firebase.FirebaseApp import com.google.firebase.ai.common.JSON import com.google.firebase.ai.common.util.CancelledCoroutineScope import com.google.firebase.ai.common.util.accumulateUntil -import com.google.firebase.FirebaseApp -import android.content.pm.PackageManager import com.google.firebase.ai.common.util.childJob import com.google.firebase.annotations.concurrent.Blocking import io.ktor.client.plugins.websocket.ClientWebSocketSession @@ -97,8 +97,9 @@ internal constructor( functionCallHandler: ((FunctionCallPart) -> FunctionResponsePart)? = null ) { val context = firebaseApp.applicationContext - if (context.checkSelfPermission(RECORD_AUDIO) != - android.content.pm.PackageManager.PERMISSION_GRANTED + if ( + context.checkSelfPermission(RECORD_AUDIO) != + android.content.pm.PackageManager.PERMISSION_GRANTED ) { throw PermissionMissingException("Missing RECORD_AUDIO permission.") } diff --git a/firebase-ai/src/test/java/com/google/firebase/ai/type/LiveSessionTest.kt b/firebase-ai/src/test/java/com/google/firebase/ai/type/LiveSessionTest.kt index c62a4a53f82..d64dfa58575 100644 --- a/firebase-ai/src/test/java/com/google/firebase/ai/type/LiveSessionTest.kt +++ b/firebase-ai/src/test/java/com/google/firebase/ai/type/LiveSessionTest.kt @@ -12,20 +12,20 @@ import org.mockito.Mockito class LiveSessionTest { - @Test - fun `startAudioConversation without permission throws exception`() = doBlocking { - val mockContext = Mockito.mock(Context::class.java) - Mockito.`when`(mockContext.checkSelfPermission(android.Manifest.permission.RECORD_AUDIO)).thenReturn(PackageManager.PERMISSION_DENIED) - val mockFirebaseApp = Mockito.mock(FirebaseApp::class.java) - Mockito.`when`(mockFirebaseApp.applicationContext).thenReturn(mockContext) - val session = LiveSession( - session = EmptyWebSockets.client.session, - blockingDispatcher = Dispatchers.IO, - firebaseApp = mockFirebaseApp - ) + @Test + fun `startAudioConversation without permission throws exception`() = doBlocking { + val mockContext = Mockito.mock(Context::class.java) + Mockito.`when`(mockContext.checkSelfPermission(android.Manifest.permission.RECORD_AUDIO)) + .thenReturn(PackageManager.PERMISSION_DENIED) + val mockFirebaseApp = Mockito.mock(FirebaseApp::class.java) + Mockito.`when`(mockFirebaseApp.applicationContext).thenReturn(mockContext) + val session = + LiveSession( + session = EmptyWebSockets.client.session, + blockingDispatcher = Dispatchers.IO, + firebaseApp = mockFirebaseApp + ) - shouldThrow { - session.startAudioConversation() - } - } + shouldThrow { session.startAudioConversation() } + } }