Loading core/java/android/app/usage/UsageEvents.java +1 −1 Original line number Original line Diff line number Diff line Loading @@ -199,7 +199,7 @@ public final class UsageEvents implements Parcelable { public static final int NOTIFICATION_INTERRUPTION = 12; public static final int NOTIFICATION_INTERRUPTION = 12; /** /** * A Slice was pinned by the default assistant. * A Slice was pinned by the default launcher or the default assistant. * @hide * @hide */ */ @SystemApi @SystemApi Loading services/core/java/com/android/server/slice/SliceManagerService.java +171 −8 Original line number Original line Diff line number Diff line Loading @@ -28,6 +28,8 @@ import static android.os.Process.SYSTEM_UID; import android.Manifest.permission; import android.Manifest.permission; import android.annotation.NonNull; import android.annotation.NonNull; import android.app.AppOpsManager; import android.app.AppOpsManager; import android.app.role.OnRoleHoldersChangedListener; import android.app.role.RoleManager; import android.app.slice.ISliceManager; import android.app.slice.ISliceManager; import android.app.slice.SliceSpec; import android.app.slice.SliceSpec; import android.app.usage.UsageStatsManagerInternal; import android.app.usage.UsageStatsManagerInternal; Loading @@ -39,7 +41,9 @@ import android.content.Context; import android.content.Intent; import android.content.Intent; import android.content.IntentFilter; import android.content.IntentFilter; import android.content.pm.PackageManager; import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal; import android.content.pm.ProviderInfo; import android.content.pm.ProviderInfo; import android.content.pm.ResolveInfo; import android.net.Uri; import android.net.Uri; import android.os.Binder; import android.os.Binder; import android.os.Handler; import android.os.Handler; Loading @@ -61,6 +65,7 @@ import com.android.internal.app.AssistUtils; import com.android.server.LocalServices; import com.android.server.LocalServices; import com.android.server.ServiceThread; import com.android.server.ServiceThread; import com.android.server.SystemService; import com.android.server.SystemService; import com.android.server.SystemService.TargetUser; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlPullParserException; Loading @@ -72,7 +77,10 @@ import java.io.ByteArrayOutputStream; import java.io.FileDescriptor; import java.io.FileDescriptor; import java.io.IOException; import java.io.IOException; import java.util.ArrayList; import java.util.ArrayList; import java.util.List; import java.util.Objects; import java.util.Objects; import java.util.concurrent.Executor; import java.util.function.Supplier; public class SliceManagerService extends ISliceManager.Stub { public class SliceManagerService extends ISliceManager.Stub { Loading @@ -80,13 +88,16 @@ public class SliceManagerService extends ISliceManager.Stub { private final Object mLock = new Object(); private final Object mLock = new Object(); private final Context mContext; private final Context mContext; private final PackageManagerInternal mPackageManagerInternal; private final AppOpsManager mAppOps; private final AppOpsManager mAppOps; private final AssistUtils mAssistUtils; private final AssistUtils mAssistUtils; @GuardedBy("mLock") @GuardedBy("mLock") private final ArrayMap<Uri, PinnedSliceState> mPinnedSlicesByUri = new ArrayMap<>(); private final ArrayMap<Uri, PinnedSliceState> mPinnedSlicesByUri = new ArrayMap<>(); @GuardedBy("mLock") @GuardedBy("mLock") private final SparseArray<String> mLastAssistantPackage = new SparseArray<>(); private final SparseArray<PackageMatchingCache> mAssistantLookup = new SparseArray<>(); @GuardedBy("mLock") private final SparseArray<PackageMatchingCache> mHomeLookup = new SparseArray<>(); private final Handler mHandler; private final Handler mHandler; private final SlicePermissionManager mPermissions; private final SlicePermissionManager mPermissions; Loading @@ -99,6 +110,8 @@ public class SliceManagerService extends ISliceManager.Stub { @VisibleForTesting @VisibleForTesting SliceManagerService(Context context, Looper looper) { SliceManagerService(Context context, Looper looper) { mContext = context; mContext = context; mPackageManagerInternal = Objects.requireNonNull( LocalServices.getService(PackageManagerInternal.class)); mAppOps = context.getSystemService(AppOpsManager.class); mAppOps = context.getSystemService(AppOpsManager.class); mAssistUtils = new AssistUtils(context); mAssistUtils = new AssistUtils(context); mHandler = new Handler(looper); mHandler = new Handler(looper); Loading @@ -111,6 +124,7 @@ public class SliceManagerService extends ISliceManager.Stub { filter.addAction(Intent.ACTION_PACKAGE_DATA_CLEARED); filter.addAction(Intent.ACTION_PACKAGE_DATA_CLEARED); filter.addAction(Intent.ACTION_PACKAGE_REMOVED); filter.addAction(Intent.ACTION_PACKAGE_REMOVED); filter.addDataScheme("package"); filter.addDataScheme("package"); mRoleObserver = new RoleObserver(); mContext.registerReceiverAsUser(mReceiver, UserHandle.ALL, filter, null, mHandler); mContext.registerReceiverAsUser(mReceiver, UserHandle.ALL, filter, null, mHandler); } } Loading Loading @@ -160,7 +174,8 @@ public class SliceManagerService extends ISliceManager.Stub { mHandler.post(() -> { mHandler.post(() -> { if (slicePkg != null && !Objects.equals(pkg, slicePkg)) { if (slicePkg != null && !Objects.equals(pkg, slicePkg)) { mAppUsageStats.reportEvent(slicePkg, user, mAppUsageStats.reportEvent(slicePkg, user, isAssistant(pkg, user) ? SLICE_PINNED_PRIV : SLICE_PINNED); isAssistant(pkg, user) || isDefaultHomeApp(pkg, user) ? SLICE_PINNED_PRIV : SLICE_PINNED); } } }); }); } } Loading Loading @@ -425,19 +440,38 @@ public class SliceManagerService extends ISliceManager.Stub { private boolean hasFullSliceAccess(String pkg, int userId) { private boolean hasFullSliceAccess(String pkg, int userId) { long ident = Binder.clearCallingIdentity(); long ident = Binder.clearCallingIdentity(); try { try { return isAssistant(pkg, userId) || isGrantedFullAccess(pkg, userId); boolean ret = isDefaultHomeApp(pkg, userId) || isAssistant(pkg, userId) || isGrantedFullAccess(pkg, userId); return ret; } finally { } finally { Binder.restoreCallingIdentity(ident); Binder.restoreCallingIdentity(ident); } } } } private boolean isAssistant(String pkg, int userId) { private boolean isAssistant(String pkg, int userId) { if (pkg == null) return false; return getAssistantMatcher(userId).matches(pkg); if (!pkg.equals(mLastAssistantPackage.get(userId))) { } // Failed on cached value, try updating. mLastAssistantPackage.put(userId, getAssistant(userId)); private boolean isDefaultHomeApp(String pkg, int userId) { return getHomeMatcher(userId).matches(pkg); } } return pkg.equals(mLastAssistantPackage.get(userId)); private PackageMatchingCache getAssistantMatcher(int userId) { PackageMatchingCache matcher = mAssistantLookup.get(userId); if (matcher == null) { matcher = new PackageMatchingCache(() -> getAssistant(userId)); mAssistantLookup.put(userId, matcher); } return matcher; } private PackageMatchingCache getHomeMatcher(int userId) { PackageMatchingCache matcher = mHomeLookup.get(userId); if (matcher == null) { matcher = new PackageMatchingCache(() -> getDefaultHome(userId)); mHomeLookup.put(userId, matcher); } return matcher; } } private String getAssistant(int userId) { private String getAssistant(int userId) { Loading @@ -448,6 +482,111 @@ public class SliceManagerService extends ISliceManager.Stub { return cn.getPackageName(); 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<ResolveInfo> allHomeCandidates = new ArrayList<>(); // Default launcher from package manager. final ComponentName defaultLauncher = mPackageManagerInternal .getHomeActivitiesAsUser(allHomeCandidates, userId); 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, // and there's already multiple launchers and there's no default set. // Find the system one with the highest priority. // (We need to check the priority too because of FallbackHome in Settings.) // If there's no system launcher yet, then no one can access slices, until // the user explicitly sets one. final int size = allHomeCandidates.size(); int lastPriority = Integer.MIN_VALUE; for (int i = 0; i < size; i++) { final ResolveInfo ri = allHomeCandidates.get(i); if (!ri.activityInfo.applicationInfo.isSystemApp()) { continue; } if (ri.priority < lastPriority) { continue; } detected = ri.activityInfo.getComponentName(); lastPriority = ri.priority; } } 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) { private boolean isGrantedFullAccess(String pkg, int userId) { return mPermissions.hasFullAccess(pkg, userId); return mPermissions.hasFullAccess(pkg, userId); } } Loading Loading @@ -496,6 +635,30 @@ public class SliceManagerService extends ISliceManager.Stub { return mPermissions.getAllPackagesGranted(pkg); return mPermissions.getAllPackagesGranted(pkg); } } /** * Holder that caches a package that has access to a slice. */ static class PackageMatchingCache { private final Supplier<String> mPkgSource; private String mCurrentPkg; public PackageMatchingCache(Supplier<String> pkgSource) { mPkgSource = pkgSource; } public boolean matches(String pkgCandidate) { if (pkgCandidate == null) return false; if (Objects.equals(pkgCandidate, mCurrentPkg)) { return true; } // Failed on cached value, try updating. mCurrentPkg = mPkgSource.get(); return Objects.equals(pkgCandidate, mCurrentPkg); } } public static class Lifecycle extends SystemService { public static class Lifecycle extends SystemService { private SliceManagerService mService; private SliceManagerService mService; Loading services/tests/uiservicestests/src/com/android/server/slice/PackageMatchingCacheTest.java 0 → 100644 +76 −0 Original line number Original line Diff line number Diff line /* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software distributed under the * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the specific language governing * permissions and limitations under the License. */ package com.android.server.slice; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper.RunWithLooper; import androidx.test.filters.SmallTest; import com.android.server.UiServiceTestCase; import com.android.server.slice.SliceManagerService.PackageMatchingCache; import org.junit.Test; import org.junit.runner.RunWith; import java.util.function.Supplier; @SmallTest @RunWith(AndroidTestingRunner.class) @RunWithLooper public class PackageMatchingCacheTest extends UiServiceTestCase { private final Supplier<String> supplier = mock(Supplier.class); private final PackageMatchingCache cache = new PackageMatchingCache(supplier); @Test public void testNulls() { // Doesn't get for a null input cache.matches(null); verify(supplier, never()).get(); // Gets once valid input in sent. cache.matches(""); verify(supplier).get(); } @Test public void testCaching() { when(supplier.get()).thenReturn("ret.pkg"); assertTrue(cache.matches("ret.pkg")); assertTrue(cache.matches("ret.pkg")); assertTrue(cache.matches("ret.pkg")); verify(supplier, times(1)).get(); } @Test public void testGetOnFailure() { when(supplier.get()).thenReturn("ret.pkg"); assertTrue(cache.matches("ret.pkg")); when(supplier.get()).thenReturn("other.pkg"); assertTrue(cache.matches("other.pkg")); verify(supplier, times(2)).get(); } } services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java +4 −0 Original line number Original line Diff line number Diff line Loading @@ -90,6 +90,8 @@ public class SliceManagerServiceTest extends UiServiceTestCase { @Test @Test public void testAddPinCreatesPinned() throws RemoteException { public void testAddPinCreatesPinned() throws RemoteException { doReturn("pkg").when(mService).getDefaultHome(anyInt()); mService.pinSlice("pkg", TEST_URI, EMPTY_SPECS, mToken); mService.pinSlice("pkg", TEST_URI, EMPTY_SPECS, mToken); mService.pinSlice("pkg", TEST_URI, EMPTY_SPECS, mToken); mService.pinSlice("pkg", TEST_URI, EMPTY_SPECS, mToken); verify(mService, times(1)).createPinnedSlice(eq(maybeAddUserId(TEST_URI, 0)), anyString()); verify(mService, times(1)).createPinnedSlice(eq(maybeAddUserId(TEST_URI, 0)), anyString()); Loading @@ -97,6 +99,8 @@ public class SliceManagerServiceTest extends UiServiceTestCase { @Test @Test public void testRemovePinDestroysPinned() throws RemoteException { public void testRemovePinDestroysPinned() throws RemoteException { doReturn("pkg").when(mService).getDefaultHome(anyInt()); mService.pinSlice("pkg", TEST_URI, EMPTY_SPECS, mToken); mService.pinSlice("pkg", TEST_URI, EMPTY_SPECS, mToken); when(mCreatedSliceState.unpin(eq("pkg"), eq(mToken))).thenReturn(false); when(mCreatedSliceState.unpin(eq("pkg"), eq(mToken))).thenReturn(false); Loading Loading
core/java/android/app/usage/UsageEvents.java +1 −1 Original line number Original line Diff line number Diff line Loading @@ -199,7 +199,7 @@ public final class UsageEvents implements Parcelable { public static final int NOTIFICATION_INTERRUPTION = 12; public static final int NOTIFICATION_INTERRUPTION = 12; /** /** * A Slice was pinned by the default assistant. * A Slice was pinned by the default launcher or the default assistant. * @hide * @hide */ */ @SystemApi @SystemApi Loading
services/core/java/com/android/server/slice/SliceManagerService.java +171 −8 Original line number Original line Diff line number Diff line Loading @@ -28,6 +28,8 @@ import static android.os.Process.SYSTEM_UID; import android.Manifest.permission; import android.Manifest.permission; import android.annotation.NonNull; import android.annotation.NonNull; import android.app.AppOpsManager; import android.app.AppOpsManager; import android.app.role.OnRoleHoldersChangedListener; import android.app.role.RoleManager; import android.app.slice.ISliceManager; import android.app.slice.ISliceManager; import android.app.slice.SliceSpec; import android.app.slice.SliceSpec; import android.app.usage.UsageStatsManagerInternal; import android.app.usage.UsageStatsManagerInternal; Loading @@ -39,7 +41,9 @@ import android.content.Context; import android.content.Intent; import android.content.Intent; import android.content.IntentFilter; import android.content.IntentFilter; import android.content.pm.PackageManager; import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal; import android.content.pm.ProviderInfo; import android.content.pm.ProviderInfo; import android.content.pm.ResolveInfo; import android.net.Uri; import android.net.Uri; import android.os.Binder; import android.os.Binder; import android.os.Handler; import android.os.Handler; Loading @@ -61,6 +65,7 @@ import com.android.internal.app.AssistUtils; import com.android.server.LocalServices; import com.android.server.LocalServices; import com.android.server.ServiceThread; import com.android.server.ServiceThread; import com.android.server.SystemService; import com.android.server.SystemService; import com.android.server.SystemService.TargetUser; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlPullParserException; Loading @@ -72,7 +77,10 @@ import java.io.ByteArrayOutputStream; import java.io.FileDescriptor; import java.io.FileDescriptor; import java.io.IOException; import java.io.IOException; import java.util.ArrayList; import java.util.ArrayList; import java.util.List; import java.util.Objects; import java.util.Objects; import java.util.concurrent.Executor; import java.util.function.Supplier; public class SliceManagerService extends ISliceManager.Stub { public class SliceManagerService extends ISliceManager.Stub { Loading @@ -80,13 +88,16 @@ public class SliceManagerService extends ISliceManager.Stub { private final Object mLock = new Object(); private final Object mLock = new Object(); private final Context mContext; private final Context mContext; private final PackageManagerInternal mPackageManagerInternal; private final AppOpsManager mAppOps; private final AppOpsManager mAppOps; private final AssistUtils mAssistUtils; private final AssistUtils mAssistUtils; @GuardedBy("mLock") @GuardedBy("mLock") private final ArrayMap<Uri, PinnedSliceState> mPinnedSlicesByUri = new ArrayMap<>(); private final ArrayMap<Uri, PinnedSliceState> mPinnedSlicesByUri = new ArrayMap<>(); @GuardedBy("mLock") @GuardedBy("mLock") private final SparseArray<String> mLastAssistantPackage = new SparseArray<>(); private final SparseArray<PackageMatchingCache> mAssistantLookup = new SparseArray<>(); @GuardedBy("mLock") private final SparseArray<PackageMatchingCache> mHomeLookup = new SparseArray<>(); private final Handler mHandler; private final Handler mHandler; private final SlicePermissionManager mPermissions; private final SlicePermissionManager mPermissions; Loading @@ -99,6 +110,8 @@ public class SliceManagerService extends ISliceManager.Stub { @VisibleForTesting @VisibleForTesting SliceManagerService(Context context, Looper looper) { SliceManagerService(Context context, Looper looper) { mContext = context; mContext = context; mPackageManagerInternal = Objects.requireNonNull( LocalServices.getService(PackageManagerInternal.class)); mAppOps = context.getSystemService(AppOpsManager.class); mAppOps = context.getSystemService(AppOpsManager.class); mAssistUtils = new AssistUtils(context); mAssistUtils = new AssistUtils(context); mHandler = new Handler(looper); mHandler = new Handler(looper); Loading @@ -111,6 +124,7 @@ public class SliceManagerService extends ISliceManager.Stub { filter.addAction(Intent.ACTION_PACKAGE_DATA_CLEARED); filter.addAction(Intent.ACTION_PACKAGE_DATA_CLEARED); filter.addAction(Intent.ACTION_PACKAGE_REMOVED); filter.addAction(Intent.ACTION_PACKAGE_REMOVED); filter.addDataScheme("package"); filter.addDataScheme("package"); mRoleObserver = new RoleObserver(); mContext.registerReceiverAsUser(mReceiver, UserHandle.ALL, filter, null, mHandler); mContext.registerReceiverAsUser(mReceiver, UserHandle.ALL, filter, null, mHandler); } } Loading Loading @@ -160,7 +174,8 @@ public class SliceManagerService extends ISliceManager.Stub { mHandler.post(() -> { mHandler.post(() -> { if (slicePkg != null && !Objects.equals(pkg, slicePkg)) { if (slicePkg != null && !Objects.equals(pkg, slicePkg)) { mAppUsageStats.reportEvent(slicePkg, user, mAppUsageStats.reportEvent(slicePkg, user, isAssistant(pkg, user) ? SLICE_PINNED_PRIV : SLICE_PINNED); isAssistant(pkg, user) || isDefaultHomeApp(pkg, user) ? SLICE_PINNED_PRIV : SLICE_PINNED); } } }); }); } } Loading Loading @@ -425,19 +440,38 @@ public class SliceManagerService extends ISliceManager.Stub { private boolean hasFullSliceAccess(String pkg, int userId) { private boolean hasFullSliceAccess(String pkg, int userId) { long ident = Binder.clearCallingIdentity(); long ident = Binder.clearCallingIdentity(); try { try { return isAssistant(pkg, userId) || isGrantedFullAccess(pkg, userId); boolean ret = isDefaultHomeApp(pkg, userId) || isAssistant(pkg, userId) || isGrantedFullAccess(pkg, userId); return ret; } finally { } finally { Binder.restoreCallingIdentity(ident); Binder.restoreCallingIdentity(ident); } } } } private boolean isAssistant(String pkg, int userId) { private boolean isAssistant(String pkg, int userId) { if (pkg == null) return false; return getAssistantMatcher(userId).matches(pkg); if (!pkg.equals(mLastAssistantPackage.get(userId))) { } // Failed on cached value, try updating. mLastAssistantPackage.put(userId, getAssistant(userId)); private boolean isDefaultHomeApp(String pkg, int userId) { return getHomeMatcher(userId).matches(pkg); } } return pkg.equals(mLastAssistantPackage.get(userId)); private PackageMatchingCache getAssistantMatcher(int userId) { PackageMatchingCache matcher = mAssistantLookup.get(userId); if (matcher == null) { matcher = new PackageMatchingCache(() -> getAssistant(userId)); mAssistantLookup.put(userId, matcher); } return matcher; } private PackageMatchingCache getHomeMatcher(int userId) { PackageMatchingCache matcher = mHomeLookup.get(userId); if (matcher == null) { matcher = new PackageMatchingCache(() -> getDefaultHome(userId)); mHomeLookup.put(userId, matcher); } return matcher; } } private String getAssistant(int userId) { private String getAssistant(int userId) { Loading @@ -448,6 +482,111 @@ public class SliceManagerService extends ISliceManager.Stub { return cn.getPackageName(); 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<ResolveInfo> allHomeCandidates = new ArrayList<>(); // Default launcher from package manager. final ComponentName defaultLauncher = mPackageManagerInternal .getHomeActivitiesAsUser(allHomeCandidates, userId); 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, // and there's already multiple launchers and there's no default set. // Find the system one with the highest priority. // (We need to check the priority too because of FallbackHome in Settings.) // If there's no system launcher yet, then no one can access slices, until // the user explicitly sets one. final int size = allHomeCandidates.size(); int lastPriority = Integer.MIN_VALUE; for (int i = 0; i < size; i++) { final ResolveInfo ri = allHomeCandidates.get(i); if (!ri.activityInfo.applicationInfo.isSystemApp()) { continue; } if (ri.priority < lastPriority) { continue; } detected = ri.activityInfo.getComponentName(); lastPriority = ri.priority; } } 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) { private boolean isGrantedFullAccess(String pkg, int userId) { return mPermissions.hasFullAccess(pkg, userId); return mPermissions.hasFullAccess(pkg, userId); } } Loading Loading @@ -496,6 +635,30 @@ public class SliceManagerService extends ISliceManager.Stub { return mPermissions.getAllPackagesGranted(pkg); return mPermissions.getAllPackagesGranted(pkg); } } /** * Holder that caches a package that has access to a slice. */ static class PackageMatchingCache { private final Supplier<String> mPkgSource; private String mCurrentPkg; public PackageMatchingCache(Supplier<String> pkgSource) { mPkgSource = pkgSource; } public boolean matches(String pkgCandidate) { if (pkgCandidate == null) return false; if (Objects.equals(pkgCandidate, mCurrentPkg)) { return true; } // Failed on cached value, try updating. mCurrentPkg = mPkgSource.get(); return Objects.equals(pkgCandidate, mCurrentPkg); } } public static class Lifecycle extends SystemService { public static class Lifecycle extends SystemService { private SliceManagerService mService; private SliceManagerService mService; Loading
services/tests/uiservicestests/src/com/android/server/slice/PackageMatchingCacheTest.java 0 → 100644 +76 −0 Original line number Original line Diff line number Diff line /* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software distributed under the * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the specific language governing * permissions and limitations under the License. */ package com.android.server.slice; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper.RunWithLooper; import androidx.test.filters.SmallTest; import com.android.server.UiServiceTestCase; import com.android.server.slice.SliceManagerService.PackageMatchingCache; import org.junit.Test; import org.junit.runner.RunWith; import java.util.function.Supplier; @SmallTest @RunWith(AndroidTestingRunner.class) @RunWithLooper public class PackageMatchingCacheTest extends UiServiceTestCase { private final Supplier<String> supplier = mock(Supplier.class); private final PackageMatchingCache cache = new PackageMatchingCache(supplier); @Test public void testNulls() { // Doesn't get for a null input cache.matches(null); verify(supplier, never()).get(); // Gets once valid input in sent. cache.matches(""); verify(supplier).get(); } @Test public void testCaching() { when(supplier.get()).thenReturn("ret.pkg"); assertTrue(cache.matches("ret.pkg")); assertTrue(cache.matches("ret.pkg")); assertTrue(cache.matches("ret.pkg")); verify(supplier, times(1)).get(); } @Test public void testGetOnFailure() { when(supplier.get()).thenReturn("ret.pkg"); assertTrue(cache.matches("ret.pkg")); when(supplier.get()).thenReturn("other.pkg"); assertTrue(cache.matches("other.pkg")); verify(supplier, times(2)).get(); } }
services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java +4 −0 Original line number Original line Diff line number Diff line Loading @@ -90,6 +90,8 @@ public class SliceManagerServiceTest extends UiServiceTestCase { @Test @Test public void testAddPinCreatesPinned() throws RemoteException { public void testAddPinCreatesPinned() throws RemoteException { doReturn("pkg").when(mService).getDefaultHome(anyInt()); mService.pinSlice("pkg", TEST_URI, EMPTY_SPECS, mToken); mService.pinSlice("pkg", TEST_URI, EMPTY_SPECS, mToken); mService.pinSlice("pkg", TEST_URI, EMPTY_SPECS, mToken); mService.pinSlice("pkg", TEST_URI, EMPTY_SPECS, mToken); verify(mService, times(1)).createPinnedSlice(eq(maybeAddUserId(TEST_URI, 0)), anyString()); verify(mService, times(1)).createPinnedSlice(eq(maybeAddUserId(TEST_URI, 0)), anyString()); Loading @@ -97,6 +99,8 @@ public class SliceManagerServiceTest extends UiServiceTestCase { @Test @Test public void testRemovePinDestroysPinned() throws RemoteException { public void testRemovePinDestroysPinned() throws RemoteException { doReturn("pkg").when(mService).getDefaultHome(anyInt()); mService.pinSlice("pkg", TEST_URI, EMPTY_SPECS, mToken); mService.pinSlice("pkg", TEST_URI, EMPTY_SPECS, mToken); when(mCreatedSliceState.unpin(eq("pkg"), eq(mToken))).thenReturn(false); when(mCreatedSliceState.unpin(eq("pkg"), eq(mToken))).thenReturn(false); Loading