@@ -30,7 +30,6 @@ import com.google.firebase.inject.Provider
30
30
import com.google.firebase.internal.api.FirebaseNoSignedInUserException
31
31
import com.google.firebase.util.nextAlphanumericString
32
32
import java.lang.ref.WeakReference
33
- import java.util.concurrent.atomic.AtomicReference
34
33
import kotlin.coroutines.coroutineContext
35
34
import kotlin.random.Random
36
35
import kotlinx.coroutines.CancellationException
@@ -46,10 +45,9 @@ import kotlinx.coroutines.async
46
45
import kotlinx.coroutines.cancel
47
46
import kotlinx.coroutines.ensureActive
48
47
import 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
51
50
import kotlinx.coroutines.launch
52
- import kotlinx.coroutines.yield
53
51
54
52
/* * Base class that shares logic for managing the Auth token and AppCheck token. */
55
53
internal sealed class DataConnectCredentialsTokenManager <T : Any >(
@@ -61,9 +59,6 @@ internal sealed class DataConnectCredentialsTokenManager<T : Any>(
61
59
val instanceId: String
62
60
get() = logger.nameWithId
63
61
64
- private val _providerAvailable = MutableStateFlow (false )
65
- val providerAvailable: StateFlow <Boolean > = _providerAvailable .asStateFlow()
66
-
67
62
@Suppress(" LeakingThis" ) private val weakThis = WeakReference (this )
68
63
69
64
private val coroutineScope =
@@ -87,49 +82,39 @@ internal sealed class DataConnectCredentialsTokenManager<T : Any>(
87
82
}
88
83
}
89
84
90
- private interface ProviderProvider <T > {
91
- val provider: T ?
92
- }
93
-
94
85
private sealed interface State <out T > {
95
86
96
87
/* * State indicating that [close] has been invoked. */
97
88
object Closed : State<Nothing>
98
89
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 > {
108
91
/* * The value to specify for `forceRefresh` on the next invocation of [getToken]. */
109
92
val forceTokenRefresh: Boolean
110
- ) : State<T>, ProviderProvider<T>
93
+ }
111
94
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>
114
97
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 >(
119
109
override val provider : T ,
120
110
121
111
/* * The job that is performing the "get token" request. */
122
112
val job : Deferred <SequencedReference <Result <GetTokenResult >>>
123
- ) : State<T>, ProviderProvider <T>
113
+ ) : StateWithProvider <T>
124
114
}
125
115
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 ))
133
118
134
119
/* *
135
120
* Adds the token listener to the given provider.
@@ -168,19 +153,42 @@ internal sealed class DataConnectCredentialsTokenManager<T : Any>(
168
153
setClosedState()
169
154
}
170
155
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
+
171
178
// This function must ONLY be called from close().
172
179
private fun setClosedState () {
173
180
while (true ) {
174
- val oldState = state.get()
175
- val providerProvider : ProviderProvider < T > =
181
+ val oldState = state.value
182
+ val provider : T ? =
176
183
when (oldState) {
177
184
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
180
188
}
181
189
182
190
if (state.compareAndSet(oldState, State .Closed )) {
183
- providerProvider. provider?.let { removeTokenListener(it) }
191
+ provider?.let { removeTokenListener(it) }
184
192
break
185
193
}
186
194
}
@@ -191,27 +199,28 @@ internal sealed class DataConnectCredentialsTokenManager<T : Any>(
191
199
*
192
200
* If [close] has been called, this method does nothing.
193
201
*/
194
- suspend fun forceRefresh () {
202
+ fun forceRefresh () {
195
203
logger.debug { " forceRefresh()" }
196
204
while (true ) {
197
- val oldState = state.get()
198
- val oldStateProviderProvider =
205
+ val oldState = state.value
206
+ val newState : State . StateWithForceTokenRefresh < T > =
199
207
when (oldState) {
200
208
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 )
202
211
is State .Active -> {
203
212
val message = " needs token refresh (wgrwbrvjxt)"
204
213
oldState.job.cancel(message, ForceRefresh (message))
205
- oldState
214
+ State . Idle ( oldState.provider, forceTokenRefresh = true )
206
215
}
207
216
}
208
217
209
- val newState = State .Idle (oldStateProviderProvider.provider, forceTokenRefresh = true )
218
+ check(newState.forceTokenRefresh) {
219
+ " newState.forceTokenRefresh should be true (error code gnvr2wx7nz)"
220
+ }
210
221
if (state.compareAndSet(oldState, newState)) {
211
222
break
212
223
}
213
-
214
- yield ()
215
224
}
216
225
}
217
226
@@ -246,7 +255,7 @@ internal sealed class DataConnectCredentialsTokenManager<T : Any>(
246
255
logger.debug { " $invocationId getToken(requestId=$requestId )" }
247
256
while (true ) {
248
257
val attemptSequenceNumber = nextSequenceNumber()
249
- val oldState = state.get()
258
+ val oldState = state.value
250
259
251
260
val newState: State .Active <T > =
252
261
when (oldState) {
@@ -257,13 +266,13 @@ internal sealed class DataConnectCredentialsTokenManager<T : Any>(
257
266
}
258
267
throw CredentialsTokenManagerClosedException (this )
259
268
}
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)"
266
272
}
273
+ return null
274
+ }
275
+ is State .Idle -> {
267
276
newActiveState(invocationId, oldState.provider, oldState.forceTokenRefresh)
268
277
}
269
278
is State .Active -> {
@@ -342,7 +351,7 @@ internal sealed class DataConnectCredentialsTokenManager<T : Any>(
342
351
addTokenListener(newProvider)
343
352
344
353
while (true ) {
345
- val oldState = state.get()
354
+ val oldState = state.value
346
355
val newState =
347
356
when (oldState) {
348
357
is State .Closed -> {
@@ -353,6 +362,7 @@ internal sealed class DataConnectCredentialsTokenManager<T : Any>(
353
362
removeTokenListener(newProvider)
354
363
break
355
364
}
365
+ is State .New -> State .Idle (newProvider, oldState.forceTokenRefresh)
356
366
is State .Idle -> State .Idle (newProvider, oldState.forceTokenRefresh)
357
367
is State .Active -> {
358
368
val newProviderClassName = newProvider::class .qualifiedName
@@ -366,8 +376,6 @@ internal sealed class DataConnectCredentialsTokenManager<T : Any>(
366
376
break
367
377
}
368
378
}
369
-
370
- _providerAvailable .value = true
371
379
}
372
380
373
381
/* *
0 commit comments