From b6f496c78a808a8457110d3540a20bc973b8c7e9 Mon Sep 17 00:00:00 2001 From: Lee Shombert Date: Mon, 20 Jul 2020 14:28:39 -0700 Subject: [PATCH] Cache default home Bug: 162102512 Cache the default home app (package) in the slice manager. The cache is invalidated whenever the HOME role is changed. Test: Tested with a debug build that computed the cached value and then compared it to the original computation. Verify that no discrepancies are detected. * Manual test - 1. Install the 3P Nova Launcher app. 2. Choose Nova Launcher as the default home app 3. Choose Pixel Launcher as the default home app 4. Choose NOva Launcher as the default and then uninstall the app * FrameworksServicesTests:UserSystemPackageInstallerTest * UserLifecycleTests Change-Id: I8e31acced3b41262641c522842e5f553c63af102 --- .../server/slice/SliceManagerService.java | 74 +++++++++++++++++-- 1 file changed, 69 insertions(+), 5 deletions(-) diff --git a/services/core/java/com/android/server/slice/SliceManagerService.java b/services/core/java/com/android/server/slice/SliceManagerService.java index 4349ca451c366..2a74b3d238290 100644 --- a/services/core/java/com/android/server/slice/SliceManagerService.java +++ b/services/core/java/com/android/server/slice/SliceManagerService.java @@ -28,6 +28,8 @@ import static android.os.Process.SYSTEM_UID; import android.Manifest.permission; import android.annotation.NonNull; import android.app.AppOpsManager; +import android.app.role.OnRoleHoldersChangedListener; +import android.app.role.RoleManager; import android.app.slice.ISliceManager; import android.app.slice.SliceSpec; import android.app.usage.UsageStatsManagerInternal; @@ -77,6 +79,7 @@ import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Objects; +import java.util.concurrent.Executor; import java.util.function.Supplier; public class SliceManagerService extends ISliceManager.Stub { @@ -121,6 +124,7 @@ public class SliceManagerService extends ISliceManager.Stub { filter.addAction(Intent.ACTION_PACKAGE_DATA_CLEARED); filter.addAction(Intent.ACTION_PACKAGE_REMOVED); filter.addDataScheme("package"); + mRoleObserver = new RoleObserver(); mContext.registerReceiverAsUser(mReceiver, UserHandle.ALL, filter, null, mHandler); } @@ -478,10 +482,26 @@ public class SliceManagerService extends ISliceManager.Stub { return cn.getPackageName(); } + /** + * A cached value of the default home app + */ + private String mCachedDefaultHome = null; + // Based on getDefaultHome in ShortcutService. // TODO: Unify if possible @VisibleForTesting protected String getDefaultHome(int userId) { + + // Set VERIFY to true to run the cache in "shadow" mode for cache + // testing. Do not commit set to true; + final boolean VERIFY = false; + + if (mCachedDefaultHome != null) { + if (!VERIFY) { + return mCachedDefaultHome; + } + } + final long token = Binder.clearCallingIdentity(); try { final List allHomeCandidates = new ArrayList<>(); @@ -490,10 +510,12 @@ public class SliceManagerService extends ISliceManager.Stub { final ComponentName defaultLauncher = mPackageManagerInternal .getHomeActivitiesAsUser(allHomeCandidates, userId); - ComponentName detected = null; - if (defaultLauncher != null) { - detected = defaultLauncher; - } + ComponentName detected = defaultLauncher; + + // Cache the default launcher. It is not a problem if the + // launcher is null - eventually, the default launcher will be + // set to something non-null. + mCachedDefaultHome = ((detected != null) ? detected.getPackageName() : null); if (detected == null) { // If we reach here, that means it's the first check since the user was created, @@ -517,12 +539,54 @@ public class SliceManagerService extends ISliceManager.Stub { lastPriority = ri.priority; } } - return detected != null ? detected.getPackageName() : null; + final String ret = ((detected != null) ? detected.getPackageName() : null); + if (VERIFY) { + if (mCachedDefaultHome != null && !mCachedDefaultHome.equals(ret)) { + Slog.e(TAG, "getDefaultHome() cache failure, is " + + mCachedDefaultHome + " should be " + ret); + } + } + return ret; } finally { Binder.restoreCallingIdentity(token); } } + public void invalidateCachedDefaultHome() { + mCachedDefaultHome = null; + } + + /** + * Listen for changes in the roles, and invalidate the cached default + * home as necessary. + */ + private RoleObserver mRoleObserver; + + class RoleObserver implements OnRoleHoldersChangedListener { + private RoleManager mRm; + private final Executor mExecutor; + + RoleObserver() { + mExecutor = mContext.getMainExecutor(); + register(); + } + + public void register() { + mRm = mContext.getSystemService(RoleManager.class); + if (mRm != null) { + mRm.addOnRoleHoldersChangedListenerAsUser(mExecutor, this, UserHandle.ALL); + invalidateCachedDefaultHome(); + } + } + + @Override + public void onRoleHoldersChanged(@NonNull String roleName, @NonNull UserHandle user) { + if (RoleManager.ROLE_HOME.equals(roleName)) { + invalidateCachedDefaultHome(); + } + } + } + private boolean isGrantedFullAccess(String pkg, int userId) { return mPermissions.hasFullAccess(pkg, userId); }