Skip to content

Commit 502ac30

Browse files
authored
Merge branch 'main' into dependabot/gradle/com.google.code.gson-gson-2.13.2
2 parents 879bedd + 24c1b86 commit 502ac30

File tree

10 files changed

+565
-34
lines changed

10 files changed

+565
-34
lines changed

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

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -331,10 +331,18 @@ internal constructor(
331331
}
332332

333333
/**
334-
* Sends a video input stream to the model, using the realtime API.
334+
* Sends a video frame to the model, using the realtime API.
335335
*
336-
* @param video Encoded video data, used to update the model on the client's conversation. The
337-
* MIME type can be a video format (e.g., `video/webm`) or an image format (e.g., `image/jpeg`).
336+
* Instead of raw video data, the model expects individual frames of the video, sent as images.
337+
*
338+
* If your video has audio, send it separately through [sendAudioRealtime].
339+
*
340+
* For better performance, frames can also be sent at a lower rate than the video; even as low as
341+
* 1 frame per second.
342+
*
343+
* @param video Encoded image data extracted from a frame of the video, used to update the model
344+
* on the client's conversation, with the corresponding IANA standard MIME type of the video frame
345+
* data (e.g., `image/png`, `image/jpeg`, etc.).
338346
*/
339347
public suspend fun sendVideoRealtime(video: InlineData) {
340348
FirebaseAIException.catchAsync {

firebase-dataconnect/CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
# Unreleased
22

33
- [changed] Internal refactor for managing Auth and App Check tokens
4-
([#7184](https://github.com/firebase/firebase-android-sdk/pull/7184))
4+
([#7484](https://github.com/firebase/firebase-android-sdk/pull/7484),
5+
[#7485](https://github.com/firebase/firebase-android-sdk/pull/7485))
56

67
# 17.1.0
78

firebase-dataconnect/src/main/kotlin/com/google/firebase/dataconnect/core/DataConnectAuth.kt

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,13 +49,22 @@ internal class DataConnectAuth(
4949
provider.removeIdTokenListener(idTokenListener)
5050

5151
override suspend fun getToken(provider: InternalAuthProvider, forceRefresh: Boolean) =
52-
provider.getAccessToken(forceRefresh).await().let { GetAuthTokenResult(it.token) }
52+
provider.getAccessToken(forceRefresh).await().let {
53+
GetAuthTokenResult(it.token, it.getAuthUid())
54+
}
5355

54-
data class GetAuthTokenResult(override val token: String?) : GetTokenResult
56+
data class GetAuthTokenResult(override val token: String?, val authUid: String?) : GetTokenResult
5557

5658
private class IdTokenListenerImpl(private val logger: Logger) : IdTokenListener {
5759
override fun onIdTokenChanged(tokenResult: InternalTokenResult) {
5860
logger.debug { "onIdTokenChanged(token=${tokenResult.token?.toScrubbedAccessToken()})" }
5961
}
6062
}
63+
64+
private companion object {
65+
66+
// The "sub" claim is documented to be "a non-empty string and must be the uid of the user or
67+
// device". See http://goo.gle/4oGjEQt for the relevant Firebase documentation.
68+
fun com.google.firebase.auth.GetTokenResult.getAuthUid(): String? = claims["sub"] as? String
69+
}
6170
}

firebase-dataconnect/src/test/kotlin/com/google/firebase/dataconnect/core/DataConnectAuthUnitTest.kt

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,9 @@ import io.kotest.matchers.shouldBe
5454
import io.kotest.matchers.types.shouldBeSameInstanceAs
5555
import io.kotest.property.Arb
5656
import io.kotest.property.RandomSource
57+
import io.kotest.property.arbitrary.map
5758
import io.kotest.property.arbitrary.next
59+
import io.kotest.property.arbs.products.brand
5860
import io.mockk.coEvery
5961
import io.mockk.confirmVerified
6062
import io.mockk.every
@@ -311,6 +313,46 @@ class DataConnectAuthUnitTest {
311313
mockLogger.shouldNotHaveLoggedAnyMessagesContaining(accessToken)
312314
}
313315

316+
@Test
317+
fun `getToken() should populate authUid from sub claim`() = runTest {
318+
val dataConnectAuth = newDataConnectAuth()
319+
dataConnectAuth.initialize()
320+
advanceUntilIdle()
321+
val uid = Arb.brand().map { it.value }.next(rs)
322+
coEvery { mockInternalAuthProvider.getAccessToken(any()) } returns
323+
taskForToken(accessToken, mapOf("sub" to uid))
324+
325+
val result = dataConnectAuth.getToken(requestId)
326+
327+
result.shouldNotBeNull().authUid shouldBe uid
328+
}
329+
330+
@Test
331+
fun `getToken() should populate null authUid if sub claim is missing`() = runTest {
332+
val dataConnectAuth = newDataConnectAuth()
333+
dataConnectAuth.initialize()
334+
advanceUntilIdle()
335+
coEvery { mockInternalAuthProvider.getAccessToken(any()) } returns
336+
taskForToken(accessToken, emptyMap())
337+
338+
val result = dataConnectAuth.getToken(requestId)
339+
340+
result.shouldNotBeNull().authUid.shouldBeNull()
341+
}
342+
343+
@Test
344+
fun `getToken() should populate null authUid if sub claim is not a String`() = runTest {
345+
val dataConnectAuth = newDataConnectAuth()
346+
dataConnectAuth.initialize()
347+
advanceUntilIdle()
348+
coEvery { mockInternalAuthProvider.getAccessToken(any()) } returns
349+
taskForToken(accessToken, mapOf("sub" to 42))
350+
351+
val result = dataConnectAuth.getToken(requestId)
352+
353+
result.shouldNotBeNull().authUid.shouldBeNull()
354+
}
355+
314356
@Test
315357
fun `getToken() should return re-throw the exception from the task returned from FirebaseAuth`() =
316358
runTest {
@@ -613,7 +655,7 @@ class DataConnectAuthUnitTest {
613655
interval = 100.milliseconds
614656
}
615657

616-
fun taskForToken(token: String?): Task<GetTokenResult> =
617-
Tasks.forResult(mockk(relaxed = true) { every { getToken() } returns token })
658+
fun taskForToken(token: String?, claims: Map<String, Any> = emptyMap()): Task<GetTokenResult> =
659+
Tasks.forResult(GetTokenResult(token, claims))
618660
}
619661
}

firebase-dataconnect/src/test/kotlin/com/google/firebase/dataconnect/core/DataConnectGrpcMetadataUnitTest.kt

Lines changed: 46 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ import android.os.Build
1919
import androidx.test.ext.junit.runners.AndroidJUnit4
2020
import com.google.firebase.dataconnect.BuildConfig
2121
import com.google.firebase.dataconnect.FirebaseDataConnect.CallerSdkType
22+
import com.google.firebase.dataconnect.core.DataConnectAppCheck.GetAppCheckTokenResult
23+
import com.google.firebase.dataconnect.core.DataConnectAuth.GetAuthTokenResult
2224
import com.google.firebase.dataconnect.testutil.FirebaseAppUnitTestingRule
2325
import com.google.firebase.dataconnect.testutil.property.arbitrary.appCheckTokenResult
2426
import com.google.firebase.dataconnect.testutil.property.arbitrary.authTokenResult
@@ -31,9 +33,12 @@ import io.kotest.matchers.collections.shouldNotContain
3133
import io.kotest.matchers.shouldBe
3234
import io.kotest.matchers.types.shouldBeSameInstanceAs
3335
import io.kotest.property.Arb
36+
import io.kotest.property.Exhaustive
3437
import io.kotest.property.arbitrary.constant
3538
import io.kotest.property.arbitrary.enum
3639
import io.kotest.property.arbitrary.next
40+
import io.kotest.property.checkAll
41+
import io.kotest.property.exhaustive.of
3742
import io.mockk.coEvery
3843
import io.mockk.mockk
3944
import kotlinx.coroutines.test.runTest
@@ -160,24 +165,33 @@ class DataConnectGrpcMetadataUnitTest {
160165

161166
@Test
162167
fun `should omit x-firebase-auth-token when the auth token is null`() = runTest {
163-
val dataConnectAuth: DataConnectAuth = mockk()
164-
coEvery { dataConnectAuth.getToken(any()) } returns null
165-
val dataConnectGrpcMetadata =
166-
Arb.dataConnect
167-
.dataConnectGrpcMetadata(dataConnectAuth = Arb.constant(dataConnectAuth))
168-
.next()
169-
val requestId = Arb.dataConnect.requestId().next()
170-
val callerSdkType = Arb.enum<CallerSdkType>().next()
168+
val getAuthTokenResults: Exhaustive<GetAuthTokenResult?> =
169+
Exhaustive.of(
170+
null,
171+
Arb.dataConnect.authTokenResult(accessToken = Arb.constant(null)).next(),
172+
)
171173

172-
val metadata = dataConnectGrpcMetadata.get(requestId, callerSdkType)
174+
checkAll(getAuthTokenResults) { getAuthTokenResult ->
175+
val dataConnectAuth: DataConnectAuth = mockk()
176+
coEvery { dataConnectAuth.getToken(any()) } returns getAuthTokenResult
177+
val dataConnectGrpcMetadata =
178+
Arb.dataConnect
179+
.dataConnectGrpcMetadata(dataConnectAuth = Arb.constant(dataConnectAuth))
180+
.next()
181+
val requestId = Arb.dataConnect.requestId().next()
182+
val callerSdkType = Arb.enum<CallerSdkType>().next()
173183

174-
metadata.asClue { it.keys() shouldNotContain "x-firebase-auth-token" }
184+
val metadata = dataConnectGrpcMetadata.get(requestId, callerSdkType)
185+
186+
metadata.asClue { it.keys() shouldNotContain "x-firebase-auth-token" }
187+
}
175188
}
176189

177190
@Test
178191
fun `should include x-firebase-auth-token when the auth token is not null`() = runTest {
179192
val dataConnectAuth: DataConnectAuth = mockk()
180-
val authTokenResult = Arb.dataConnect.authTokenResult().next()
193+
val authTokenResult =
194+
Arb.dataConnect.authTokenResult(accessToken = Arb.dataConnect.accessToken()).next()
181195
coEvery { dataConnectAuth.getToken(any()) } returns authTokenResult
182196
val dataConnectGrpcMetadata =
183197
Arb.dataConnect
@@ -197,24 +211,33 @@ class DataConnectGrpcMetadataUnitTest {
197211

198212
@Test
199213
fun `should omit x-firebase-appcheck when the AppCheck token is null`() = runTest {
200-
val dataConnectAppCheck: DataConnectAppCheck = mockk {
201-
coEvery { getToken(any()) } returns null
202-
}
203-
val dataConnectGrpcMetadata =
204-
Arb.dataConnect
205-
.dataConnectGrpcMetadata(dataConnectAppCheck = Arb.constant(dataConnectAppCheck))
206-
.next()
207-
val requestId = Arb.dataConnect.requestId().next()
208-
val callerSdkType = Arb.enum<CallerSdkType>().next()
214+
val getAppCheckTokenResults: Exhaustive<GetAppCheckTokenResult?> =
215+
Exhaustive.of(
216+
null,
217+
Arb.dataConnect.appCheckTokenResult(accessToken = Arb.constant(null)).next(),
218+
)
209219

210-
val metadata = dataConnectGrpcMetadata.get(requestId, callerSdkType)
220+
checkAll(getAppCheckTokenResults) { getAppCheckTokenResult ->
221+
val dataConnectAppCheck: DataConnectAppCheck = mockk {
222+
coEvery { getToken(any()) } returns getAppCheckTokenResult
223+
}
224+
val dataConnectGrpcMetadata =
225+
Arb.dataConnect
226+
.dataConnectGrpcMetadata(dataConnectAppCheck = Arb.constant(dataConnectAppCheck))
227+
.next()
228+
val requestId = Arb.dataConnect.requestId().next()
229+
val callerSdkType = Arb.enum<CallerSdkType>().next()
211230

212-
metadata.asClue { it.keys() shouldNotContain "x-firebase-appcheck" }
231+
val metadata = dataConnectGrpcMetadata.get(requestId, callerSdkType)
232+
233+
metadata.asClue { it.keys() shouldNotContain "x-firebase-appcheck" }
234+
}
213235
}
214236

215237
@Test
216238
fun `should include x-firebase-appcheck when the AppCheck token is not null`() = runTest {
217-
val appCheckTokenResult = Arb.dataConnect.appCheckTokenResult().next()
239+
val appCheckTokenResult =
240+
Arb.dataConnect.appCheckTokenResult(accessToken = Arb.dataConnect.accessToken()).next()
218241
val dataConnectAppCheck: DataConnectAppCheck = mockk {
219242
coEvery { getToken(any()) } returns appCheckTokenResult
220243
}

firebase-dataconnect/src/test/kotlin/com/google/firebase/dataconnect/testutil/property/arbitrary/arbs.kt

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -333,9 +333,11 @@ internal inline fun <Data, reified Variables> DataConnectArb.operationRefConstru
333333
}
334334

335335
internal fun DataConnectArb.authTokenResult(
336-
accessToken: Arb<String> = accessToken()
337-
): Arb<GetAuthTokenResult> = accessToken.map { GetAuthTokenResult(it) }
336+
accessToken: Arb<String?> = accessToken().orNull(nullProbability = 0.33),
337+
authUid: Arb<String?> =
338+
Arb.string(0..10, Codepoint.alphanumeric()).orNull(nullProbability = 0.33),
339+
): Arb<GetAuthTokenResult> = Arb.bind(accessToken, authUid, ::GetAuthTokenResult)
338340

339341
internal fun DataConnectArb.appCheckTokenResult(
340-
accessToken: Arb<String> = accessToken()
342+
accessToken: Arb<String?> = accessToken().orNull(nullProbability = 0.33),
341343
): Arb<GetAppCheckTokenResult> = accessToken.map { GetAppCheckTokenResult(it) }

firebase-inappmessaging/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# Unreleased
22

3+
- [changed] Bumped internal dependencies.
4+
35
# 22.0.1
46

57
- [changed] Bumped internal dependencies.
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/*
2+
* Copyright 2025 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.google.firebase.gradle.plugins.report
17+
18+
/**
19+
* @param sha Commit SHA.
20+
* @param pr GitHub PR number that was squashed to create this commit. Used only for display
21+
* purposes, does not affect logic.
22+
*/
23+
data class ReportCommit(val sha: String, val pr: Int)
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/*
2+
* Copyright 2025 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.google.firebase.gradle.plugins.report
17+
18+
/**
19+
* Represents a single run of a test in CI. One unit/instrumentation test workflow run creates many
20+
* `TestReport`s, one for each tested SDK.
21+
*
22+
* @param name SDK name of the associated test run.
23+
* @param type What type of test result this is, either unit or instrumentation test.
24+
* @param status Conclusion status of the test run, `SUCCESS`/`FAILURE` for typical results, `OTHER`
25+
* for ongoing runs and unexpected data.
26+
* @param commit Commit SHA this test was run on.
27+
* @param url Link to the GHA test run info, including logs.
28+
*/
29+
data class TestReport(
30+
val name: String,
31+
val type: Type,
32+
val status: Status,
33+
val commit: String,
34+
val url: String,
35+
) {
36+
enum class Type {
37+
UNIT_TEST,
38+
INSTRUMENTATION_TEST,
39+
}
40+
41+
enum class Status {
42+
SUCCESS,
43+
FAILURE,
44+
OTHER,
45+
}
46+
}

0 commit comments

Comments
 (0)