Skip to content

Commit 59bad0a

Browse files
authored
Cache Active Broker In Memory (BrokerDiscoveryClient), Fixes AB#3429928 (#2817)
1 parent 2895057 commit 59bad0a

File tree

6 files changed

+527
-197
lines changed

6 files changed

+527
-197
lines changed

changelog.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
vNext
22
----------
3+
- [MINOR] Cache Active Broker In Memory (BrokerDiscoveryClient) (#2817)
34
- [MINOR] Enable Broker Discovery by default in MSAL/Broker API (#2818)
45
- [MINOR] Share SharedPreferencesInMemoryCache across instances of BrokerOAuth2TokenCache
56
- [MINOR] Use SharedPreferencesInMemoryCache implementation in Broker (#2802)

common/src/main/java/com/microsoft/identity/common/internal/activebrokerdiscovery/BrokerDiscoveryClient.kt

Lines changed: 135 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ package com.microsoft.identity.common.internal.activebrokerdiscovery
2424

2525
import android.content.Context
2626
import android.os.Bundle
27+
import androidx.annotation.VisibleForTesting
2728
import com.microsoft.identity.common.exception.BrokerCommunicationException
2829
import com.microsoft.identity.common.internal.broker.BrokerData
2930
import com.microsoft.identity.common.internal.broker.BrokerValidator
@@ -206,6 +207,21 @@ class BrokerDiscoveryClient(private val brokerCandidates: Set<BrokerData>,
206207
}
207208
}
208209

210+
data class CachedBrokerData (
211+
val brokerData: BrokerData?
212+
)
213+
214+
/**
215+
* In-memory cache for the active broker data.
216+
* There are 3 possible states:
217+
* 1. null: the cache hasn't been initialized (needs to read from storage or query from broker first).
218+
* 2. CachedBrokerData with null brokerData: no active broker found.
219+
* 3. CachedBrokerData with non-null brokerData: found an active broker.
220+
**/
221+
@Volatile
222+
@VisibleForTesting
223+
var cachedData: CachedBrokerData? = null
224+
209225
constructor(context: Context,
210226
components: IPlatformComponents,
211227
cache: IClientActiveBrokerCache) : this(
@@ -273,9 +289,59 @@ class BrokerDiscoveryClient(private val brokerCandidates: Set<BrokerData>,
273289
}
274290
}
275291

292+
override fun getActiveBrokerWithInMemoryCache(telemetryCallback: IBrokerDiscoveryClientTelemetryCallback?): BrokerData?{
293+
cachedData?.let {
294+
if (it.brokerData == null) {
295+
return null
296+
}
297+
if (validateInMemoryCacheValue(it.brokerData, telemetryCallback)){
298+
return it.brokerData
299+
}
300+
301+
// Even if the value is not valid, we don't modify (invalidate) the class variable here since we're not in the lock.
302+
// The variable will be updated in the block below.
303+
}
304+
305+
return runBlocking {
306+
val timeStartAcquiringLock = System.nanoTime()
307+
classLevelLock.withLock {
308+
telemetryCallback?.onLockAcquired(System.nanoTime() - timeStartAcquiringLock)
309+
310+
// just in case the value is already populated while waiting for the lock.
311+
cachedData?.let {
312+
if (it.brokerData == null) {
313+
return@runBlocking null
314+
}
315+
if (validateInMemoryCacheValue(it.brokerData, telemetryCallback)){
316+
return@runBlocking it.brokerData
317+
}
318+
}
319+
320+
val brokerData = getActiveBrokerAsync(shouldSkipCache = false, telemetryCallback)
321+
cachedData = CachedBrokerData(brokerData)
322+
return@runBlocking brokerData
323+
}
324+
}
325+
}
326+
327+
/**
328+
* Make sure the [BrokerData] that stays in the memory cache (if any) is valid.
329+
**/
330+
private fun validateInMemoryCacheValue(
331+
data: BrokerData,
332+
telemetryCallback: IBrokerDiscoveryClientTelemetryCallback?,
333+
): Boolean {
334+
val timeStartIsValidBroker = System.nanoTime()
335+
val isValidBroker = isValidBroker(data)
336+
telemetryCallback?.onFinishCheckingIfValidBroker(System.nanoTime() - timeStartIsValidBroker)
337+
return isValidBroker
338+
}
339+
276340
override fun getActiveBroker(shouldSkipCache: Boolean): BrokerData? {
277341
return runBlocking {
278-
return@runBlocking getActiveBrokerAsync(shouldSkipCache, null)
342+
classLevelLock.withLock {
343+
return@runBlocking getActiveBrokerAsync(shouldSkipCache, null)
344+
}
279345
}
280346
}
281347

@@ -284,102 +350,88 @@ class BrokerDiscoveryClient(private val brokerCandidates: Set<BrokerData>,
284350
telemetryCallback: IBrokerDiscoveryClientTelemetryCallback
285351
): BrokerData? {
286352
return runBlocking {
287-
return@runBlocking getActiveBrokerAsync(shouldSkipCache, telemetryCallback)
353+
val timeStartAcquiringLock = System.nanoTime()
354+
classLevelLock.withLock {
355+
telemetryCallback.onLockAcquired(System.nanoTime() - timeStartAcquiringLock)
356+
return@runBlocking getActiveBrokerAsync(shouldSkipCache, telemetryCallback)
357+
}
288358
}
289359
}
290360

291361
private suspend fun getActiveBrokerAsync(shouldSkipCache:Boolean,
292362
telemetryCallback: IBrokerDiscoveryClientTelemetryCallback?): BrokerData?{
293363
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()
364+
if (!shouldSkipCache) {
365+
if (cache.shouldUseAccountManager()) {
366+
telemetryCallback?.onUseAccountManager()
367+
return getActiveBrokerFromAccountManager()
368+
}
369+
val timeStartReadingFromCache = System.nanoTime()
370+
cache.getCachedActiveBroker()?.let {
371+
telemetryCallback?.onReadFromCache(System.nanoTime() - timeStartReadingFromCache)
372+
373+
val timeStartIsPackageInstalled = System.nanoTime()
374+
val isPackageInstalled = isPackageInstalled(it)
375+
telemetryCallback?.onFinishCheckingIfPackageIsInstalled(System.nanoTime() - timeStartIsPackageInstalled)
376+
if (!isPackageInstalled) {
377+
Logger.info(
378+
methodTag,
379+
"There is a cached broker: $it, but the app is no longer installed."
380+
)
381+
cache.clearCachedActiveBroker()
382+
return@let
302383
}
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-
}
318384

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
329-
}
330-
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
342-
}
343-
344-
Logger.info(methodTag, "Returning cached broker: $it")
345-
return it
385+
val timeStartIsValidBroker = System.nanoTime()
386+
val isValidBroker = isValidBroker(it)
387+
telemetryCallback?.onFinishCheckingIfValidBroker(System.nanoTime() - timeStartIsValidBroker)
388+
if (!isValidBroker) {
389+
Logger.info(
390+
methodTag,
391+
"Clearing cache as the installed app does not have a matching signature hash."
392+
)
393+
cache.clearCachedActiveBroker()
394+
return@let
346395
}
347-
}
348396

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)
357-
358-
if (brokerData != null) {
359-
cache.setCachedActiveBroker(brokerData)
360-
return brokerData
397+
Logger.info(methodTag, "Returning cached broker: $it")
398+
return it
361399
}
400+
}
362401

363-
Logger.info(
364-
methodTag,
365-
"Will skip broker discovery via IPC and fall back to AccountManager " +
366-
"for the next 60 minutes."
367-
)
368-
cache.clearCachedActiveBroker()
369-
cache.setShouldUseAccountManagerForTheNextMilliseconds(
370-
TimeUnit.MINUTES.toMillis(
371-
60
372-
)
373-
)
402+
val timeStartQueryFromBroker = System.nanoTime()
403+
val brokerData = queryFromBroker(
404+
brokerCandidates = brokerCandidates,
405+
ipcStrategy = ipcStrategy,
406+
isPackageInstalled = isPackageInstalled,
407+
isValidBroker = isValidBroker
408+
)
409+
telemetryCallback?.onFinishQueryingResultFromBroker(System.nanoTime() - timeStartQueryFromBroker)
410+
411+
if (brokerData != null) {
412+
cache.setCachedActiveBroker(brokerData)
413+
return brokerData
414+
}
374415

375-
telemetryCallback?.onUseAccountManager()
376-
val accountManagerResult = getActiveBrokerFromAccountManager()
377-
Logger.info(
378-
methodTag, "Tried getting active broker from account manager, " +
379-
"get ${accountManagerResult?.packageName}."
416+
Logger.info(
417+
methodTag,
418+
"Will skip broker discovery via IPC and fall back to AccountManager " +
419+
"for the next 60 minutes."
420+
)
421+
cache.clearCachedActiveBroker()
422+
cache.setShouldUseAccountManagerForTheNextMilliseconds(
423+
TimeUnit.MINUTES.toMillis(
424+
60
380425
)
426+
)
381427

382-
return accountManagerResult
383-
}
428+
telemetryCallback?.onUseAccountManager()
429+
val accountManagerResult = getActiveBrokerFromAccountManager()
430+
Logger.info(
431+
methodTag, "Tried getting active broker from account manager, " +
432+
"get ${accountManagerResult?.packageName}."
433+
)
434+
435+
return accountManagerResult
384436
}
385437
}

common/src/main/java/com/microsoft/identity/common/internal/activebrokerdiscovery/IBrokerDiscoveryClient.kt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,4 +65,15 @@ interface IBrokerDiscoveryClient {
6565
* **/
6666
@kotlin.jvm.Throws(ClientException::class)
6767
fun forceBrokerRediscovery(brokerCandidate: BrokerData): BrokerData
68+
69+
/**
70+
* This method will return the cached [BrokerData] in the memory (if any).
71+
* If the cached value is invalid, it will then proceed with the regular [getActiveBroker] flow.
72+
*
73+
* The cached value in memory will remain the same during the app process lifetime.
74+
*
75+
* @param telemetryCallback callback with telemetry data.
76+
* @return BrokerData package name and signature hash of the targeted app.
77+
* */
78+
fun getActiveBrokerWithInMemoryCache(telemetryCallback: IBrokerDiscoveryClientTelemetryCallback?): BrokerData?
6879
}

common/src/main/java/com/microsoft/identity/common/internal/activebrokerdiscovery/LegacyBrokerDiscoveryClient.kt

Lines changed: 0 additions & 50 deletions
This file was deleted.

0 commit comments

Comments
 (0)