mContext.registerReceiverAsUser(mPackageMonitor, UserHandle.ALL,
packageFilter, null, mHandler);
+ final IntentFilter preferedActivityFilter = new IntentFilter();
+ preferedActivityFilter.addAction(Intent.ACTION_PREFERRED_ACTIVITY_CHANGED);
+ preferedActivityFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
+ mContext.registerReceiverAsUser(mPackageMonitor, UserHandle.ALL,
+ preferedActivityFilter, null, mHandler);
+
final IntentFilter localeFilter = new IntentFilter();
localeFilter.addAction(Intent.ACTION_LOCALE_CHANGED);
localeFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
// We override this method in unit tests to do a simpler check.
boolean hasShortcutHostPermission(@NonNull String callingPackage, int userId) {
- return hasShortcutHostPermissionInner(callingPackage, userId);
+ final long start = injectElapsedRealtime();
+ try {
+ return hasShortcutHostPermissionInner(callingPackage, userId);
+ } finally {
+ logDurationStat(Stats.LAUNCHER_PERMISSION_CHECK, start);
+ }
}
// This method is extracted so we can directly call this method from unit tests,
@VisibleForTesting
boolean hasShortcutHostPermissionInner(@NonNull String callingPackage, int userId) {
synchronized (mLock) {
- final long start = injectElapsedRealtime();
-
final ShortcutUser user = getUserShortcutsLocked(userId);
+ // Always trust the in-memory cache.
+ final ComponentName cached = user.getCachedLauncher();
+ if (cached != null) {
+ if (cached.getPackageName().equals(callingPackage)) {
+ return true;
+ }
+ }
+ // If the cached one doesn't match, then go ahead
+
final List<ResolveInfo> allHomeCandidates = new ArrayList<>();
// Default launcher from package manager.
final long startGetHomeActivitiesAsUser = injectElapsedRealtime();
- final ComponentName defaultLauncher = injectPackageManagerInternal()
+ final ComponentName defaultLauncher = mPackageManagerInternal
.getHomeActivitiesAsUser(allHomeCandidates, userId);
logDurationStat(Stats.GET_DEFAULT_HOME, startGetHomeActivitiesAsUser);
Slog.v(TAG, "Default launcher from PM: " + detected);
}
} else {
- detected = user.getDefaultLauncherComponent();
+ detected = user.getLastKnownLauncher();
if (detected != null) {
if (injectIsActivityEnabledAndExported(detected, userId)) {
} else {
Slog.w(TAG, "Cached launcher " + detected + " no longer exists");
detected = null;
- user.setDefaultLauncherComponent(null);
+ user.clearLauncher();
}
}
}
lastPriority = ri.priority;
}
}
- logDurationStat(Stats.LAUNCHER_PERMISSION_CHECK, start);
+ // Update the cache.
+ user.setLauncher(detected);
if (detected != null) {
if (DEBUG) {
Slog.v(TAG, "Detected launcher: " + detected);
}
- user.setDefaultLauncherComponent(detected);
return detected.getPackageName().equals(callingPackage);
} else {
// Default launcher not found.
return;
}
+ // Whenever we get one of those package broadcasts, or get
+ // ACTION_PREFERRED_ACTIVITY_CHANGED, we purge the default launcher cache.
+ synchronized (mLock) {
+ final ShortcutUser user = getUserShortcutsLocked(userId);
+ user.clearLauncher();
+ }
+ if (Intent.ACTION_PREFERRED_ACTIVITY_CHANGED.equals(action)) {
+ // Nothing farther to do.
+ return;
+ }
+
final Uri intentUri = intent.getData();
final String packageName = (intentUri != null) ? intentUri.getSchemeSpecificPart()
: null;
private void clearLauncher() {
synchronized (mLock) {
- getUserShortcutsLocked(mUserId).setDefaultLauncherComponent(null);
+ getUserShortcutsLocked(mUserId).forceClearLauncher();
}
}
hasShortcutHostPermissionInner("-", mUserId);
getOutPrintWriter().println("Launcher: "
- + getUserShortcutsLocked(mUserId).getDefaultLauncherComponent());
+ + getUserShortcutsLocked(mUserId).getLastKnownLauncher());
}
}
}
}
- @VisibleForTesting
- PackageManagerInternal injectPackageManagerInternal() {
- return mPackageManagerInternal;
- }
-
File getUserBitmapFilePath(@UserIdInt int userId) {
return new File(injectUserDataPath(userId), DIRECTORY_BITMAPS);
}
}
private void verifyStatesInner() {
- synchronized (this) {
+ synchronized (mLock) {
forEachLoadedUserLocked(u -> u.forAllPackageItems(ShortcutPackageItem::verifyStates));
}
}
--- /dev/null
+/*
+ * Copyright (C) 2016 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.pm;
+
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.list;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doAnswer;
+
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.ResolveInfo;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import java.util.List;
+
+/**
+ * Tests for {@link ShortcutService#hasShortcutHostPermissionInner}.
+ */
+@SmallTest
+public class ShortcutManagerTest6 extends BaseShortcutManagerTest {
+
+ private static final String PACKAGE_SYSTEM_LAUNCHER = "com.android.systemlauncher";
+ private static final String PACKAGE_SYSTEM_LAUNCHER_NAME = "systemlauncher_name";
+ private static final int PACKAGE_SYSTEM_LAUNCHER_PRIORITY = 0;
+
+ private static final String PACKAGE_FALLBACK_LAUNCHER = "com.android.settings";
+ private static final String PACKAGE_FALLBACK_LAUNCHER_NAME = "fallback";
+ private static final int PACKAGE_FALLBACK_LAUNCHER_PRIORITY = -999;
+
+ private void prepareGetHomeActivitiesAsUser(ComponentName preferred,
+ List<ResolveInfo> candidates, int userId) {
+ doAnswer(inv -> {
+ ((List) inv.getArguments()[0]).addAll(candidates);
+ return preferred;
+ }).when(mMockPackageManagerInternal).getHomeActivitiesAsUser(any(List.class), eq(userId));
+ }
+
+ private static ComponentName cn(String packageName, String name) {
+ return new ComponentName(packageName, name);
+ }
+
+ private static ResolveInfo ri(String packageName, String name, boolean isSystem, int priority) {
+ final ResolveInfo ri = new ResolveInfo();
+ ri.activityInfo = new ActivityInfo();
+ ri.activityInfo.applicationInfo = new ApplicationInfo();
+
+ ri.activityInfo.packageName = packageName;
+ ri.activityInfo.name = name;
+ if (isSystem) {
+ ri.activityInfo.applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
+ }
+ ri.priority = priority;
+ return ri;
+ }
+
+ private static ResolveInfo getSystemLauncher() {
+ return ri(PACKAGE_SYSTEM_LAUNCHER, PACKAGE_SYSTEM_LAUNCHER_NAME, true,
+ PACKAGE_SYSTEM_LAUNCHER_PRIORITY);
+ }
+
+ private static ResolveInfo getFallbackLauncher() {
+ return ri(PACKAGE_FALLBACK_LAUNCHER, PACKAGE_FALLBACK_LAUNCHER_NAME, true,
+ PACKAGE_FALLBACK_LAUNCHER_PRIORITY);
+ }
+
+ public void testHasShortcutHostPermissionInner_systemLauncherOnly() {
+ // Preferred isn't set, use the system launcher.
+ prepareGetHomeActivitiesAsUser(
+ /* preferred */ null,
+ list(getSystemLauncher(), getFallbackLauncher()),
+ USER_0);
+ assertTrue(mService.hasShortcutHostPermissionInner(PACKAGE_SYSTEM_LAUNCHER, USER_0));
+ assertFalse(mService.hasShortcutHostPermissionInner(PACKAGE_FALLBACK_LAUNCHER, USER_0));
+ assertFalse(mService.hasShortcutHostPermissionInner(CALLING_PACKAGE_1, USER_0));
+ assertFalse(mService.hasShortcutHostPermissionInner(CALLING_PACKAGE_2, USER_0));
+
+ // Should be cached.
+ assertEquals(cn(PACKAGE_SYSTEM_LAUNCHER, PACKAGE_SYSTEM_LAUNCHER_NAME),
+ mService.getUserShortcutsLocked(USER_0).getLastKnownLauncher());
+ assertEquals(cn(PACKAGE_SYSTEM_LAUNCHER, PACKAGE_SYSTEM_LAUNCHER_NAME),
+ mService.getUserShortcutsLocked(USER_0).getCachedLauncher());
+
+ // Also make sure the last known is saved, but the cached is not.
+
+ initService();
+
+ assertEquals(cn(PACKAGE_SYSTEM_LAUNCHER, PACKAGE_SYSTEM_LAUNCHER_NAME),
+ mService.getUserShortcutsLocked(USER_0).getLastKnownLauncher());
+ assertEquals(null,
+ mService.getUserShortcutsLocked(USER_0).getCachedLauncher());
+ }
+
+ public void testHasShortcutHostPermissionInner_with3pLauncher() {
+ // Preferred isn't set, still use the system launcher.
+ prepareGetHomeActivitiesAsUser(
+ /* preferred */ null,
+ list(getSystemLauncher(), getFallbackLauncher(),
+ ri(CALLING_PACKAGE_1, "name", false, 0),
+ ri(CALLING_PACKAGE_2, "name", false, 0)
+ ),
+ USER_0);
+ assertTrue(mService.hasShortcutHostPermissionInner(PACKAGE_SYSTEM_LAUNCHER, USER_0));
+ assertFalse(mService.hasShortcutHostPermissionInner(PACKAGE_FALLBACK_LAUNCHER, USER_0));
+ assertFalse(mService.hasShortcutHostPermissionInner(CALLING_PACKAGE_1, USER_0));
+ assertFalse(mService.hasShortcutHostPermissionInner(CALLING_PACKAGE_2, USER_0));
+
+ // Should be cached.
+ assertEquals(cn(PACKAGE_SYSTEM_LAUNCHER, PACKAGE_SYSTEM_LAUNCHER_NAME),
+ mService.getUserShortcutsLocked(USER_0).getLastKnownLauncher());
+ assertEquals(cn(PACKAGE_SYSTEM_LAUNCHER, PACKAGE_SYSTEM_LAUNCHER_NAME),
+ mService.getUserShortcutsLocked(USER_0).getCachedLauncher());
+ }
+
+ public void testHasShortcutHostPermissionInner_with3pLauncher_complicated() {
+ // Preferred is set. That's the default launcher.
+ prepareGetHomeActivitiesAsUser(
+ /* preferred */ cn(CALLING_PACKAGE_2, "name"),
+ list(getSystemLauncher(), getFallbackLauncher(),
+ ri(CALLING_PACKAGE_1, "name", false, 0),
+ ri(CALLING_PACKAGE_2, "name", false, 0)
+ ),
+ USER_0);
+ assertFalse(mService.hasShortcutHostPermissionInner(PACKAGE_SYSTEM_LAUNCHER, USER_0));
+ assertFalse(mService.hasShortcutHostPermissionInner(PACKAGE_FALLBACK_LAUNCHER, USER_0));
+ assertFalse(mService.hasShortcutHostPermissionInner(CALLING_PACKAGE_1, USER_0));
+ assertTrue(mService.hasShortcutHostPermissionInner(CALLING_PACKAGE_2, USER_0));
+
+ // Should be cached.
+ assertEquals(cn(CALLING_PACKAGE_2, "name"),
+ mService.getUserShortcutsLocked(USER_0).getLastKnownLauncher());
+ assertEquals(cn(CALLING_PACKAGE_2, "name"),
+ mService.getUserShortcutsLocked(USER_0).getCachedLauncher());
+
+
+ // Once set, even after the preferred launcher is cleared, SM still allows it to access
+ // shortcuts.
+ prepareGetHomeActivitiesAsUser(
+ /* preferred */ null,
+ list(getSystemLauncher(), getFallbackLauncher(),
+ ri(CALLING_PACKAGE_1, "name", false, 0),
+ ri(CALLING_PACKAGE_2, "name", false, 0)
+ ),
+ USER_0);
+
+ assertFalse(mService.hasShortcutHostPermissionInner(PACKAGE_SYSTEM_LAUNCHER, USER_0));
+ assertFalse(mService.hasShortcutHostPermissionInner(PACKAGE_FALLBACK_LAUNCHER, USER_0));
+ assertFalse(mService.hasShortcutHostPermissionInner(CALLING_PACKAGE_1, USER_0));
+ assertTrue(mService.hasShortcutHostPermissionInner(CALLING_PACKAGE_2, USER_0));
+
+ // Should be cached.
+ assertEquals(cn(CALLING_PACKAGE_2, "name"),
+ mService.getUserShortcutsLocked(USER_0).getLastKnownLauncher());
+ assertEquals(cn(CALLING_PACKAGE_2, "name"),
+ mService.getUserShortcutsLocked(USER_0).getCachedLauncher());
+
+ // However, if the component has been disabled, then we'll recalculate it.
+ mEnabledActivityChecker = (comp, user) -> false;
+
+ assertTrue(mService.hasShortcutHostPermissionInner(PACKAGE_SYSTEM_LAUNCHER, USER_0));
+ assertFalse(mService.hasShortcutHostPermissionInner(PACKAGE_FALLBACK_LAUNCHER, USER_0));
+ assertFalse(mService.hasShortcutHostPermissionInner(CALLING_PACKAGE_1, USER_0));
+ assertFalse(mService.hasShortcutHostPermissionInner(CALLING_PACKAGE_2, USER_0));
+
+ mEnabledActivityChecker = (comp, user) -> true;
+
+ // Now the preferred changed.
+ prepareGetHomeActivitiesAsUser(
+ /* preferred */ cn(CALLING_PACKAGE_1, "xyz"),
+ list(getSystemLauncher(), getFallbackLauncher(),
+ ri(CALLING_PACKAGE_1, "name", false, 0),
+ ri(CALLING_PACKAGE_2, "name", false, 0)
+ ),
+ USER_0);
+
+ assertTrue(mService.hasShortcutHostPermissionInner(CALLING_PACKAGE_1, USER_0));
+
+ // Should be cached.
+ assertEquals(cn(CALLING_PACKAGE_1, "xyz"),
+ mService.getUserShortcutsLocked(USER_0).getLastKnownLauncher());
+ assertEquals(cn(CALLING_PACKAGE_1, "xyz"),
+ mService.getUserShortcutsLocked(USER_0).getCachedLauncher());
+
+
+ // As long as there's the cached launcher set, even if getHomeActivitiesAsUser()
+ // returns different values, the cached one is still the default.
+ prepareGetHomeActivitiesAsUser(
+ /* preferred */ getSystemLauncher().activityInfo.getComponentName(),
+ list(getSystemLauncher(), getFallbackLauncher()),
+ USER_0);
+
+ assertTrue(mService.hasShortcutHostPermissionInner(CALLING_PACKAGE_1, USER_0));
+
+ // Cached ones haven't changed.
+ assertEquals(cn(CALLING_PACKAGE_1, "xyz"),
+ mService.getUserShortcutsLocked(USER_0).getLastKnownLauncher());
+ assertEquals(cn(CALLING_PACKAGE_1, "xyz"),
+ mService.getUserShortcutsLocked(USER_0).getCachedLauncher());
+
+ // However, now the "real" default launcher is the system one. So if the system
+ // launcher asks for shortcuts, we'll allow it.
+ assertTrue(mService.hasShortcutHostPermissionInner(PACKAGE_SYSTEM_LAUNCHER, USER_0));
+
+ // Since the cache is updated, CALLING_PACKAGE_1 no longer has the permission.
+ assertFalse(mService.hasShortcutHostPermissionInner(CALLING_PACKAGE_1, USER_0));
+
+ // Cached ones haven't changed.
+ assertEquals(cn(PACKAGE_SYSTEM_LAUNCHER, PACKAGE_SYSTEM_LAUNCHER_NAME),
+ mService.getUserShortcutsLocked(USER_0).getLastKnownLauncher());
+ assertEquals(cn(PACKAGE_SYSTEM_LAUNCHER, PACKAGE_SYSTEM_LAUNCHER_NAME),
+ mService.getUserShortcutsLocked(USER_0).getCachedLauncher());
+ }
+
+ public void testHasShortcutHostPermissionInner_multiUser() {
+ prepareGetHomeActivitiesAsUser(
+ /* preferred */ null,
+ list(getSystemLauncher(), getFallbackLauncher()),
+ USER_0);
+
+ prepareGetHomeActivitiesAsUser(
+ /* preferred */ cn(CALLING_PACKAGE_2, "name"),
+ list(getSystemLauncher(), getFallbackLauncher(),
+ ri(CALLING_PACKAGE_1, "name", false, 0),
+ ri(CALLING_PACKAGE_2, "name", false, 0)
+ ),
+ USER_10);
+
+ assertTrue(mService.hasShortcutHostPermissionInner(PACKAGE_SYSTEM_LAUNCHER, USER_0));
+ assertFalse(mService.hasShortcutHostPermissionInner(PACKAGE_FALLBACK_LAUNCHER, USER_0));
+ assertFalse(mService.hasShortcutHostPermissionInner(CALLING_PACKAGE_1, USER_0));
+ assertFalse(mService.hasShortcutHostPermissionInner(CALLING_PACKAGE_2, USER_0));
+
+ // Check the cache.
+ assertEquals(cn(PACKAGE_SYSTEM_LAUNCHER, PACKAGE_SYSTEM_LAUNCHER_NAME),
+ mService.getUserShortcutsLocked(USER_0).getLastKnownLauncher());
+ assertEquals(cn(PACKAGE_SYSTEM_LAUNCHER, PACKAGE_SYSTEM_LAUNCHER_NAME),
+ mService.getUserShortcutsLocked(USER_0).getCachedLauncher());
+
+ assertFalse(mService.hasShortcutHostPermissionInner(PACKAGE_SYSTEM_LAUNCHER, USER_10));
+ assertFalse(mService.hasShortcutHostPermissionInner(PACKAGE_FALLBACK_LAUNCHER, USER_10));
+ assertFalse(mService.hasShortcutHostPermissionInner(CALLING_PACKAGE_1, USER_10));
+ assertTrue(mService.hasShortcutHostPermissionInner(CALLING_PACKAGE_2, USER_10));
+
+ // Check the cache.
+ assertEquals(cn(CALLING_PACKAGE_2, "name"),
+ mService.getUserShortcutsLocked(USER_10).getLastKnownLauncher());
+ assertEquals(cn(CALLING_PACKAGE_2, "name"),
+ mService.getUserShortcutsLocked(USER_10).getCachedLauncher());
+ }
+
+ public void testHasShortcutHostPermissionInner_clearCache() {
+ prepareGetHomeActivitiesAsUser(
+ /* preferred */ null,
+ list(getSystemLauncher(), getFallbackLauncher()),
+ USER_0);
+
+ prepareGetHomeActivitiesAsUser(
+ /* preferred */ cn(CALLING_PACKAGE_2, "name"),
+ list(getSystemLauncher(), getFallbackLauncher(),
+ ri(CALLING_PACKAGE_1, "name", false, 0),
+ ri(CALLING_PACKAGE_2, "name", false, 0)
+ ),
+ USER_10);
+
+ assertTrue(mService.hasShortcutHostPermissionInner(PACKAGE_SYSTEM_LAUNCHER, USER_0));
+ assertTrue(mService.hasShortcutHostPermissionInner(CALLING_PACKAGE_2, USER_10));
+
+ assertEquals(cn(PACKAGE_SYSTEM_LAUNCHER, PACKAGE_SYSTEM_LAUNCHER_NAME),
+ mService.getUserShortcutsLocked(USER_0).getCachedLauncher());
+
+ assertEquals(cn(CALLING_PACKAGE_2, "name"),
+ mService.getUserShortcutsLocked(USER_10).getCachedLauncher());
+
+ // Send ACTION_PREFERRED_ACTIVITY_CHANGED on user 10.
+ // But the user is not running, so will be ignored.
+ mService.mPackageMonitor.onReceive(mServiceContext,
+ new Intent(Intent.ACTION_PREFERRED_ACTIVITY_CHANGED).putExtra(
+ Intent.EXTRA_USER_HANDLE, USER_10));
+
+ assertEquals(cn(PACKAGE_SYSTEM_LAUNCHER, PACKAGE_SYSTEM_LAUNCHER_NAME),
+ mService.getUserShortcutsLocked(USER_0).getCachedLauncher());
+
+ assertEquals(cn(CALLING_PACKAGE_2, "name"),
+ mService.getUserShortcutsLocked(USER_10).getCachedLauncher());
+
+ // Send it again after starting the user.
+ mRunningUsers.put(USER_10, true);
+ mService.mPackageMonitor.onReceive(mServiceContext,
+ new Intent(Intent.ACTION_PREFERRED_ACTIVITY_CHANGED).putExtra(
+ Intent.EXTRA_USER_HANDLE, USER_10));
+
+ assertEquals(cn(PACKAGE_SYSTEM_LAUNCHER, PACKAGE_SYSTEM_LAUNCHER_NAME),
+ mService.getUserShortcutsLocked(USER_0).getCachedLauncher());
+
+ // Only user-10's cache is cleared.
+ assertEquals(null,
+ mService.getUserShortcutsLocked(USER_10).getCachedLauncher());
+
+ }
+}