From e8b42e069661ebf8f143edda6d5c82ab39fc67f0 Mon Sep 17 00:00:00 2001 From: Mark Renouf Date: Fri, 15 Mar 2024 15:14:32 -0400 Subject: Remove caching from UserScopedService Caching the intermediate values isn't necessary and could present an opportunity for a memory leak. Retrieved service instances should be stored for as long as they are needed, and once out of scope, be available for collection. This aligns with the copy now in SystemUI: ag/26600464 Flag: None; no usages yet Test: NA; this class is stateless Bug: 327613051 Change-Id: I2b1225cad3d17baf1a7c45e6b5e8e9ca0a79df03 --- .../intentresolver/inject/SystemServices.kt | 27 ++++++-- .../v2/data/repository/UserScopedService.kt | 79 +++++++--------------- 2 files changed, 49 insertions(+), 57 deletions(-) (limited to 'java/src') diff --git a/java/src/com/android/intentresolver/inject/SystemServices.kt b/java/src/com/android/intentresolver/inject/SystemServices.kt index 9e7c67b6..c09598e0 100644 --- a/java/src/com/android/intentresolver/inject/SystemServices.kt +++ b/java/src/com/android/intentresolver/inject/SystemServices.kt @@ -17,6 +17,7 @@ package com.android.intentresolver.inject import android.app.ActivityManager import android.app.admin.DevicePolicyManager +import android.app.prediction.AppPredictionManager import android.content.ClipboardManager import android.content.ContentInterface import android.content.ContentResolver @@ -26,7 +27,6 @@ import android.content.pm.ShortcutManager import android.os.UserManager import android.view.WindowManager import androidx.core.content.getSystemService -import com.android.intentresolver.v2.data.repository.UserScopedContext import com.android.intentresolver.v2.data.repository.UserScopedService import com.android.intentresolver.v2.data.repository.UserScopedServiceImpl import dagger.Binds @@ -89,12 +89,31 @@ class PackageManagerModule { fun packageManager(@ApplicationContext ctx: Context) = requireNotNull(ctx.packageManager) } +@Module +@InstallIn(SingletonComponent::class) +class PredictionManagerModule { + @Provides + fun scopedPredictionManager( + @ApplicationContext ctx: Context, + ): UserScopedService { + return UserScopedServiceImpl(ctx, AppPredictionManager::class) + } +} + @Module @InstallIn(SingletonComponent::class) class ShortcutManagerModule { @Provides - fun shortcutManager(@ApplicationContext ctx: Context): ShortcutManager = - ctx.requireSystemService() + fun shortcutManager(@ApplicationContext ctx: Context): ShortcutManager { + return ctx.requireSystemService() + } + + @Provides + fun scopedShortcutManager( + @ApplicationContext ctx: Context, + ): UserScopedService { + return UserScopedServiceImpl(ctx, ShortcutManager::class) + } } @Module @@ -104,7 +123,7 @@ class UserManagerModule { fun userManager(@ApplicationContext ctx: Context): UserManager = ctx.requireSystemService() @Provides - fun scopedUserManager(ctx: UserScopedContext): UserScopedService { + fun scopedUserManager(@ApplicationContext ctx: Context): UserScopedService { return UserScopedServiceImpl(ctx, UserManager::class) } } diff --git a/java/src/com/android/intentresolver/v2/data/repository/UserScopedService.kt b/java/src/com/android/intentresolver/v2/data/repository/UserScopedService.kt index 507979a0..65a48a55 100644 --- a/java/src/com/android/intentresolver/v2/data/repository/UserScopedService.kt +++ b/java/src/com/android/intentresolver/v2/data/repository/UserScopedService.kt @@ -18,77 +18,50 @@ package com.android.intentresolver.v2.data.repository import android.content.Context import android.os.UserHandle -import android.util.LruCache import androidx.core.content.getSystemService -import javax.inject.Inject +import dagger.hilt.android.qualifiers.ApplicationContext import kotlin.reflect.KClass /** - * Provides cached instances of a [system service][Context.getSystemService] created with + * Provides instances of a [system service][Context.getSystemService] created with * [the context of a specified user][Context.createContextAsUser]. * - * System services which have only `@UserHandleAware` APIs operate on the user id available from + * Some services which have only `@UserHandleAware` APIs operate on the user id available from * [Context.getUser], the context used to retrieve the service. This utility helps adapt a per-user * API model to work in multi-user manner. * * Example usage: * ``` - * val usageStats = userScopedService(context) + * @Provides + * fun scopedUserManager(@ApplicationContext ctx: Context): UserScopedService { + * return UserScopedServiceImpl(ctx, UserManager::class) + * } * - * fun getStatsForUser( - * user: User, - * from: Long, - * to: Long - * ): UsageStats { - * return usageStats.forUser(user) - * .queryUsageStats(INTERVAL_BEST, from, to) - * } + * class MyUserHelper @Inject constructor( + * private val userMgr: UserScopedService, + * ) { + * fun isPrivateProfile(user: UserHandle): UserManager { + * return userMgr.forUser(user).isPrivateProfile() + * } + * } * ``` */ -interface UserScopedService { +fun interface UserScopedService { + /** Create a service instance for the given user. */ fun forUser(user: UserHandle): T } -/** - * Provides cached Context instances each distinct per-User. - * - * @see [UserScopedService] - */ -class UserScopedContext @Inject constructor(private val applicationContext: Context) { - private val contextCacheSizeLimit = 8 - - private val instances = - object : LruCache(contextCacheSizeLimit) { - override fun create(key: UserHandle): Context { - return applicationContext.createContextAsUser(key, 0) - } - } - - fun forUser(user: UserHandle): Context { - synchronized(this) { - return if (applicationContext.user == user) { - applicationContext +class UserScopedServiceImpl( + @ApplicationContext private val context: Context, + private val serviceType: KClass, +) : UserScopedService { + override fun forUser(user: UserHandle): T { + val context = + if (context.user == user) { + context } else { - return instances[user] + context.createContextAsUser(user, 0) } - } - } -} - -/** Returns a cache of service instances, distinct by user */ -class UserScopedServiceImpl(contexts: UserScopedContext, serviceType: KClass) : - UserScopedService { - private val instances = - object : LruCache(8) { - override fun create(key: UserHandle): T { - val context = contexts.forUser(key) - return requireNotNull(context.getSystemService(serviceType.java)) - } - } - - override fun forUser(user: UserHandle): T { - synchronized(this) { - return instances[user] - } + return requireNotNull(context.getSystemService(serviceType.java)) } } -- cgit v1.2.3-59-g8ed1b