Skip to content

Commit 232ae78

Browse files
feat: Add runtime permission check for RECORD_AUDIO
1 parent 803857d commit 232ae78

File tree

4 files changed

+21
-4
lines changed

4 files changed

+21
-4
lines changed

firebase-ai/src/main/kotlin/com/google/firebase/ai/LiveGenerativeModel.kt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,11 @@ internal constructor(
113113
val receivedJson = JSON.parseToJsonElement(receivedJsonStr)
114114

115115
return if (receivedJson is JsonObject && "setupComplete" in receivedJson) {
116-
LiveSession(session = webSession, blockingDispatcher = blockingDispatcher)
116+
LiveSession(
117+
session = webSession,
118+
blockingDispatcher = blockingDispatcher,
119+
firebaseApp = controller.firebaseApp
120+
)
117121
} else {
118122
webSession.close()
119123
throw ServiceConnectionHandshakeFailedException("Unable to connect to the server")

firebase-ai/src/main/kotlin/com/google/firebase/ai/common/APIController.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ internal constructor(
9797
private val requestOptions: RequestOptions,
9898
httpEngine: HttpClientEngine,
9999
private val apiClient: String,
100-
private val firebaseApp: FirebaseApp,
100+
internal val firebaseApp: FirebaseApp,
101101
private val appVersion: Int = 0,
102102
private val googleAppId: String,
103103
private val headerProvider: HeaderProvider?,

firebase-ai/src/main/kotlin/com/google/firebase/ai/type/Exceptions.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,3 +222,6 @@ public class ServiceConnectionHandshakeFailedException(message: String, cause: T
222222
/** Catch all case for exceptions not explicitly expected. */
223223
public class UnknownException internal constructor(message: String, cause: Throwable? = null) :
224224
FirebaseAIException(message, cause)
225+
226+
/** A required permission is missing. */
227+
public class PermissionMissingException(message: String) : FirebaseAIException(message)

firebase-ai/src/main/kotlin/com/google/firebase/ai/type/LiveSession.kt

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ import androidx.annotation.RequiresPermission
2424
import com.google.firebase.ai.common.JSON
2525
import com.google.firebase.ai.common.util.CancelledCoroutineScope
2626
import com.google.firebase.ai.common.util.accumulateUntil
27+
import com.google.firebase.FirebaseApp
28+
import android.content.pm.PackageManager
2729
import com.google.firebase.ai.common.util.childJob
2830
import com.google.firebase.annotations.concurrent.Blocking
2931
import io.ktor.client.plugins.websocket.ClientWebSocketSession
@@ -58,7 +60,8 @@ public class LiveSession
5860
internal constructor(
5961
private val session: ClientWebSocketSession,
6062
@Blocking private val blockingDispatcher: CoroutineContext,
61-
private var audioHelper: AudioHelper? = null
63+
private var audioHelper: AudioHelper? = null,
64+
private val firebaseApp: FirebaseApp,
6265
) {
6366
/**
6467
* Coroutine scope that we batch data on for [startAudioConversation].
@@ -93,12 +96,19 @@ internal constructor(
9396
public suspend fun startAudioConversation(
9497
functionCallHandler: ((FunctionCallPart) -> FunctionResponsePart)? = null
9598
) {
99+
val context = firebaseApp.applicationContext
100+
if (context.checkSelfPermission(RECORD_AUDIO) !=
101+
android.content.pm.PackageManager.PERMISSION_GRANTED
102+
) {
103+
throw PermissionMissingException("Missing RECORD_AUDIO permission.")
104+
}
105+
96106
FirebaseAIException.catchAsync {
97107
if (scope.isActive) {
98108
Log.w(
99109
TAG,
100110
"startAudioConversation called after the recording has already started. " +
101-
"Call stopAudioConversation to close the previous connection."
111+
"Call stopAudioAudioConversation to close the previous connection."
102112
)
103113
return@catchAsync
104114
}

0 commit comments

Comments
 (0)