From 1acaefe956fc70fc0704c2ddb51477eb357733b3 Mon Sep 17 00:00:00 2001 From: VinayGuthal Date: Mon, 18 Aug 2025 11:26:47 -0400 Subject: [PATCH 1/5] add initialization functions --- .../com/google/firebase/ai/java/LiveSessionFutures.kt | 1 + .../kotlin/com/google/firebase/ai/type/Exceptions.kt | 4 ++++ .../kotlin/com/google/firebase/ai/type/LiveSession.kt | 11 +++++++++++ 3 files changed, 16 insertions(+) diff --git a/firebase-ai/src/main/kotlin/com/google/firebase/ai/java/LiveSessionFutures.kt b/firebase-ai/src/main/kotlin/com/google/firebase/ai/java/LiveSessionFutures.kt index 1efa2dfedfc..fe11bffacc3 100644 --- a/firebase-ai/src/main/kotlin/com/google/firebase/ai/java/LiveSessionFutures.kt +++ b/firebase-ai/src/main/kotlin/com/google/firebase/ai/java/LiveSessionFutures.kt @@ -68,6 +68,7 @@ public abstract class LiveSessionFutures internal constructor() { @RequiresPermission(RECORD_AUDIO) public abstract fun stopAudioConversation(): ListenableFuture + /** * Stops receiving from the model. * 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..fdeceecb1b2 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 @@ -219,6 +219,10 @@ public class AudioRecordInitializationFailedException(message: String) : public class ServiceConnectionHandshakeFailedException(message: String, cause: Throwable? = null) : FirebaseAIException(message, cause) +/** The request is missing a permission that is required to perform the requested operation. */ +public class PermissionMissingException(message: String, cause: Throwable? = null) : + FirebaseAIException(message, cause) + /** Catch all case for exceptions not explicitly expected. */ public class UnknownException internal constructor(message: String, cause: Throwable? = null) : FirebaseAIException(message, cause) 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 6e584fe2a50..36d3195d1c2 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 @@ -131,6 +131,17 @@ internal constructor( } } + + /** + * Indicates whether the underlying websocket connection is active. + */ + public fun isActive(): Boolean = session.isActive + + /** + * Indicates whether an audio conversation is being used for this session object. + */ + public fun isAudioConversationRunning(): Boolean = (audioHelper == null) + /** * Receives responses from the model for both streaming and standard requests. * From 6df111257f68fc2609b4d1e4ad8cb561bfc0b449 Mon Sep 17 00:00:00 2001 From: VinayGuthal Date: Mon, 18 Aug 2025 20:53:25 -0400 Subject: [PATCH 2/5] add everything --- .../google/firebase/ai/LiveGenerativeModel.kt | 8 +++++-- .../firebase/ai/common/APIController.kt | 2 +- .../firebase/ai/java/LiveSessionFutures.kt | 1 - .../google/firebase/ai/type/LiveSession.kt | 23 ++++++++++++------- 4 files changed, 22 insertions(+), 12 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..624d1cc205c 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,13 +113,17 @@ 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") } } catch (e: ClosedReceiveChannelException) { - throw ServiceConnectionHandshakeFailedException("Channel was closed by the server", e) + throw ServiceConnectionHandshakeFailedException("Error: Too many concurrent live requests", e) } } 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/java/LiveSessionFutures.kt b/firebase-ai/src/main/kotlin/com/google/firebase/ai/java/LiveSessionFutures.kt index fe11bffacc3..1efa2dfedfc 100644 --- a/firebase-ai/src/main/kotlin/com/google/firebase/ai/java/LiveSessionFutures.kt +++ b/firebase-ai/src/main/kotlin/com/google/firebase/ai/java/LiveSessionFutures.kt @@ -68,7 +68,6 @@ public abstract class LiveSessionFutures internal constructor() { @RequiresPermission(RECORD_AUDIO) public abstract fun stopAudioConversation(): ListenableFuture - /** * Stops receiving from the model. * 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 36d3195d1c2..0d6418315a2 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,10 +17,13 @@ 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 androidx.core.content.ContextCompat +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 @@ -58,7 +61,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,6 +97,14 @@ internal constructor( public suspend fun startAudioConversation( functionCallHandler: ((FunctionCallPart) -> FunctionResponsePart)? = null ) { + + val context = firebaseApp.applicationContext + if ( + ContextCompat.checkSelfPermission(context, RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED + ) { + throw PermissionMissingException("Audio access not provided by the user") + } + FirebaseAIException.catchAsync { if (scope.isActive) { Log.w( @@ -131,15 +143,10 @@ internal constructor( } } - - /** - * Indicates whether the underlying websocket connection is active. - */ + /** Indicates whether the underlying websocket connection is active. */ public fun isActive(): Boolean = session.isActive - /** - * Indicates whether an audio conversation is being used for this session object. - */ + /** Indicates whether an audio conversation is being used for this session object. */ public fun isAudioConversationRunning(): Boolean = (audioHelper == null) /** From dd06a46024cd4963d8b278f47539a62234ccab96 Mon Sep 17 00:00:00 2001 From: VinayGuthal Date: Mon, 18 Aug 2025 20:54:50 -0400 Subject: [PATCH 3/5] commit --- .../src/main/kotlin/com/google/firebase/ai/type/LiveSession.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 0d6418315a2..3f50aff2471 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 @@ -147,7 +147,7 @@ internal constructor( public fun isActive(): Boolean = session.isActive /** Indicates whether an audio conversation is being used for this session object. */ - public fun isAudioConversationRunning(): Boolean = (audioHelper == null) + public fun isAudioConversationRunning(): Boolean = (audioHelper != null) /** * Receives responses from the model for both streaming and standard requests. From 772b279879e5ffd49ef3c8f27d2d5f2b6914f44f Mon Sep 17 00:00:00 2001 From: VinayGuthal Date: Wed, 20 Aug 2025 14:23:14 -0400 Subject: [PATCH 4/5] add changes to future object as well --- .../google/firebase/ai/java/LiveSessionFutures.kt | 12 ++++++++++++ .../com/google/firebase/ai/type/LiveSession.kt | 2 +- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/firebase-ai/src/main/kotlin/com/google/firebase/ai/java/LiveSessionFutures.kt b/firebase-ai/src/main/kotlin/com/google/firebase/ai/java/LiveSessionFutures.kt index 1efa2dfedfc..20d688b8c46 100644 --- a/firebase-ai/src/main/kotlin/com/google/firebase/ai/java/LiveSessionFutures.kt +++ b/firebase-ai/src/main/kotlin/com/google/firebase/ai/java/LiveSessionFutures.kt @@ -29,6 +29,7 @@ import com.google.firebase.ai.type.MediaData import com.google.firebase.ai.type.PublicPreviewAPI import com.google.firebase.ai.type.SessionAlreadyReceivingException import io.ktor.websocket.close +import kotlinx.coroutines.isActive import kotlinx.coroutines.reactive.asPublisher import org.reactivestreams.Publisher @@ -51,6 +52,12 @@ public abstract class LiveSessionFutures internal constructor() { functionCallHandler: ((FunctionCallPart) -> FunctionResponsePart)? ): ListenableFuture + /** Indicates whether the underlying websocket connection is active. */ + public abstract fun isActive(): ListenableFuture + + /** Indicates whether an audio conversation is being used for this session object. */ + public abstract fun isAudioConversationActive(): ListenableFuture + /** * Starts an audio conversation with the model, which can only be stopped using * [stopAudioConversation]. @@ -169,6 +176,11 @@ public abstract class LiveSessionFutures internal constructor() { override fun startAudioConversation() = SuspendToFutureAdapter.launchFuture { session.startAudioConversation() } + override fun isActive() = SuspendToFutureAdapter.launchFuture { session.isActive() } + + override fun isAudioConversationActive() = + SuspendToFutureAdapter.launchFuture { session.isAudioConversationActive() } + override fun stopAudioConversation() = SuspendToFutureAdapter.launchFuture { session.stopAudioConversation() } 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 3f50aff2471..a3ae8fdf5ce 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 @@ -147,7 +147,7 @@ internal constructor( public fun isActive(): Boolean = session.isActive /** Indicates whether an audio conversation is being used for this session object. */ - public fun isAudioConversationRunning(): Boolean = (audioHelper != null) + public fun isAudioConversationActive(): Boolean = (audioHelper != null) /** * Receives responses from the model for both streaming and standard requests. From 5edbfff4d4a1b59fa69239703589a5e55da34e9b Mon Sep 17 00:00:00 2001 From: VinayGuthal Date: Tue, 26 Aug 2025 12:50:12 -0400 Subject: [PATCH 5/5] pudate --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 236028bb6a5..7d01b70e6b4 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Firebase Android Open Source Development +# Firebase Android Open Source Development T This repository contains the source code for all Android Firebase SDKs except Analytics and Auth.