diff options
| author | 2024-10-14 09:46:13 +0000 | |
|---|---|---|
| committer | 2024-10-14 09:46:13 +0000 | |
| commit | b8bd4a55cfbffd80972d0d8be94d82aa582bd445 (patch) | |
| tree | a4f6659ba3223441c7904276486a553a2240edc9 | |
| parent | 249f87d7838cfb32d8abce8c6097e37b911de12a (diff) | |
| parent | 1d5156d9cc4d8d64f331122ef2336752e263cce5 (diff) | |
Merge "Fix EventConditionProvider for secondary users (and HSUM)" into main
10 files changed, 187 insertions, 24 deletions
diff --git a/core/java/android/app/notification.aconfig b/core/java/android/app/notification.aconfig index 8014537e8838..4ec869a143c3 100644 --- a/core/java/android/app/notification.aconfig +++ b/core/java/android/app/notification.aconfig @@ -48,6 +48,16 @@ flag { } flag { + name: "modes_hsum" + namespace: "systemui" + description: "Fixes for modes (and DND/Zen in general) with HSUM or secondary users" + bug: "366203070" + metadata { + purpose: PURPOSE_BUGFIX + } +} + +flag { name: "api_tvextender" is_exported: true namespace: "systemui" diff --git a/services/core/java/com/android/server/notification/CalendarTracker.java b/services/core/java/com/android/server/notification/CalendarTracker.java index cfcf6ebf9540..f186f4273f61 100644 --- a/services/core/java/com/android/server/notification/CalendarTracker.java +++ b/services/core/java/com/android/server/notification/CalendarTracker.java @@ -32,6 +32,8 @@ import android.util.ArraySet; import android.util.Log; import android.util.Slog; +import com.android.internal.annotations.VisibleForTesting; + import java.io.PrintWriter; import java.util.Date; import java.util.Objects; @@ -296,4 +298,8 @@ public class CalendarTracker { void onChanged(); } + @VisibleForTesting // (otherwise = NONE) + public int getUserId() { + return mUserContext.getUserId(); + } } diff --git a/services/core/java/com/android/server/notification/ConditionProviders.java b/services/core/java/com/android/server/notification/ConditionProviders.java index 66e61c076030..0b40d64e3a09 100644 --- a/services/core/java/com/android/server/notification/ConditionProviders.java +++ b/services/core/java/com/android/server/notification/ConditionProviders.java @@ -169,16 +169,15 @@ public class ConditionProviders extends ManagedServices { for (int i = 0; i < mSystemConditionProviders.size(); i++) { mSystemConditionProviders.valueAt(i).onBootComplete(); } - if (mCallback != null) { - mCallback.onBootComplete(); - } } @Override public void onUserSwitched(int user) { super.onUserSwitched(user); - if (mCallback != null) { - mCallback.onUserSwitched(); + if (android.app.Flags.modesHsum()) { + for (int i = 0; i < mSystemConditionProviders.size(); i++) { + mSystemConditionProviders.valueAt(i).onUserSwitched(UserHandle.of(user)); + } } } @@ -515,10 +514,8 @@ public class ConditionProviders extends ManagedServices { } public interface Callback { - void onBootComplete(); void onServiceAdded(ComponentName component); void onConditionChanged(Uri id, Condition condition); - void onUserSwitched(); } } diff --git a/services/core/java/com/android/server/notification/CountdownConditionProvider.java b/services/core/java/com/android/server/notification/CountdownConditionProvider.java index c3ace15a8c0f..bb8ccd20f9ab 100644 --- a/services/core/java/com/android/server/notification/CountdownConditionProvider.java +++ b/services/core/java/com/android/server/notification/CountdownConditionProvider.java @@ -23,6 +23,7 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.net.Uri; +import android.os.UserHandle; import android.service.notification.Condition; import android.service.notification.ZenModeConfig; import android.text.format.DateUtils; @@ -65,6 +66,11 @@ public class CountdownConditionProvider extends SystemConditionProviderService { } @Override + public void onUserSwitched(UserHandle user) { + // Nothing to do because countdown conditions are not tied to any user data. + } + + @Override public void dump(PrintWriter pw, DumpFilter filter) { pw.println(" CountdownConditionProvider:"); pw.print(" mConnected="); pw.println(mConnected); diff --git a/services/core/java/com/android/server/notification/CustomManualConditionProvider.java b/services/core/java/com/android/server/notification/CustomManualConditionProvider.java index 9531c5e58851..52dc116c699d 100644 --- a/services/core/java/com/android/server/notification/CustomManualConditionProvider.java +++ b/services/core/java/com/android/server/notification/CustomManualConditionProvider.java @@ -17,6 +17,7 @@ package com.android.server.notification; import android.net.Uri; +import android.os.UserHandle; import android.service.notification.ZenModeConfig; import java.io.PrintWriter; @@ -40,6 +41,11 @@ public class CustomManualConditionProvider extends SystemConditionProviderServic } @Override + public void onUserSwitched(UserHandle user) { + // Nothing to do because we won't ever call notifyConditions. + } + + @Override public void onConnected() { // No need to keep subscriptions because we won't ever call notifyConditions } diff --git a/services/core/java/com/android/server/notification/EventConditionProvider.java b/services/core/java/com/android/server/notification/EventConditionProvider.java index 00dd547f6c4e..ecc4cf72a1c3 100644 --- a/services/core/java/com/android/server/notification/EventConditionProvider.java +++ b/services/core/java/com/android/server/notification/EventConditionProvider.java @@ -16,6 +16,7 @@ package com.android.server.notification; +import android.annotation.Nullable; import android.app.AlarmManager; import android.app.PendingIntent; import android.content.BroadcastReceiver; @@ -23,6 +24,7 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageManager.NameNotFoundException; +import android.content.pm.UserInfo; import android.net.Uri; import android.os.Handler; import android.os.HandlerThread; @@ -37,6 +39,7 @@ import android.util.Log; import android.util.Slog; import android.util.SparseArray; +import com.android.internal.annotations.VisibleForTesting; import com.android.server.notification.CalendarTracker.CheckEventResult; import com.android.server.notification.NotificationManagerService.DumpFilter; import com.android.server.pm.PackageManagerService; @@ -59,12 +62,13 @@ public class EventConditionProvider extends SystemConditionProviderService { private static final String EXTRA_TIME = "time"; private static final long CHANGE_DELAY = 2 * 1000; // coalesce chatty calendar changes - private final Context mContext = this; + @VisibleForTesting Context mContext = this; private final ArraySet<Uri> mSubscriptions = new ArraySet<Uri>(); private final SparseArray<CalendarTracker> mTrackers = new SparseArray<>(); private final Handler mWorker; private final HandlerThread mThread; + @Nullable private UserHandle mCurrentUser; private boolean mConnected; private boolean mRegistered; private boolean mBootComplete; // don't hammer the calendar provider until boot completes. @@ -114,10 +118,28 @@ public class EventConditionProvider extends SystemConditionProviderService { mContext.registerReceiver(new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { - reloadTrackers(); + if (android.app.Flags.modesHsum()) { + if (mCurrentUser != null) { + // Possibly the intent signals a profile added on a different user, but it + // doesn't matter (except for a bit of wasted work here). We will reload + // trackers for that user when we switch. + reloadTrackers(mCurrentUser); + } + } else { + reloadTrackers(); + } } }, filter); - reloadTrackers(); + if (!android.app.Flags.modesHsum()) { + reloadTrackers(); + } + } + + @Override + public void onUserSwitched(UserHandle user) { + if (DEBUG) Slog.d(TAG, "onUserSwitched: " + user); + mCurrentUser = user; + reloadTrackers(user); } @Override @@ -157,8 +179,41 @@ public class EventConditionProvider extends SystemConditionProviderService { } } + private void reloadTrackers(UserHandle user) { + if (DEBUG) Slog.d(TAG, "reloadTrackers user=" + user); + for (int i = 0; i < mTrackers.size(); i++) { + mTrackers.valueAt(i).setCallback(null); + } + mTrackers.clear(); + + // Ensure that user is the main user and not a profile. + UserManager userManager = UserManager.get(mContext); + UserHandle possibleParent = userManager.getProfileParent(user); + if (possibleParent != null) { + Slog.wtf(TAG, "reloadTrackers should not be called with profile " + user + + "; continuing with parent " + possibleParent); + user = possibleParent; + } + + for (UserInfo profile : userManager.getProfiles(user.getIdentifier())) { + final Context profileContext = getContextForUser(mContext, profile.getUserHandle()); + if (profileContext == null) { + Slog.w(TAG, "Unable to create context for profile " + profile.id + " of user " + + user.getIdentifier()); + continue; + } + mTrackers.put(profile.id, new CalendarTracker(mContext, profileContext)); + } + evaluateSubscriptions(); + } + + @Deprecated // Remove when inlining MODES_HSUM private void reloadTrackers() { if (DEBUG) Slog.d(TAG, "reloadTrackers"); + if (android.app.Flags.modesHsum()) { + Slog.wtf(TAG, "Shouldn't call reloadTrackers() without user in MODES_HSUM", + new Exception()); + } for (int i = 0; i < mTrackers.size(); i++) { mTrackers.valueAt(i).setCallback(null); } @@ -325,4 +380,9 @@ public class EventConditionProvider extends SystemConditionProviderService { evaluateSubscriptionsW(); } }; + + @VisibleForTesting // (otherwise = NONE) + public SparseArray<CalendarTracker> getTrackers() { + return mTrackers; + } } diff --git a/services/core/java/com/android/server/notification/ScheduleConditionProvider.java b/services/core/java/com/android/server/notification/ScheduleConditionProvider.java index 6efe88f6a155..24b090c6ffab 100644 --- a/services/core/java/com/android/server/notification/ScheduleConditionProvider.java +++ b/services/core/java/com/android/server/notification/ScheduleConditionProvider.java @@ -25,6 +25,7 @@ import android.content.Intent; import android.content.IntentFilter; import android.net.Uri; import android.os.Binder; +import android.os.UserHandle; import android.provider.Settings; import android.service.notification.Condition; import android.service.notification.ScheduleCalendar; @@ -115,6 +116,11 @@ public class ScheduleConditionProvider extends SystemConditionProviderService { } @Override + public void onUserSwitched(UserHandle user) { + // Nothing to do because time-based schedules are not tied to any user data. + } + + @Override public void onDestroy() { super.onDestroy(); if (DEBUG) Slog.d(TAG, "onDestroy"); diff --git a/services/core/java/com/android/server/notification/SystemConditionProviderService.java b/services/core/java/com/android/server/notification/SystemConditionProviderService.java index 97073b77f1f7..656f9dfd0917 100644 --- a/services/core/java/com/android/server/notification/SystemConditionProviderService.java +++ b/services/core/java/com/android/server/notification/SystemConditionProviderService.java @@ -19,6 +19,7 @@ package com.android.server.notification; import android.content.ComponentName; import android.content.Context; import android.net.Uri; +import android.os.UserHandle; import android.service.notification.ConditionProviderService; import android.service.notification.IConditionProvider; import android.util.TimeUtils; @@ -30,9 +31,10 @@ import java.util.Date; public abstract class SystemConditionProviderService extends ConditionProviderService { - abstract public void dump(PrintWriter pw, DumpFilter filter); - abstract public boolean isValidConditionId(Uri id); - abstract public void onBootComplete(); + public abstract void dump(PrintWriter pw, DumpFilter filter); + public abstract boolean isValidConditionId(Uri id); + public abstract void onBootComplete(); + public abstract void onUserSwitched(UserHandle user); final ComponentName getComponent() { return new ComponentName("android", this.getClass().getName()); diff --git a/services/core/java/com/android/server/notification/ZenModeConditions.java b/services/core/java/com/android/server/notification/ZenModeConditions.java index 50bfbc3530a9..b1f010c38d4e 100644 --- a/services/core/java/com/android/server/notification/ZenModeConditions.java +++ b/services/core/java/com/android/server/notification/ZenModeConditions.java @@ -102,16 +102,6 @@ public class ZenModeConditions implements ConditionProviders.Callback { } @Override - public void onBootComplete() { - // noop - } - - @Override - public void onUserSwitched() { - // noop - } - - @Override public void onServiceAdded(ComponentName component) { if (DEBUG) Log.d(TAG, "onServiceAdded " + component); final int callingUid = Binder.getCallingUid(); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/EventConditionProviderTest.java b/services/tests/uiservicestests/src/com/android/server/notification/EventConditionProviderTest.java index 4af96ef4800f..05210aca19dd 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/EventConditionProviderTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/EventConditionProviderTest.java @@ -19,13 +19,25 @@ package com.android.server.notification; import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; import android.app.Application; import android.app.PendingIntent; import android.content.ComponentName; +import android.content.Context; import android.content.Intent; +import android.content.pm.UserInfo; +import android.os.UserHandle; +import android.os.UserManager; +import android.platform.test.annotations.DisableFlags; +import android.platform.test.annotations.EnableFlags; +import android.platform.test.flag.junit.SetFlagsRule; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper.RunWithLooper; @@ -35,16 +47,24 @@ import com.android.server.UiServiceTestCase; import com.android.server.pm.PackageManagerService; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import org.mockito.stubbing.Answer; + +import java.util.List; @RunWith(AndroidTestingRunner.class) @SmallTest @RunWithLooper public class EventConditionProviderTest extends UiServiceTestCase { + @Rule + public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); - EventConditionProvider mService; + private EventConditionProvider mService; + @Mock private UserManager mUserManager; @Before public void setUp() throws Exception { @@ -65,6 +85,18 @@ public class EventConditionProviderTest extends UiServiceTestCase { service.onCreate(); service.onBind(startIntent); mService = spy(service); + mService.mContext = this.getContext(); + + mContext.addMockSystemService(UserManager.class, mUserManager); + when(mUserManager.getProfiles(eq(mUserId))).thenReturn( + List.of(new UserInfo(mUserId, "mUserId", 0))); + + doAnswer((Answer<Context>) invocationOnMock -> { + Context mockUserContext = mock(Context.class); + UserHandle userHandle = invocationOnMock.getArgument(2); + when(mockUserContext.getUserId()).thenReturn(userHandle.getIdentifier()); + return mockUserContext; + }).when(mContext).createPackageContextAsUser(any(), anyInt(), any()); } @Test @@ -78,4 +110,52 @@ public class EventConditionProviderTest extends UiServiceTestCase { PendingIntent pi = mService.getPendingIntent(1000); assertEquals(PackageManagerService.PLATFORM_PACKAGE_NAME, pi.getIntent().getPackage()); } + + @Test + @DisableFlags(android.app.Flags.FLAG_MODES_HSUM) + public void onBootComplete_flagOff_loadsTrackers() { + when(mUserManager.getUserProfiles()).thenReturn(List.of(UserHandle.of(mUserId))); + + mService.onBootComplete(); + + assertThat(mService.getTrackers().size()).isEqualTo(1); + assertThat(mService.getTrackers().keyAt(0)).isEqualTo(mUserId); + } + + @Test + @EnableFlags(android.app.Flags.FLAG_MODES_HSUM) + public void onBootComplete_waitsForUserSwitched() { + mService.onBootComplete(); + assertThat(mService.getTrackers().size()).isEqualTo(0); + } + + @Test + @EnableFlags(android.app.Flags.FLAG_MODES_HSUM) + public void onUserSwitched_reloadsTrackers() { + UserHandle someUser = UserHandle.of(42); + when(mUserManager.getProfiles(eq(42))).thenReturn(List.of(new UserInfo(42, "user 42", 0))); + + mService.onUserSwitched(someUser); + + assertThat(mService.getTrackers().size()).isEqualTo(1); + assertThat(mService.getTrackers().keyAt(0)).isEqualTo(42); + assertThat(mService.getTrackers().valueAt(0).getUserId()).isEqualTo(42); + } + + @Test + @EnableFlags(android.app.Flags.FLAG_MODES_HSUM) + public void onUserSwitched_reloadsTrackersIncludingProfiles() { + UserHandle anotherUser = UserHandle.of(42); + when(mUserManager.getProfiles(eq(42))).thenReturn(List.of( + new UserInfo(42, "user 42", 0), + new UserInfo(43, "profile 43", 0))); + + mService.onUserSwitched(anotherUser); + + assertThat(mService.getTrackers().size()).isEqualTo(2); + assertThat(mService.getTrackers().keyAt(0)).isEqualTo(42); + assertThat(mService.getTrackers().valueAt(0).getUserId()).isEqualTo(42); + assertThat(mService.getTrackers().keyAt(1)).isEqualTo(43); + assertThat(mService.getTrackers().valueAt(1).getUserId()).isEqualTo(43); + } } |