summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Pinyao Ting <pinyaoting@google.com> 2020-09-09 18:09:05 +0000
committer Pinyao Ting <pinyaoting@google.com> 2020-09-09 18:10:46 +0000
commita8fee79b8150eb8a7127bb0ecb4144da4374c49a (patch)
tree2a2e0925599d7bc5d7b935c4f45cb7fea905c87c
parentd1f7f96a5803c0d7e11ece57919aae1958b65438 (diff)
Revert "clean-up unrequired slice access from default home app"
This reverts commit d1f7f96a5803c0d7e11ece57919aae1958b65438. Reason for revert: regression, see b/167993144. (original bug: b/163121804) Change-Id: I54699b02e7de8c6e6ecebe0a5b684b7383caa81b
-rw-r--r--core/java/android/app/usage/UsageEvents.java2
-rw-r--r--services/core/java/com/android/server/slice/SliceManagerService.java179
-rw-r--r--services/tests/uiservicestests/src/com/android/server/slice/PackageMatchingCacheTest.java76
-rw-r--r--services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java4
4 files changed, 252 insertions, 9 deletions
diff --git a/core/java/android/app/usage/UsageEvents.java b/core/java/android/app/usage/UsageEvents.java
index 0e6a0637d801..3522b1b8aff5 100644
--- a/core/java/android/app/usage/UsageEvents.java
+++ b/core/java/android/app/usage/UsageEvents.java
@@ -199,7 +199,7 @@ public final class UsageEvents implements Parcelable {
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
*/
@SystemApi
diff --git a/services/core/java/com/android/server/slice/SliceManagerService.java b/services/core/java/com/android/server/slice/SliceManagerService.java
index 5aedfc19028b..2a74b3d23829 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;
@@ -39,7 +41,9 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
import android.content.pm.ProviderInfo;
+import android.content.pm.ResolveInfo;
import android.net.Uri;
import android.os.Binder;
import android.os.Handler;
@@ -61,6 +65,7 @@ import com.android.internal.app.AssistUtils;
import com.android.server.LocalServices;
import com.android.server.ServiceThread;
import com.android.server.SystemService;
+import com.android.server.SystemService.TargetUser;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -72,7 +77,10 @@ import java.io.ByteArrayOutputStream;
import java.io.FileDescriptor;
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 {
@@ -80,13 +88,16 @@ public class SliceManagerService extends ISliceManager.Stub {
private final Object mLock = new Object();
private final Context mContext;
+ private final PackageManagerInternal mPackageManagerInternal;
private final AppOpsManager mAppOps;
private final AssistUtils mAssistUtils;
@GuardedBy("mLock")
private final ArrayMap<Uri, PinnedSliceState> mPinnedSlicesByUri = new ArrayMap<>();
@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 SlicePermissionManager mPermissions;
@@ -99,6 +110,8 @@ public class SliceManagerService extends ISliceManager.Stub {
@VisibleForTesting
SliceManagerService(Context context, Looper looper) {
mContext = context;
+ mPackageManagerInternal = Objects.requireNonNull(
+ LocalServices.getService(PackageManagerInternal.class));
mAppOps = context.getSystemService(AppOpsManager.class);
mAssistUtils = new AssistUtils(context);
mHandler = new Handler(looper);
@@ -111,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);
}
@@ -160,7 +174,8 @@ public class SliceManagerService extends ISliceManager.Stub {
mHandler.post(() -> {
if (slicePkg != null && !Objects.equals(pkg, slicePkg)) {
mAppUsageStats.reportEvent(slicePkg, user,
- isAssistant(pkg, user) ? SLICE_PINNED_PRIV : SLICE_PINNED);
+ isAssistant(pkg, user) || isDefaultHomeApp(pkg, user)
+ ? SLICE_PINNED_PRIV : SLICE_PINNED);
}
});
}
@@ -425,19 +440,38 @@ public class SliceManagerService extends ISliceManager.Stub {
private boolean hasFullSliceAccess(String pkg, int userId) {
long ident = Binder.clearCallingIdentity();
try {
- return isAssistant(pkg, userId) || isGrantedFullAccess(pkg, userId);
+ boolean ret = isDefaultHomeApp(pkg, userId) || isAssistant(pkg, userId)
+ || isGrantedFullAccess(pkg, userId);
+ return ret;
} finally {
Binder.restoreCallingIdentity(ident);
}
}
private boolean isAssistant(String pkg, int userId) {
- if (pkg == null) return false;
- if (!pkg.equals(mLastAssistantPackage.get(userId))) {
- // Failed on cached value, try updating.
- mLastAssistantPackage.put(userId, getAssistant(userId));
+ return getAssistantMatcher(userId).matches(pkg);
+ }
+
+ private boolean isDefaultHomeApp(String pkg, int userId) {
+ return getHomeMatcher(userId).matches(pkg);
+ }
+
+ private PackageMatchingCache getAssistantMatcher(int userId) {
+ PackageMatchingCache matcher = mAssistantLookup.get(userId);
+ if (matcher == null) {
+ matcher = new PackageMatchingCache(() -> getAssistant(userId));
+ mAssistantLookup.put(userId, matcher);
}
- return pkg.equals(mLastAssistantPackage.get(userId));
+ 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) {
@@ -448,6 +482,111 @@ 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<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) {
return mPermissions.hasFullAccess(pkg, userId);
}
@@ -496,6 +635,30 @@ public class SliceManagerService extends ISliceManager.Stub {
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 {
private SliceManagerService mService;
diff --git a/services/tests/uiservicestests/src/com/android/server/slice/PackageMatchingCacheTest.java b/services/tests/uiservicestests/src/com/android/server/slice/PackageMatchingCacheTest.java
new file mode 100644
index 000000000000..f6c854e23494
--- /dev/null
+++ b/services/tests/uiservicestests/src/com/android/server/slice/PackageMatchingCacheTest.java
@@ -0,0 +1,76 @@
+/*
+ * 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();
+ }
+}
diff --git a/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java
index cf1c36c0d243..a4436951f48b 100644
--- a/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java
@@ -90,6 +90,8 @@ public class SliceManagerServiceTest extends UiServiceTestCase {
@Test
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);
verify(mService, times(1)).createPinnedSlice(eq(maybeAddUserId(TEST_URI, 0)), anyString());
@@ -97,6 +99,8 @@ public class SliceManagerServiceTest extends UiServiceTestCase {
@Test
public void testRemovePinDestroysPinned() throws RemoteException {
+ doReturn("pkg").when(mService).getDefaultHome(anyInt());
+
mService.pinSlice("pkg", TEST_URI, EMPTY_SPECS, mToken);
when(mCreatedSliceState.unpin(eq("pkg"), eq(mToken))).thenReturn(false);