From 2f6086166956949bc5ab732795f671b1800cb613 Mon Sep 17 00:00:00 2001 From: Andrey Yepin Date: Tue, 6 Aug 2024 13:46:56 -0700 Subject: Add timeout for AppPredictor response. Timeout AppPredictor callbacks and fallback to ShortcutManager. Bug: 295956687 Bug: 343300158 Test: atest IntentResolver-tests-unit Flag: com.android.intentresolver.fix_shortcuts_flashing Change-Id: Ia6e5643451a840e10213f242c6c79364b0193e78 --- .../intentresolver/shortcuts/ShortcutLoader.kt | 28 ++++++++++++++++++++++ 1 file changed, 28 insertions(+) (limited to 'java/src') diff --git a/java/src/com/android/intentresolver/shortcuts/ShortcutLoader.kt b/java/src/com/android/intentresolver/shortcuts/ShortcutLoader.kt index 68412256..1fedee30 100644 --- a/java/src/com/android/intentresolver/shortcuts/ShortcutLoader.kt +++ b/java/src/com/android/intentresolver/shortcuts/ShortcutLoader.kt @@ -36,18 +36,22 @@ import androidx.annotation.OpenForTesting import androidx.annotation.VisibleForTesting import androidx.annotation.WorkerThread import com.android.intentresolver.Flags.fixShortcutLoaderJobLeak +import com.android.intentresolver.Flags.fixShortcutsFlashing import com.android.intentresolver.chooser.DisplayResolveInfo import com.android.intentresolver.measurements.Tracer import com.android.intentresolver.measurements.runTracing import java.util.concurrent.Executor import java.util.function.Consumer +import kotlinx.atomicfu.atomic import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.CoroutineStart import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.asExecutor import kotlinx.coroutines.cancel import kotlinx.coroutines.channels.BufferOverflow +import kotlinx.coroutines.delay import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.filter @@ -80,6 +84,7 @@ constructor( if (fixShortcutLoaderJobLeak()) parentScope.createChildScope() else parentScope private val shortcutToChooserTargetConverter = ShortcutToChooserTargetConverter() private val userManager = context.getSystemService(Context.USER_SERVICE) as UserManager + private val appPredictorWatchdog = atomic(null) private val appPredictorCallback = ScopedAppTargetListCallback(scope) { onAppPredictorCallback(it) }.toAppPredictorCallback() @@ -186,8 +191,29 @@ constructor( if (!skipAppPredictionService && appPredictor != null) { try { Log.d(TAG, "[$id] query AppPredictor for user $userHandle") + + val watchdogJob = + if (fixShortcutsFlashing()) { + scope + .launch(start = CoroutineStart.LAZY) { + delay(APP_PREDICTOR_RESPONSE_TIMEOUT_MS) + Log.w(TAG, "AppPredictor response timeout for user: $userHandle") + appPredictorCallback.onTargetsAvailable(emptyList()) + } + .also { job -> + appPredictorWatchdog.getAndSet(job)?.cancel() + job.invokeOnCompletion { + appPredictorWatchdog.compareAndSet(job, null) + } + } + } else { + null + } + Tracer.beginAppPredictorQueryTrace(userHandle) appPredictor.requestPredictionUpdate() + + watchdogJob?.start() return } catch (e: Throwable) { endAppPredictorQueryTrace(userHandle) @@ -230,6 +256,7 @@ constructor( @WorkerThread private fun onAppPredictorCallback(appPredictorTargets: List) { + appPredictorWatchdog.value?.cancel() endAppPredictorQueryTrace(userHandle) Log.d(TAG, "[$id] receive app targets from AppPredictor") if (appPredictorTargets.isEmpty() && shouldQueryDirectShareTargets()) { @@ -378,6 +405,7 @@ constructor( } companion object { + @VisibleForTesting const val APP_PREDICTOR_RESPONSE_TIMEOUT_MS = 2_000L private const val TAG = "ShortcutLoader" private fun PackageManager.isPackageEnabled(packageName: String): Boolean { -- cgit v1.2.3-59-g8ed1b