@@ -112,9 +112,8 @@ class BrokerDiscoveryClient(private val brokerCandidates: Set<BrokerData>,
112112 *
113113 * @param brokerCandidates the candidate(s) to query from.
114114 * @param ipcStrategy the ipc mechanism to query with.
115- * @param isPackageInstalled a method which returns true if the provided [BrokerData] is installed.
116- * @param shouldStopQueryForAWhile a method which, if invoked, will force [BrokerDiscoveryClient]
117- * to skip the IPC discovery process for a while.
115+ * @param isPackageInstalled a function to determine if any given broker app is installed.
116+ * @param isValidBroker a function to determine if the installed broker app contains a matching signature hash.
118117 **/
119118 internal suspend fun queryFromBroker (
120119 brokerCandidates : Set <BrokerData >,
@@ -206,6 +205,15 @@ class BrokerDiscoveryClient(private val brokerCandidates: Set<BrokerData>,
206205 }
207206 }
208207
208+ data class BrokerInMemoryCache (
209+ val brokerData : BrokerData ?
210+ )
211+
212+ // Null = not yet queried.
213+ // Non-null = queried before. (the broker data could either be null (no broker installed) or non-null))
214+ @Volatile
215+ private var cachedBroker: BrokerInMemoryCache ? = null
216+
209217 constructor (context: Context ,
210218 components: IPlatformComponents ,
211219 cache: IClientActiveBrokerCache ) : this (
@@ -225,6 +233,7 @@ class BrokerDiscoveryClient(private val brokerCandidates: Set<BrokerData>,
225233 @kotlin.jvm.Throws (ClientException ::class )
226234 override fun forceBrokerRediscovery (brokerCandidate : BrokerData ): BrokerData {
227235 val methodTag = " $TAG :forceBrokerRediscovery"
236+
228237 return runBlocking {
229238 classLevelLock.withLock {
230239 try {
@@ -256,7 +265,7 @@ class BrokerDiscoveryClient(private val brokerCandidates: Set<BrokerData>,
256265 FORCE_TRIGGER_BROKER_DISCOVERY_RESULT_UNEXPECTED_ERROR ,
257266 " Result bundle should not be null."
258267 )
259- cache.setCachedActiveBroker (result)
268+ cacheActiveBroker (result)
260269 return @runBlocking result
261270 } catch (c: ClientException ) {
262271 Logger .error(methodTag, " forceBrokerRediscovery Failed." , c)
@@ -274,112 +283,187 @@ class BrokerDiscoveryClient(private val brokerCandidates: Set<BrokerData>,
274283 }
275284
276285 override fun getActiveBroker (shouldSkipCache : Boolean ): BrokerData ? {
277- return runBlocking {
278- return @runBlocking getActiveBrokerAsync(shouldSkipCache, null )
279- }
286+ return getActiveBrokerInternal (shouldSkipCache, null )
280287 }
281288
282289 override fun getActiveBroker (
283290 shouldSkipCache : Boolean ,
284291 telemetryCallback : IBrokerDiscoveryClientTelemetryCallback
285292 ): BrokerData ? {
286- return runBlocking {
287- return @runBlocking getActiveBrokerAsync(shouldSkipCache, telemetryCallback)
288- }
293+ return getActiveBrokerInternal (shouldSkipCache, telemetryCallback)
289294 }
290295
291- private suspend fun getActiveBrokerAsync (shouldSkipCache : Boolean ,
292- telemetryCallback : IBrokerDiscoveryClientTelemetryCallback ? ): BrokerData ? {
293- val methodTag = " $TAG :getActiveBrokerAsync"
294-
295- val timeStartAcquiringLock = System .nanoTime()
296- classLevelLock.withLock {
297- telemetryCallback?.onLockAcquired(System .nanoTime() - timeStartAcquiringLock)
298- if (! shouldSkipCache) {
299- if (cache.shouldUseAccountManager()) {
300- telemetryCallback?.onUseAccountManager()
301- return getActiveBrokerFromAccountManager()
302- }
303- val timeStartReadingFromCache = System .nanoTime()
304- cache.getCachedActiveBroker()?.let {
305- telemetryCallback?.onReadFromCache(System .nanoTime() - timeStartReadingFromCache)
306-
307- val timeStartIsPackageInstalled = System .nanoTime()
308- val isPackageInstalled = isPackageInstalled(it)
309- telemetryCallback?.onFinishCheckingIfPackageIsInstalled(System .nanoTime() - timeStartIsPackageInstalled)
310- if (! isPackageInstalled) {
311- Logger .info(
312- methodTag,
313- " There is a cached broker: $it , but the app is no longer installed."
314- )
315- cache.clearCachedActiveBroker()
316- return @let
317- }
296+ private fun getActiveBrokerInternal (shouldSkipCache : Boolean ,
297+ telemetryCallback : IBrokerDiscoveryClientTelemetryCallback ? ): BrokerData ? {
298+ if (! shouldSkipCache) {
299+ getActiveBrokerFromInMemoryCache(telemetryCallback)?.let {
300+ return it
301+ }
302+ }
318303
319- val timeStartIsValidBroker = System .nanoTime()
320- val isValidBroker = isValidBroker(it)
321- telemetryCallback?.onFinishCheckingIfValidBroker(System .nanoTime() - timeStartIsValidBroker)
322- if (! isValidBroker) {
323- Logger .info(
324- methodTag,
325- " Clearing cache as the installed app does not have a matching signature hash."
326- )
327- cache.clearCachedActiveBroker()
328- return @let
304+ val totalTimeSpentAcquiringLock = System .nanoTime()
305+ return runBlocking {
306+ classLevelLock.withLock {
307+ telemetryCallback?.onLockAcquired(System .nanoTime() - totalTimeSpentAcquiringLock)
308+
309+ if (! shouldSkipCache) {
310+ // Early return if another coroutine has already populated the in-memory cache.
311+ getActiveBrokerFromInMemoryCache(telemetryCallback)?.let {
312+ return @runBlocking it
329313 }
330314
331- val timeStartIsSupportedByTargetedBroker = System .nanoTime()
332- val isSupportedByTargetedBroker =
333- ipcStrategy.isSupportedByTargetedBroker(it.packageName)
334- telemetryCallback?.onFinishCheckingIfSupportedByTargetedBroker(System .nanoTime() - timeStartIsSupportedByTargetedBroker)
335- if (! isSupportedByTargetedBroker){
336- Logger .info(
337- methodTag,
338- " Clearing cache as the installed app does not provide any IPC mechanism to communicate to. (e.g. the broker code isn't shipped with this apk)"
339- )
340- cache.clearCachedActiveBroker()
341- return @let
315+ getActiveBrokerFromStorageCache(telemetryCallback)?.let {
316+ return @runBlocking it
342317 }
318+ }
343319
344- Logger .info(methodTag, " Returning cached broker: $it " )
345- return it
320+ getActiveBrokerFromBrokerApp(telemetryCallback)?. let {
321+ return @runBlocking it
346322 }
323+
324+ return @runBlocking getActiveBrokerFromAccountManager()
347325 }
326+ }
327+ }
348328
349- val timeStartQueryFromBroker = System .nanoTime()
350- val brokerData = queryFromBroker(
351- brokerCandidates = brokerCandidates,
352- ipcStrategy = ipcStrategy,
353- isPackageInstalled = isPackageInstalled,
354- isValidBroker = isValidBroker
355- )
356- telemetryCallback?.onFinishQueryingResultFromBroker(System .nanoTime() - timeStartQueryFromBroker)
329+ private fun getActiveBrokerFromInMemoryCache (
330+ telemetryCallback : IBrokerDiscoveryClientTelemetryCallback ?
331+ ): BrokerData ? {
332+ val methodTag = " $TAG :getValidBrokerFromInMemoryCache"
333+
334+ // There's no readWriteLock in coroutines library yet... volatile should be sufficient for our use case here.
335+ cachedBroker?.let {
336+ if (it.brokerData == null ){
337+ // "null" value is cached, meaning no valid broker installed.
338+ Logger .info(
339+ methodTag,
340+ " [Cached] No broker app is installed."
341+ )
342+ return null
343+ }
357344
358- if (brokerData != null ) {
359- cache.setCachedActiveBroker(brokerData)
360- return brokerData
345+ val timeStartIsValidBroker = System .nanoTime()
346+ val isValidBroker = isValidBroker(it.brokerData)
347+ telemetryCallback?.onFinishCheckingIfValidBroker(System .nanoTime() - timeStartIsValidBroker)
348+ if (isValidBroker) {
349+ return it.brokerData
361350 }
362351
363352 Logger .info(
364353 methodTag,
365- " Will skip broker discovery via IPC and fall back to AccountManager " +
366- " for the next 60 minutes."
354+ " Clearing cache as the installed app does not have a matching signature hash."
367355 )
368- cache.clearCachedActiveBroker()
369- cache.setShouldUseAccountManagerForTheNextMilliseconds(
370- TimeUnit .MINUTES .toMillis(
371- 60
356+ clearCachedData()
357+ }
358+
359+ // Nothing valid to return.
360+ return null
361+ }
362+
363+ private fun clearCachedData () {
364+ cachedBroker = null
365+ cache.clearCachedActiveBroker()
366+ }
367+
368+ private fun cacheActiveBroker (brokerData : BrokerData ) {
369+ cachedBroker = BrokerInMemoryCache (brokerData)
370+ cache.setCachedActiveBroker(brokerData)
371+ }
372+
373+ private fun getActiveBrokerFromStorageCache (
374+ telemetryCallback : IBrokerDiscoveryClientTelemetryCallback ?
375+ ): BrokerData ? {
376+ val methodTag = " $TAG :getValidBrokerFromInMemoryCache"
377+ val timeStartReadingFromCache = System .nanoTime()
378+ cache.getCachedActiveBroker()?.let {
379+ telemetryCallback?.onReadFromCache(System .nanoTime() - timeStartReadingFromCache)
380+ if (cache.shouldUseAccountManager()) {
381+ telemetryCallback?.onUseAccountManager()
382+ val accountManagerBroker = getActiveBrokerFromAccountManager()
383+
384+ // Only cache in memory, but don't persist accountManager result to cache storage.
385+ cachedBroker = BrokerInMemoryCache (accountManagerBroker)
386+ return accountManagerBroker
387+ }
388+
389+ val timeStartIsPackageInstalled = System .nanoTime()
390+ val isPackageInstalled = isPackageInstalled(it)
391+ telemetryCallback?.onFinishCheckingIfPackageIsInstalled(System .nanoTime() - timeStartIsPackageInstalled)
392+ if (! isPackageInstalled) {
393+ Logger .info(
394+ methodTag,
395+ " There is a cached broker: $it , but the app is no longer installed."
372396 )
373- )
397+ clearCachedData()
398+ return @let
399+ }
374400
375- telemetryCallback?.onUseAccountManager()
376- val accountManagerResult = getActiveBrokerFromAccountManager()
377- Logger .info(
378- methodTag, " Tried getting active broker from account manager, " +
379- " get ${accountManagerResult?.packageName} ."
380- )
401+ val timeStartIsValidBroker = System .nanoTime()
402+ val isValidBroker = isValidBroker(it)
403+ telemetryCallback?.onFinishCheckingIfValidBroker(System .nanoTime() - timeStartIsValidBroker)
404+ if (! isValidBroker) {
405+ Logger .info(
406+ methodTag,
407+ " Clearing cache as the installed app does not have a matching signature hash."
408+ )
409+ clearCachedData()
410+ return @let
411+ }
381412
382- return accountManagerResult
413+ val timeStartIsSupportedByTargetedBroker = System .nanoTime()
414+ val isSupportedByTargetedBroker =
415+ ipcStrategy.isSupportedByTargetedBroker(it.packageName)
416+ telemetryCallback?.onFinishCheckingIfSupportedByTargetedBroker(System .nanoTime() - timeStartIsSupportedByTargetedBroker)
417+ if (! isSupportedByTargetedBroker){
418+ Logger .info(
419+ methodTag,
420+ " Clearing cache as the installed app does not provide any IPC mechanism to communicate to. (e.g. the broker code isn't shipped with this apk)"
421+ )
422+ clearCachedData()
423+ return @let
424+ }
425+
426+ Logger .info(methodTag, " Returning cached broker: $it " )
427+ cacheActiveBroker(it)
428+ return it
383429 }
430+
431+ return null
432+ }
433+
434+ private suspend fun getActiveBrokerFromBrokerApp (
435+ telemetryCallback : IBrokerDiscoveryClientTelemetryCallback ?
436+ ): BrokerData ? {
437+ val methodTag = " $TAG :getActiveBrokerFromBrokerApp"
438+ val timeStartQueryFromBroker = System .nanoTime()
439+ val brokerData = queryFromBroker(
440+ brokerCandidates = brokerCandidates,
441+ ipcStrategy = ipcStrategy,
442+ isPackageInstalled = isPackageInstalled,
443+ isValidBroker = isValidBroker
444+ )
445+ telemetryCallback?.onFinishQueryingResultFromBroker(System .nanoTime() - timeStartQueryFromBroker)
446+
447+ if (brokerData != null ) {
448+ cacheActiveBroker(brokerData)
449+ return brokerData
450+ }
451+
452+ cache.setShouldUseAccountManagerForTheNextMilliseconds(
453+ TimeUnit .MINUTES .toMillis(
454+ 60
455+ )
456+ )
457+
458+ telemetryCallback?.onUseAccountManager()
459+ val accountManagerResult = getActiveBrokerFromAccountManager()
460+ Logger .info(
461+ methodTag, " Tried getting active broker from account manager, " +
462+ " get ${accountManagerResult?.packageName} ."
463+ )
464+
465+ // Only cache in memory, but don't persist accountManager result to cache storage.
466+ cachedBroker = BrokerInMemoryCache (accountManagerResult)
467+ return accountManagerResult
384468 }
385469}
0 commit comments