DivestOS/Patches/LineageOS-20.0/ASB-2024-06/fwb-03.patch
Tavi 7f00fd1dde
20.0: June 2024 ASB picks
Signed-off-by: Tavi <tavi@divested.dev>
2024-06-13 15:11:11 -04:00

464 lines
21 KiB
Diff

From fcb02396749ec9318803887febffea13f726dfc2 Mon Sep 17 00:00:00 2001
From: Valentin Iftime <valiiftime@google.com>
Date: Thu, 22 Feb 2024 10:51:58 +0100
Subject: [PATCH] Check for NLS bind permission when rebinding services
Also, after updating packages with NLS components, check
the approved services and remove from approved list if missing permissions.
Test: atest ManagedServicesTest
Bug: 321707289
(cherry picked from commit 24b13a64f9f5e5aa7f45a2132806d6c74e2c62dc)
(cherry picked from https://googleplex-android-review.googlesource.com/q/commit:f126be35f4b9f179dbb957ec56ca80b12f47abdc)
Merged-In: I11901755ec430c6e3145def9d67e4e63cda00806
Change-Id: I11901755ec430c6e3145def9d67e4e63cda00806
---
.../server/notification/ManagedServices.java | 193 ++++++++++--------
.../notification/ManagedServicesTest.java | 54 +++++
2 files changed, 164 insertions(+), 83 deletions(-)
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index d0cfa164b5af..c3691e717e9b 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -132,7 +132,6 @@ abstract public class ManagedServices {
// contains connections to all connected services, including app services
// and system services
- @GuardedBy("mMutex")
private final ArrayList<ManagedServiceInfo> mServices = new ArrayList<>();
/**
* The services that have been bound by us. If the service is also connected, it will also
@@ -151,15 +150,13 @@ abstract public class ManagedServices {
= new ArraySet<>();
// Just the packages from mEnabledServicesForCurrentProfiles
private ArraySet<String> mEnabledServicesPackageNames = new ArraySet<>();
- // Per user id, list of enabled packages that have nevertheless asked not to be run
- private final android.util.SparseSetArray<ComponentName> mSnoozing =
- new android.util.SparseSetArray<>();
+ // List of enabled packages that have nevertheless asked not to be run
+ private ArraySet<ComponentName> mSnoozingForCurrentProfiles = new ArraySet<>();
// List of approved packages or components (by user, then by primary/secondary) that are
// allowed to be bound as managed services. A package or component appearing in this list does
// not mean that we are currently bound to said package/component.
- protected final ArrayMap<Integer, ArrayMap<Boolean, ArraySet<String>>> mApproved =
- new ArrayMap<>();
+ protected ArrayMap<Integer, ArrayMap<Boolean, ArraySet<String>>> mApproved = new ArrayMap<>();
// List of packages or components (by user) that are configured to be enabled/disabled
// explicitly by the user
@@ -318,7 +315,6 @@ ArrayMap<Boolean, ArrayList<ComponentName>> resetComponents(String packageName,
return changes;
}
- @GuardedBy("mApproved")
private boolean clearUserSetFlagLocked(ComponentName component, int userId) {
String approvedValue = getApprovedValue(component.flattenToString());
ArraySet<String> userSet = mUserSetServices.get(userId);
@@ -379,8 +375,8 @@ public void dump(PrintWriter pw, DumpFilter filter) {
pw.println(" " + cmpt);
}
+ pw.println(" Live " + getCaption() + "s (" + mServices.size() + "):");
synchronized (mMutex) {
- pw.println(" Live " + getCaption() + "s (" + mServices.size() + "):");
for (ManagedServiceInfo info : mServices) {
if (filter != null && !filter.matches(info.component)) continue;
pw.println(" " + info.component
@@ -390,15 +386,10 @@ public void dump(PrintWriter pw, DumpFilter filter) {
}
}
- synchronized (mSnoozing) {
- pw.println(" Snoozed " + getCaption() + "s ("
- + mSnoozing.size() + "):");
- for (int i = 0; i < mSnoozing.size(); i++) {
- pw.println(" User: " + mSnoozing.keyAt(i));
- for (ComponentName name : mSnoozing.valuesAt(i)) {
- pw.println(" " + name.flattenToShortString());
- }
- }
+ pw.println(" Snoozed " + getCaption() + "s (" +
+ mSnoozingForCurrentProfiles.size() + "):");
+ for (ComponentName name : mSnoozingForCurrentProfiles) {
+ pw.println(" " + name.flattenToShortString());
}
}
@@ -440,16 +431,8 @@ public void dump(ProtoOutputStream proto, DumpFilter filter) {
}
}
- synchronized (mSnoozing) {
- for (int i = 0; i < mSnoozing.size(); i++) {
- long token = proto.start(ManagedServicesProto.SNOOZED);
- proto.write(ManagedServicesProto.SnoozedServices.USER_ID,
- mSnoozing.keyAt(i));
- for (ComponentName name : mSnoozing.valuesAt(i)) {
- name.dumpDebug(proto, ManagedServicesProto.SnoozedServices.SNOOZED);
- }
- proto.end(token);
- }
+ for (ComponentName name : mSnoozingForCurrentProfiles) {
+ name.dumpDebug(proto, ManagedServicesProto.SNOOZED);
}
}
@@ -918,6 +901,23 @@ protected boolean isPackageOrComponentAllowed(String pkgOrComponent, int userId)
return false;
}
+ protected boolean isPackageOrComponentAllowedWithPermission(ComponentName component,
+ int userId) {
+ if (!(isPackageOrComponentAllowed(component.flattenToString(), userId)
+ || isPackageOrComponentAllowed(component.getPackageName(), userId))) {
+ return false;
+ }
+ return componentHasBindPermission(component, userId);
+ }
+
+ private boolean componentHasBindPermission(ComponentName component, int userId) {
+ ServiceInfo info = getServiceInfo(component, userId);
+ if (info == null) {
+ return false;
+ }
+ return mConfig.bindPermission.equals(info.permission);
+ }
+
boolean isPackageOrComponentUserSet(String pkgOrComponent, int userId) {
synchronized (mApproved) {
ArraySet<String> services = mUserSetServices.get(userId);
@@ -975,6 +975,7 @@ public void onPackagesChanged(boolean removingPackage, String[] pkgList, int[] u
for (int uid : uidList) {
if (isPackageAllowed(pkgName, UserHandle.getUserId(uid))) {
anyServicesInvolved = true;
+ trimApprovedListsForInvalidServices(pkgName, UserHandle.getUserId(uid));
}
}
}
@@ -992,9 +993,6 @@ public void onUserRemoved(int user) {
synchronized (mApproved) {
mApproved.remove(user);
}
- synchronized (mSnoozing) {
- mSnoozing.remove(user);
- }
rebindServices(true, user);
}
@@ -1014,12 +1012,10 @@ private ManagedServiceInfo getServiceFromTokenLocked(IInterface service) {
return null;
}
final IBinder token = service.asBinder();
- synchronized (mMutex) {
- final int nServices = mServices.size();
- for (int i = 0; i < nServices; i++) {
- final ManagedServiceInfo info = mServices.get(i);
- if (info.service.asBinder() == token) return info;
- }
+ final int N = mServices.size();
+ for (int i = 0; i < N; i++) {
+ final ManagedServiceInfo info = mServices.get(i);
+ if (info.service.asBinder() == token) return info;
}
return null;
}
@@ -1094,17 +1090,15 @@ protected void registerGuestService(ManagedServiceInfo guest) {
}
protected void setComponentState(ComponentName component, int userId, boolean enabled) {
- synchronized (mSnoozing) {
- boolean previous = !mSnoozing.contains(userId, component);
- if (previous == enabled) {
- return;
- }
+ boolean previous = !mSnoozingForCurrentProfiles.contains(component);
+ if (previous == enabled) {
+ return;
+ }
- if (enabled) {
- mSnoozing.remove(userId, component);
- } else {
- mSnoozing.add(userId, component);
- }
+ if (enabled) {
+ mSnoozingForCurrentProfiles.remove(component);
+ } else {
+ mSnoozingForCurrentProfiles.add(component);
}
// State changed
@@ -1113,8 +1107,7 @@ protected void setComponentState(ComponentName component, int userId, boolean en
synchronized (mMutex) {
if (enabled) {
- if (isPackageOrComponentAllowed(component.flattenToString(), userId)
- || isPackageOrComponentAllowed(component.getPackageName(), userId)) {
+ if (isPackageOrComponentAllowedWithPermission(component, userId)) {
registerServiceLocked(component, userId);
} else {
Slog.d(TAG, component + " no longer has permission to be bound");
@@ -1252,6 +1245,33 @@ private boolean removeUninstalledItemsFromApprovedLists(int uninstalledUserId, S
return removed;
}
+ private void trimApprovedListsForInvalidServices(String packageName, int userId) {
+ synchronized (mApproved) {
+ final ArrayMap<Boolean, ArraySet<String>> approvedByType = mApproved.get(userId);
+ if (approvedByType == null) {
+ return;
+ }
+ for (int i = 0; i < approvedByType.size(); i++) {
+ final ArraySet<String> approved = approvedByType.valueAt(i);
+ for (int j = approved.size() - 1; j >= 0; j--) {
+ final String approvedPackageOrComponent = approved.valueAt(j);
+ if (TextUtils.equals(getPackageName(approvedPackageOrComponent), packageName)) {
+ final ComponentName component = ComponentName.unflattenFromString(
+ approvedPackageOrComponent);
+ if (component != null && !componentHasBindPermission(component, userId)) {
+ approved.removeAt(j);
+ if (DEBUG) {
+ Slog.v(TAG, "Removing " + approvedPackageOrComponent
+ + " from approved list; no bind permission found "
+ + mConfig.bindPermission);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
protected String getPackageName(String packageOrComponent) {
final ComponentName component = ComponentName.unflattenFromString(packageOrComponent);
if (component != null) {
@@ -1317,10 +1337,7 @@ protected void populateComponentsToBind(SparseArray<Set<ComponentName>> componen
}
final Set<ComponentName> add = new HashSet<>(userComponents);
- ArraySet<ComponentName> snoozed = mSnoozing.get(userId);
- if (snoozed != null) {
- add.removeAll(snoozed);
- }
+ add.removeAll(mSnoozingForCurrentProfiles);
componentsToBind.put(userId, add);
@@ -1438,28 +1455,20 @@ private void bindToServices(SparseArray<Set<ComponentName>> componentsToBind) {
final int userId = componentsToBind.keyAt(i);
final Set<ComponentName> add = componentsToBind.get(userId);
for (ComponentName component : add) {
- try {
- ServiceInfo info = mPm.getServiceInfo(component,
- PackageManager.GET_META_DATA
- | PackageManager.MATCH_DIRECT_BOOT_AWARE
- | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
- userId);
- if (info == null) {
- Slog.w(TAG, "Not binding " + getCaption() + " service " + component
- + ": service not found");
- continue;
- }
- if (!mConfig.bindPermission.equals(info.permission)) {
- Slog.w(TAG, "Not binding " + getCaption() + " service " + component
- + ": it does not require the permission " + mConfig.bindPermission);
- continue;
- }
- Slog.v(TAG,
- "enabling " + getCaption() + " for " + userId + ": " + component);
- registerService(info, userId);
- } catch (RemoteException e) {
- e.rethrowFromSystemServer();
+ ServiceInfo info = getServiceInfo(component, userId);
+ if (info == null) {
+ Slog.w(TAG, "Not binding " + getCaption() + " service " + component
+ + ": service not found");
+ continue;
}
+ if (!mConfig.bindPermission.equals(info.permission)) {
+ Slog.w(TAG, "Not binding " + getCaption() + " service " + component
+ + ": it does not require the permission " + mConfig.bindPermission);
+ continue;
+ }
+ Slog.v(TAG,
+ "enabling " + getCaption() + " for " + userId + ": " + component);
+ registerService(info, userId);
}
}
}
@@ -1484,8 +1493,7 @@ void registerService(final ComponentName cn, final int userId) {
void reregisterService(final ComponentName cn, final int userId) {
// If rebinding a package that died, ensure it still has permission
// after the rebind delay
- if (isPackageOrComponentAllowed(cn.getPackageName(), userId)
- || isPackageOrComponentAllowed(cn.flattenToString(), userId)) {
+ if (isPackageOrComponentAllowedWithPermission(cn, userId)) {
registerService(cn, userId);
}
}
@@ -1499,12 +1507,10 @@ public void registerSystemService(final ComponentName name, final int userid) {
}
}
- @GuardedBy("mMutex")
private void registerServiceLocked(final ComponentName name, final int userid) {
registerServiceLocked(name, userid, false /* isSystem */);
}
- @GuardedBy("mMutex")
private void registerServiceLocked(final ComponentName name, final int userid,
final boolean isSystem) {
if (DEBUG) Slog.v(TAG, "registerService: " + name + " u=" + userid);
@@ -1590,9 +1596,12 @@ public void onBindingDied(ComponentName name) {
unbindService(this, name, userid);
if (!mServicesRebinding.contains(servicesBindingTag)) {
mServicesRebinding.add(servicesBindingTag);
- mHandler.postDelayed(() ->
- reregisterService(name, userid),
- ON_BINDING_DIED_REBIND_DELAY_MS);
+ mHandler.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ reregisterService(name, userid);
+ }
+ }, ON_BINDING_DIED_REBIND_DELAY_MS);
} else {
Slog.v(TAG, getCaption() + " not rebinding in user " + userid
+ " as a previous rebind attempt was made: " + name);
@@ -1635,7 +1644,6 @@ private void unregisterService(ComponentName name, int userid) {
}
}
- @GuardedBy("mMutex")
private void unregisterServiceLocked(ComponentName name, int userid) {
final int N = mServices.size();
for (int i = N - 1; i >= 0; i--) {
@@ -1670,7 +1678,6 @@ private ManagedServiceInfo removeServiceImpl(IInterface service, final int useri
return serviceInfo;
}
- @GuardedBy("mMutex")
private ManagedServiceInfo removeServiceLocked(int i) {
final ManagedServiceInfo info = mServices.remove(i);
onServiceRemovedLocked(info);
@@ -1724,6 +1731,19 @@ private void unbindService(ServiceConnection connection, ComponentName component
}
}
+ private ServiceInfo getServiceInfo(ComponentName component, int userId) {
+ try {
+ return mPm.getServiceInfo(component,
+ PackageManager.GET_META_DATA
+ | PackageManager.MATCH_DIRECT_BOOT_AWARE
+ | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
+ userId);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ return null;
+ }
+
public class ManagedServiceInfo implements IBinder.DeathRecipient {
public IInterface service;
public ComponentName component;
@@ -1823,7 +1843,7 @@ public boolean isEnabledForCurrentProfiles() {
* from receiving events from the profile.
*/
public boolean isPermittedForProfile(int userId) {
- if (!mUserProfiles.isProfileUser(userId)) {
+ if (!mUserProfiles.isManagedProfile(userId)) {
return true;
}
DevicePolicyManager dpm =
@@ -1899,6 +1919,13 @@ public boolean isCurrentProfile(int userId) {
}
}
+ public boolean isManagedProfile(int userId) {
+ synchronized (mCurrentProfiles) {
+ UserInfo user = mCurrentProfiles.get(userId);
+ return user != null && user.isManagedProfile();
+ }
+ }
+
public boolean isProfileUser(int userId) {
synchronized (mCurrentProfiles) {
UserInfo user = mCurrentProfiles.get(userId);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
index 8b1384ed894f..7c7bb509ebb4 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
@@ -33,8 +33,10 @@
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyLong;
import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -1117,6 +1119,58 @@ public void testUpgradeAppBindsNewServices() throws Exception {
}
}
+ @Test
+ public void testUpgradeAppNoPermissionNoRebind() throws Exception {
+ Context context = spy(getContext());
+ doReturn(true).when(context).bindServiceAsUser(any(), any(), anyInt(), any());
+
+ ManagedServices service = new TestManagedServices(context, mLock, mUserProfiles,
+ mIpm,
+ APPROVAL_BY_COMPONENT);
+
+ List<String> packages = new ArrayList<>();
+ packages.add("package");
+ addExpectedServices(service, packages, 0);
+
+ final ComponentName unapprovedComponent = ComponentName.unflattenFromString("package/C1");
+ final ComponentName approvedComponent = ComponentName.unflattenFromString("package/C2");
+
+ // Both components are approved initially
+ mExpectedPrimaryComponentNames.clear();
+ mExpectedPrimaryPackages.clear();
+ mExpectedPrimaryComponentNames.put(0, "package/C1:package/C2");
+ mExpectedSecondaryComponentNames.clear();
+ mExpectedSecondaryPackages.clear();
+
+ loadXml(service);
+
+ //Component package/C1 loses bind permission
+ when(mIpm.getServiceInfo(any(), anyLong(), anyInt())).thenAnswer(
+ (Answer<ServiceInfo>) invocation -> {
+ ComponentName invocationCn = invocation.getArgument(0);
+ if (invocationCn != null) {
+ ServiceInfo serviceInfo = new ServiceInfo();
+ serviceInfo.packageName = invocationCn.getPackageName();
+ serviceInfo.name = invocationCn.getClassName();
+ if (invocationCn.equals(unapprovedComponent)) {
+ serviceInfo.permission = "none";
+ } else {
+ serviceInfo.permission = service.getConfig().bindPermission;
+ }
+ serviceInfo.metaData = null;
+ return serviceInfo;
+ }
+ return null;
+ }
+ );
+
+ // Trigger package update
+ service.onPackagesChanged(false, new String[]{"package"}, new int[]{0});
+
+ assertFalse(service.isComponentEnabledForCurrentProfiles(unapprovedComponent));
+ assertTrue(service.isComponentEnabledForCurrentProfiles(approvedComponent));
+ }
+
@Test
public void testSetPackageOrComponentEnabled() throws Exception {
for (int approvalLevel : new int[] {APPROVAL_BY_COMPONENT, APPROVAL_BY_PACKAGE}) {