@@ -30,7 +30,6 @@ import com.google.firebase.inject.Provider
3030import com.google.firebase.internal.api.FirebaseNoSignedInUserException
3131import com.google.firebase.util.nextAlphanumericString
3232import java.lang.ref.WeakReference
33- import java.util.concurrent.atomic.AtomicReference
3433import kotlin.coroutines.coroutineContext
3534import kotlin.random.Random
3635import kotlinx.coroutines.CancellationException
@@ -46,10 +45,9 @@ import kotlinx.coroutines.async
4645import kotlinx.coroutines.cancel
4746import kotlinx.coroutines.ensureActive
4847import kotlinx.coroutines.flow.MutableStateFlow
49- import kotlinx.coroutines.flow.StateFlow
50- import kotlinx.coroutines.flow.asStateFlow
48+ import kotlinx.coroutines.flow.filter
49+ import kotlinx.coroutines.flow.first
5150import kotlinx.coroutines.launch
52- import kotlinx.coroutines.yield
5351
5452/* * Base class that shares logic for managing the Auth token and AppCheck token. */
5553internal sealed class DataConnectCredentialsTokenManager <T : Any >(
@@ -61,9 +59,6 @@ internal sealed class DataConnectCredentialsTokenManager<T : Any>(
6159 val instanceId: String
6260 get() = logger.nameWithId
6361
64- private val _providerAvailable = MutableStateFlow (false )
65- val providerAvailable: StateFlow <Boolean > = _providerAvailable .asStateFlow()
66-
6762 @Suppress(" LeakingThis" ) private val weakThis = WeakReference (this )
6863
6964 private val coroutineScope =
@@ -87,49 +82,39 @@ internal sealed class DataConnectCredentialsTokenManager<T : Any>(
8782 }
8883 }
8984
90- private interface ProviderProvider <T > {
91- val provider: T ?
92- }
93-
9485 private sealed interface State <out T > {
9586
9687 /* * State indicating that [close] has been invoked. */
9788 object Closed : State<Nothing>
9889
99- /* * State indicating that there is no outstanding "get token" request. */
100- class Idle <T >(
101-
102- /* *
103- * The [InternalAuthProvider] or [InteropAppCheckTokenProvider]; may be null if the deferred
104- * has not yet given us a provider.
105- */
106- override val provider : T ? ,
107-
90+ sealed interface StateWithForceTokenRefresh <out T > : State <T > {
10891 /* * The value to specify for `forceRefresh` on the next invocation of [getToken]. */
10992 val forceTokenRefresh: Boolean
110- ) : State<T>, ProviderProvider<T>
93+ }
11194
112- /* * State indicating that there _is_ an outstanding "get token" request . */
113- class Active < T >(
95+ /* * State indicating that the token provider is not (yet?) available . */
96+ data class New ( override val forceTokenRefresh : Boolean ) : StateWithForceTokenRefresh<Nothing>
11497
115- /* *
116- * The [InternalAuthProvider] or [InteropAppCheckTokenProvider] that is performing the "get
117- * token" request.
118- */
98+ sealed interface StateWithProvider <out T > : State <T > {
99+ /* * The token provider, [InternalAuthProvider] or [InteropAppCheckTokenProvider] */
100+ val provider: T
101+ }
102+
103+ /* * State indicating that there is no outstanding "get token" request. */
104+ data class Idle <T >(override val provider : T , override val forceTokenRefresh : Boolean ) :
105+ StateWithProvider <T >, StateWithForceTokenRefresh <T >
106+
107+ /* * State indicating that there _is_ an outstanding "get token" request. */
108+ data class Active <out T >(
119109 override val provider : T ,
120110
121111 /* * The job that is performing the "get token" request. */
122112 val job : Deferred <SequencedReference <Result <GetTokenResult >>>
123- ) : State<T>, ProviderProvider <T>
113+ ) : StateWithProvider <T>
124114 }
125115
126- /* *
127- * The current state of this object. The value should only be changed in a compare-and-swap loop
128- * in order to be thread-safe. Such a loop should call `yield()` on each iteration to allow other
129- * coroutines to run on the thread.
130- */
131- private val state =
132- AtomicReference <State <T >>(State .Idle (provider = null , forceTokenRefresh = false ))
116+ /* * The current state of this object. */
117+ private val state = MutableStateFlow <State <T >>(State .New (forceTokenRefresh = false ))
133118
134119 /* *
135120 * Adds the token listener to the given provider.
@@ -168,19 +153,42 @@ internal sealed class DataConnectCredentialsTokenManager<T : Any>(
168153 setClosedState()
169154 }
170155
156+ /* *
157+ * Suspends until the token provider becomes available to this object.
158+ *
159+ * If [close] has been invoked, or is invoked _before_ a token provider becomes available, then
160+ * this method returns normally, as if a token provider _had_ become available.
161+ */
162+ suspend fun awaitTokenProvider () {
163+ logger.debug { " awaitTokenProvider() start" }
164+ val currentState =
165+ state
166+ .filter {
167+ when (it) {
168+ State .Closed -> true
169+ is State .New -> false
170+ is State .Idle -> true
171+ is State .Active -> true
172+ }
173+ }
174+ .first()
175+ logger.debug { " awaitTokenProvider() done: currentState=$currentState " }
176+ }
177+
171178 // This function must ONLY be called from close().
172179 private fun setClosedState () {
173180 while (true ) {
174- val oldState = state.get()
175- val providerProvider : ProviderProvider < T > =
181+ val oldState = state.value
182+ val provider : T ? =
176183 when (oldState) {
177184 is State .Closed -> return
178- is State .Idle -> oldState
179- is State .Active -> oldState
185+ is State .New -> null
186+ is State .Idle -> oldState.provider
187+ is State .Active -> oldState.provider
180188 }
181189
182190 if (state.compareAndSet(oldState, State .Closed )) {
183- providerProvider. provider?.let { removeTokenListener(it) }
191+ provider?.let { removeTokenListener(it) }
184192 break
185193 }
186194 }
@@ -191,27 +199,28 @@ internal sealed class DataConnectCredentialsTokenManager<T : Any>(
191199 *
192200 * If [close] has been called, this method does nothing.
193201 */
194- suspend fun forceRefresh () {
202+ fun forceRefresh () {
195203 logger.debug { " forceRefresh()" }
196204 while (true ) {
197- val oldState = state.get()
198- val oldStateProviderProvider =
205+ val oldState = state.value
206+ val newState : State . StateWithForceTokenRefresh < T > =
199207 when (oldState) {
200208 is State .Closed -> return
201- is State .Idle -> oldState
209+ is State .New -> oldState.copy(forceTokenRefresh = true )
210+ is State .Idle -> oldState.copy(forceTokenRefresh = true )
202211 is State .Active -> {
203212 val message = " needs token refresh (wgrwbrvjxt)"
204213 oldState.job.cancel(message, ForceRefresh (message))
205- oldState
214+ State . Idle ( oldState.provider, forceTokenRefresh = true )
206215 }
207216 }
208217
209- val newState = State .Idle (oldStateProviderProvider.provider, forceTokenRefresh = true )
218+ check(newState.forceTokenRefresh) {
219+ " newState.forceTokenRefresh should be true (error code gnvr2wx7nz)"
220+ }
210221 if (state.compareAndSet(oldState, newState)) {
211222 break
212223 }
213-
214- yield ()
215224 }
216225 }
217226
@@ -246,7 +255,7 @@ internal sealed class DataConnectCredentialsTokenManager<T : Any>(
246255 logger.debug { " $invocationId getToken(requestId=$requestId )" }
247256 while (true ) {
248257 val attemptSequenceNumber = nextSequenceNumber()
249- val oldState = state.get()
258+ val oldState = state.value
250259
251260 val newState: State .Active <T > =
252261 when (oldState) {
@@ -257,13 +266,13 @@ internal sealed class DataConnectCredentialsTokenManager<T : Any>(
257266 }
258267 throw CredentialsTokenManagerClosedException (this )
259268 }
260- is State .Idle -> {
261- if (oldState.provider == = null ) {
262- logger.debug {
263- " $invocationId getToken() returns null (token provider is not (yet?) available)"
264- }
265- return null
269+ is State .New -> {
270+ logger.debug {
271+ " $invocationId getToken() returns null (token provider is not (yet?) available)"
266272 }
273+ return null
274+ }
275+ is State .Idle -> {
267276 newActiveState(invocationId, oldState.provider, oldState.forceTokenRefresh)
268277 }
269278 is State .Active -> {
@@ -342,7 +351,7 @@ internal sealed class DataConnectCredentialsTokenManager<T : Any>(
342351 addTokenListener(newProvider)
343352
344353 while (true ) {
345- val oldState = state.get()
354+ val oldState = state.value
346355 val newState =
347356 when (oldState) {
348357 is State .Closed -> {
@@ -353,6 +362,7 @@ internal sealed class DataConnectCredentialsTokenManager<T : Any>(
353362 removeTokenListener(newProvider)
354363 break
355364 }
365+ is State .New -> State .Idle (newProvider, oldState.forceTokenRefresh)
356366 is State .Idle -> State .Idle (newProvider, oldState.forceTokenRefresh)
357367 is State .Active -> {
358368 val newProviderClassName = newProvider::class .qualifiedName
@@ -366,8 +376,6 @@ internal sealed class DataConnectCredentialsTokenManager<T : Any>(
366376 break
367377 }
368378 }
369-
370- _providerAvailable .value = true
371379 }
372380
373381 /* *
0 commit comments