Loading core/java/android/app/IUiModeManager.aidl +11 −6 Original line number Diff line number Diff line Loading @@ -24,10 +24,16 @@ import android.app.IUiModeManagerCallback; * @hide */ interface IUiModeManager { /** * @hide */ void addCallback(IUiModeManagerCallback callback, int userId); /** * @hide */ void addCallback(IUiModeManagerCallback callback); void removeCallback(IUiModeManagerCallback callback, int userId); /** * Enables the car mode. Only the system can do this. Loading Loading @@ -215,15 +221,14 @@ interface IUiModeManager { int getActiveProjectionTypes(); /** * Returns the contrast for the current user. * Returns the contrast for the given user. */ float getContrast(); float getContrast(int userId); /** * Returns the force invert state. * Returns the force invert state for the given user. * * @hide */ int getForceInvertState(); int getForceInvertState(int userId); } core/java/android/app/UiModeManager.java +224 −17 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ package android.app; import static android.app.Flags.enableCurrentModeTypeBinderCache; import static android.app.Flags.enableNightModeBinderCache; import static android.app.Flags.fixContrastAndForceInvertStateForMultiUser; import android.annotation.CallbackExecutor; import android.annotation.FlaggedApi; Loading @@ -38,10 +39,12 @@ import android.os.IpcDataCache; import android.os.RemoteException; import android.os.ServiceManager; import android.os.ServiceManager.ServiceNotFoundException; import android.os.UserHandle; import android.util.ArrayMap; import android.util.ArraySet; import android.util.Log; import android.util.Slog; import android.util.SparseArray; import com.android.internal.annotations.GuardedBy; import com.android.internal.util.function.pooled.PooledLambda; Loading Loading @@ -86,7 +89,6 @@ public class UiModeManager { private static final String TAG = "UiModeManager"; /** * A listener with a single method that is invoked whenever the packages projecting using the * {@link ProjectionType}s for which it is registered change. Loading Loading @@ -427,7 +429,7 @@ public class UiModeManager { * Context required for getting the opPackageName of API caller; maybe be {@code null} if the * old constructor marked with UnSupportedAppUsage is used. */ private @Nullable Context mContext; private final @Nullable Context mContext; private final Object mLock = new Object(); /** Loading @@ -452,6 +454,8 @@ public class UiModeManager { private final IUiModeManager mService; private final Object mGlobalsLock = new Object(); // ============= Legacy values and methods ============= // // TODO(b/362682063) remove when cleaning up the flag @ForceInvertType private int mForceInvertState = FORCE_INVERT_TYPE_OFF; private float mContrast = ContrastUtils.CONTRAST_DEFAULT_VALUE; Loading @@ -466,17 +470,6 @@ public class UiModeManager { private final ArrayMap<ForceInvertStateChangeListener, Executor> mForceInvertStateChangeListeners = new ArrayMap<>(); Globals(IUiModeManager service) { mService = service; try { mService.addCallback(this); mContrast = mService.getContrast(); mForceInvertState = mService.getForceInvertState(); } catch (RemoteException e) { Log.e(TAG, "Setup failed: UiModeManagerService is dead", e); } } @ForceInvertType private int getForceInvertState() { synchronized (mGlobalsLock) { Loading Loading @@ -548,6 +541,190 @@ public class UiModeManager { () -> listener.onContrastChanged(contrast))); } } // ============= End legacy values and methods ============= // /** * Map of {@link UserCallback} per user id. This will only contain one value for the current * user, unless the process using this service interacts across users. */ private final SparseArray<UserCallback> mUserCallbacks = new SparseArray<>(); Globals(IUiModeManager service) { mService = service; if (fixContrastAndForceInvertStateForMultiUser()) return; try { mService.addCallback(this, UserHandle.USER_NULL); mContrast = mService.getContrast(UserHandle.USER_NULL); mForceInvertState = mService.getForceInvertState(UserHandle.USER_NULL); } catch (RemoteException e) { Log.e(TAG, "Setup failed: UiModeManagerService is dead", e); } } private UserCallback getUserCallbackOrCreate(int userId) { UserCallback userCallback = mUserCallbacks.get(userId); if (userCallback == null) { try { userCallback = new UserCallback(userId); mService.addCallback(userCallback, userId); mUserCallbacks.put(userId, userCallback); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } return userCallback; } private void removeCallbackIfUnusedLocked(int userId) { UserCallback userCallback = mUserCallbacks.get(userId); if (userCallback == null || !userCallback.mContrastChangeListeners.isEmpty() || !userCallback.mForceInvertStateChangeListeners.isEmpty()) { return; } try { mService.removeCallback(userCallback, userId); mUserCallbacks.remove(userId); } catch (RemoteException e) { Log.e(TAG, "Failed to remove UiModeManagerCallback", e); } } @ForceInvertType private int getForceInvertState(int userId) { synchronized (mGlobalsLock) { UserCallback userCallback = mUserCallbacks.get(userId); if (userCallback != null) { return userCallback.mForceInvertState; } try { return mService.getForceInvertState(userId); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } } private void addForceInvertStateChangeListener(ForceInvertStateChangeListener listener, Executor executor, int userId) { synchronized (mGlobalsLock) { UserCallback userCallback = getUserCallbackOrCreate(userId); userCallback.mForceInvertStateChangeListeners.put(listener, executor); } } private void removeForceInvertStateChangeListener(ForceInvertStateChangeListener listener, int userId) { synchronized (mGlobalsLock) { UserCallback userCallback = mUserCallbacks.get(userId); if (userCallback != null) { userCallback.mForceInvertStateChangeListeners.remove(listener); removeCallbackIfUnusedLocked(userId); } } } private float getContrast(int userId) { synchronized (mGlobalsLock) { UserCallback userCallback = mUserCallbacks.get(userId); if (userCallback != null) { return userCallback.mContrast; } try { return mService.getContrast(userId); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } } private void addContrastChangeListener(ContrastChangeListener listener, Executor executor, int userId) { synchronized (mGlobalsLock) { UserCallback userCallback = getUserCallbackOrCreate(userId); userCallback.mContrastChangeListeners.put(listener, executor); } } private void removeContrastChangeListener(ContrastChangeListener listener, int userId) { synchronized (mGlobalsLock) { UserCallback userCallback = mUserCallbacks.get(userId); if (userCallback != null) { userCallback.mContrastChangeListeners.remove(listener); removeCallbackIfUnusedLocked(userId); } } } } /** Global class storing all listeners and cached values for a specific user id. */ private static class UserCallback extends IUiModeManagerCallback.Stub { private UserCallback(int userId) { try { sGlobals.mService.addCallback(this, userId); mContrast = sGlobals.mService.getContrast(userId); mForceInvertState = sGlobals.mService.getForceInvertState(userId); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** Cached contrast value */ private float mContrast = ContrastUtils.CONTRAST_DEFAULT_VALUE; /** Cached force invert state value */ @ForceInvertType private int mForceInvertState = FORCE_INVERT_TYPE_OFF; /** * Map that stores user provided {@link ContrastChangeListener} callbacks, * and the executors on which these callbacks should be called. */ private final ArrayMap<ContrastChangeListener, Executor> mContrastChangeListeners = new ArrayMap<>(); private final ArrayMap<ForceInvertStateChangeListener, Executor> mForceInvertStateChangeListeners = new ArrayMap<>(); @Override public void notifyContrastChanged(float contrast) throws RemoteException { synchronized (sGlobals.mGlobalsLock) { // if value changed in the settings, update the cached value and notify listeners Float previousContrast = mContrast; if (Math.abs(previousContrast - contrast) < 1e-10) { return; } mContrast = contrast; mContrastChangeListeners.forEach((listener, executor) -> executor.execute(() -> listener.onContrastChanged(contrast))); } } @Override public void notifyForceInvertStateChanged(@ForceInvertType int forceInvertState) throws RemoteException { final Map<ForceInvertStateChangeListener, Executor> listeners = new ArrayMap<>(); synchronized (sGlobals.mGlobalsLock) { // if value changed in the settings, update the cached value and notify listeners if (mForceInvertState == forceInvertState) { return; } mForceInvertState = forceInvertState; listeners.putAll(mForceInvertStateChangeListeners); } listeners.forEach((listener, executor) -> { final long token = Binder.clearCallingIdentity(); try { executor.execute(() -> listener.onForceInvertStateChanged(forceInvertState)); } finally { Binder.restoreCallingIdentity(token); } }); } } /** Loading Loading @@ -1520,11 +1697,14 @@ public class UiModeManager { */ @FloatRange(from = -1.0f, to = 1.0f) public float getContrast() { if (fixContrastAndForceInvertStateForMultiUser()) { return sGlobals.getContrast(getUserId()); } return sGlobals.getContrast(); } /** * Registers a {@link ContrastChangeListener} for the current user. * Registers a {@link ContrastChangeListener} for the user. * * @param executor The executor on which the listener should be called back. * @param listener The listener. Loading @@ -1534,17 +1714,25 @@ public class UiModeManager { @NonNull ContrastChangeListener listener) { Objects.requireNonNull(executor); Objects.requireNonNull(listener); if (fixContrastAndForceInvertStateForMultiUser()) { sGlobals.addContrastChangeListener(listener, executor, getUserId()); return; } sGlobals.addContrastChangeListener(listener, executor); } /** * Unregisters a {@link ContrastChangeListener} for the current user. * Unregisters a {@link ContrastChangeListener} for the user. * If the listener was not registered, does nothing and returns. * * @param listener The listener to unregister. */ public void removeContrastChangeListener(@NonNull ContrastChangeListener listener) { Objects.requireNonNull(listener); if (fixContrastAndForceInvertStateForMultiUser()) { sGlobals.removeContrastChangeListener(listener, getUserId()); return; } sGlobals.removeContrastChangeListener(listener); } Loading @@ -1557,6 +1745,9 @@ public class UiModeManager { @FlaggedApi(android.view.accessibility.Flags.FLAG_FORCE_INVERT_COLOR) @ForceInvertType public int getForceInvertState() { if (fixContrastAndForceInvertStateForMultiUser()) { return sGlobals.getForceInvertState(getUserId()); } return sGlobals.getForceInvertState(); } Loading @@ -1577,7 +1768,7 @@ public class UiModeManager { } /** * Registers a {@link ForceInvertStateChangeListener} for the current user. * Registers a {@link ForceInvertStateChangeListener} for the user. * * @param executor The executor on which the listener should be called back. * @param listener The listener. Loading @@ -1589,11 +1780,15 @@ public class UiModeManager { @NonNull ForceInvertStateChangeListener listener) { Objects.requireNonNull(executor); Objects.requireNonNull(listener); if (fixContrastAndForceInvertStateForMultiUser()) { sGlobals.addForceInvertStateChangeListener(listener, executor, getUserId()); return; } sGlobals.addForceInvertStateChangeListener(listener, executor); } /** * Unregisters a {@link ForceInvertStateChangeListener} for the current user. * Unregisters a {@link ForceInvertStateChangeListener} for the user. * If the listener was not registered, does nothing and returns. * * @param listener The listener to unregister. Loading @@ -1603,6 +1798,18 @@ public class UiModeManager { public void removeForceInvertStateChangeListener( @NonNull ForceInvertStateChangeListener listener) { Objects.requireNonNull(listener); if (fixContrastAndForceInvertStateForMultiUser()) { sGlobals.removeForceInvertStateChangeListener(listener, getUserId()); return; } sGlobals.removeForceInvertStateChangeListener(listener); } /** * Return the context user id. If this class was built with the UnsupportedAppUsage constructor * and the context is null, return the user id of this process instead. */ private int getUserId() { return mContext != null ? mContext.getUserId() : UserHandle.myUserId(); } } core/java/android/app/ui_mode_manager.aconfig +10 −0 Original line number Diff line number Diff line Loading @@ -21,3 +21,13 @@ flag { purpose: PURPOSE_BUGFIX } } flag { namespace: "systemui" name: "fix_contrast_and_force_invert_state_for_multi_user" description: "Fixes contrast and force invert state APIs for multi user and HSUM cases." bug: "362682063" metadata { purpose: PURPOSE_BUGFIX } } No newline at end of file services/core/java/com/android/server/UiModeManagerService.java +147 −16 File changed.Preview size limit exceeded, changes collapsed. Show changes services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java +65 −7 Original line number Diff line number Diff line Loading @@ -91,6 +91,7 @@ import android.os.Process; import android.os.RemoteException; import android.os.UserHandle; import android.os.test.FakePermissionEnforcer; import android.platform.test.annotations.DisableFlags; import android.platform.test.annotations.EnableFlags; import android.platform.test.flag.junit.SetFlagsRule; import android.provider.Settings; Loading Loading @@ -405,9 +406,7 @@ public class UiModeManagerServiceTest extends UiServiceTestCase { @Test public void setNightModeActivated_permissionToChangeOtherUsers() throws RemoteException { SystemService.TargetUser user = mock(SystemService.TargetUser.class); doReturn(9).when(user).getUserIdentifier(); mUiManagerService.onUserSwitching(user, user); switchUser(9); when(mContext.checkCallingOrSelfPermission( eq(Manifest.permission.INTERACT_ACROSS_USERS))) .thenReturn(PackageManager.PERMISSION_DENIED); Loading Loading @@ -1575,16 +1574,31 @@ public class UiModeManagerServiceTest extends UiServiceTestCase { @Test @EnableFlags(android.view.accessibility.Flags.FLAG_FORCE_INVERT_COLOR) public void getForceInvertState_nightModeFalse_returnsOff() throws RemoteException { @DisableFlags(android.app.Flags.FLAG_FIX_CONTRAST_AND_FORCE_INVERT_STATE_FOR_MULTI_USER) public void getForceInvertState_nightModeFalse_returnsOff_legacy() throws RemoteException { mService.setNightModeActivated(false); assertThat(mUiManagerService.getForceInvertStateInternal()) .isEqualTo(FORCE_INVERT_TYPE_OFF); } @Test @EnableFlags({ android.view.accessibility.Flags.FLAG_FORCE_INVERT_COLOR, android.app.Flags.FLAG_FIX_CONTRAST_AND_FORCE_INVERT_STATE_FOR_MULTI_USER }) public void getForceInvertState_nightModeFalse_returnsOff() throws RemoteException { int testUserId = 9; switchUser(testUserId); mService.setNightModeActivated(false); assertThat(mUiManagerService.getForceInvertStateInternal(testUserId)) .isEqualTo(FORCE_INVERT_TYPE_OFF); } @Test @EnableFlags(android.view.accessibility.Flags.FLAG_FORCE_INVERT_COLOR) public void getForceInvertState_nightModeTrueAndForceInvertOff_returnsOff() @DisableFlags(android.app.Flags.FLAG_FIX_CONTRAST_AND_FORCE_INVERT_STATE_FOR_MULTI_USER) public void getForceInvertState_nightModeTrueAndForceInvertOff_returnsOff_legacy() throws RemoteException { mService.setNightModeActivated(true); Loading @@ -1598,10 +1612,30 @@ public class UiModeManagerServiceTest extends UiServiceTestCase { } @Test @EnableFlags(android.view.accessibility.Flags.FLAG_FORCE_INVERT_COLOR) public void getForceInvertState_nightModeTrueAndForceInvertOn_returnsDark() throws Exception { @EnableFlags({ android.view.accessibility.Flags.FLAG_FORCE_INVERT_COLOR, android.app.Flags.FLAG_FIX_CONTRAST_AND_FORCE_INVERT_STATE_FOR_MULTI_USER }) public void getForceInvertState_nightModeTrueAndForceInvertOff_returnsOff() throws RemoteException { int testUserId = 9; switchUser(testUserId); mService.setNightModeActivated(true); Settings.Secure.putIntForUser( mContentResolver, Settings.Secure.ACCESSIBILITY_FORCE_INVERT_COLOR_ENABLED, /* value= */ 0, /* userId= */ testUserId); assertThat(mUiManagerService.getForceInvertStateInternal(testUserId)) .isEqualTo(FORCE_INVERT_TYPE_OFF); } @Test @EnableFlags(android.view.accessibility.Flags.FLAG_FORCE_INVERT_COLOR) @DisableFlags(android.app.Flags.FLAG_FIX_CONTRAST_AND_FORCE_INVERT_STATE_FOR_MULTI_USER) public void getForceInvertState_nightModeTrueAndForceInvertOn_returnsDark_legacy() throws Exception { mService.setNightModeActivated(true); Settings.Secure.putInt( mContentResolver, Settings.Secure.ACCESSIBILITY_FORCE_INVERT_COLOR_ENABLED, Loading @@ -1611,6 +1645,30 @@ public class UiModeManagerServiceTest extends UiServiceTestCase { .isEqualTo(FORCE_INVERT_TYPE_DARK); } @Test @EnableFlags({ android.view.accessibility.Flags.FLAG_FORCE_INVERT_COLOR, android.app.Flags.FLAG_FIX_CONTRAST_AND_FORCE_INVERT_STATE_FOR_MULTI_USER }) public void getForceInvertState_nightModeTrueAndForceInvertOn_returnsDark() throws Exception { int testUserId = 9; switchUser(testUserId); mService.setNightModeActivated(true); Settings.Secure.putIntForUser( mContentResolver, Settings.Secure.ACCESSIBILITY_FORCE_INVERT_COLOR_ENABLED, /* value= */ 1, /* userId = */ testUserId); assertThat(mUiManagerService.getForceInvertStateInternal(testUserId)) .isEqualTo(FORCE_INVERT_TYPE_DARK); } private void switchUser(int userId) { SystemService.TargetUser user = mock(SystemService.TargetUser.class); doReturn(userId).when(user).getUserIdentifier(); mUiManagerService.onUserSwitching(user, user); } private void triggerDockIntent() { final Intent dockedIntent = new Intent(Intent.ACTION_DOCK_EVENT) Loading Loading
core/java/android/app/IUiModeManager.aidl +11 −6 Original line number Diff line number Diff line Loading @@ -24,10 +24,16 @@ import android.app.IUiModeManagerCallback; * @hide */ interface IUiModeManager { /** * @hide */ void addCallback(IUiModeManagerCallback callback, int userId); /** * @hide */ void addCallback(IUiModeManagerCallback callback); void removeCallback(IUiModeManagerCallback callback, int userId); /** * Enables the car mode. Only the system can do this. Loading Loading @@ -215,15 +221,14 @@ interface IUiModeManager { int getActiveProjectionTypes(); /** * Returns the contrast for the current user. * Returns the contrast for the given user. */ float getContrast(); float getContrast(int userId); /** * Returns the force invert state. * Returns the force invert state for the given user. * * @hide */ int getForceInvertState(); int getForceInvertState(int userId); }
core/java/android/app/UiModeManager.java +224 −17 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ package android.app; import static android.app.Flags.enableCurrentModeTypeBinderCache; import static android.app.Flags.enableNightModeBinderCache; import static android.app.Flags.fixContrastAndForceInvertStateForMultiUser; import android.annotation.CallbackExecutor; import android.annotation.FlaggedApi; Loading @@ -38,10 +39,12 @@ import android.os.IpcDataCache; import android.os.RemoteException; import android.os.ServiceManager; import android.os.ServiceManager.ServiceNotFoundException; import android.os.UserHandle; import android.util.ArrayMap; import android.util.ArraySet; import android.util.Log; import android.util.Slog; import android.util.SparseArray; import com.android.internal.annotations.GuardedBy; import com.android.internal.util.function.pooled.PooledLambda; Loading Loading @@ -86,7 +89,6 @@ public class UiModeManager { private static final String TAG = "UiModeManager"; /** * A listener with a single method that is invoked whenever the packages projecting using the * {@link ProjectionType}s for which it is registered change. Loading Loading @@ -427,7 +429,7 @@ public class UiModeManager { * Context required for getting the opPackageName of API caller; maybe be {@code null} if the * old constructor marked with UnSupportedAppUsage is used. */ private @Nullable Context mContext; private final @Nullable Context mContext; private final Object mLock = new Object(); /** Loading @@ -452,6 +454,8 @@ public class UiModeManager { private final IUiModeManager mService; private final Object mGlobalsLock = new Object(); // ============= Legacy values and methods ============= // // TODO(b/362682063) remove when cleaning up the flag @ForceInvertType private int mForceInvertState = FORCE_INVERT_TYPE_OFF; private float mContrast = ContrastUtils.CONTRAST_DEFAULT_VALUE; Loading @@ -466,17 +470,6 @@ public class UiModeManager { private final ArrayMap<ForceInvertStateChangeListener, Executor> mForceInvertStateChangeListeners = new ArrayMap<>(); Globals(IUiModeManager service) { mService = service; try { mService.addCallback(this); mContrast = mService.getContrast(); mForceInvertState = mService.getForceInvertState(); } catch (RemoteException e) { Log.e(TAG, "Setup failed: UiModeManagerService is dead", e); } } @ForceInvertType private int getForceInvertState() { synchronized (mGlobalsLock) { Loading Loading @@ -548,6 +541,190 @@ public class UiModeManager { () -> listener.onContrastChanged(contrast))); } } // ============= End legacy values and methods ============= // /** * Map of {@link UserCallback} per user id. This will only contain one value for the current * user, unless the process using this service interacts across users. */ private final SparseArray<UserCallback> mUserCallbacks = new SparseArray<>(); Globals(IUiModeManager service) { mService = service; if (fixContrastAndForceInvertStateForMultiUser()) return; try { mService.addCallback(this, UserHandle.USER_NULL); mContrast = mService.getContrast(UserHandle.USER_NULL); mForceInvertState = mService.getForceInvertState(UserHandle.USER_NULL); } catch (RemoteException e) { Log.e(TAG, "Setup failed: UiModeManagerService is dead", e); } } private UserCallback getUserCallbackOrCreate(int userId) { UserCallback userCallback = mUserCallbacks.get(userId); if (userCallback == null) { try { userCallback = new UserCallback(userId); mService.addCallback(userCallback, userId); mUserCallbacks.put(userId, userCallback); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } return userCallback; } private void removeCallbackIfUnusedLocked(int userId) { UserCallback userCallback = mUserCallbacks.get(userId); if (userCallback == null || !userCallback.mContrastChangeListeners.isEmpty() || !userCallback.mForceInvertStateChangeListeners.isEmpty()) { return; } try { mService.removeCallback(userCallback, userId); mUserCallbacks.remove(userId); } catch (RemoteException e) { Log.e(TAG, "Failed to remove UiModeManagerCallback", e); } } @ForceInvertType private int getForceInvertState(int userId) { synchronized (mGlobalsLock) { UserCallback userCallback = mUserCallbacks.get(userId); if (userCallback != null) { return userCallback.mForceInvertState; } try { return mService.getForceInvertState(userId); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } } private void addForceInvertStateChangeListener(ForceInvertStateChangeListener listener, Executor executor, int userId) { synchronized (mGlobalsLock) { UserCallback userCallback = getUserCallbackOrCreate(userId); userCallback.mForceInvertStateChangeListeners.put(listener, executor); } } private void removeForceInvertStateChangeListener(ForceInvertStateChangeListener listener, int userId) { synchronized (mGlobalsLock) { UserCallback userCallback = mUserCallbacks.get(userId); if (userCallback != null) { userCallback.mForceInvertStateChangeListeners.remove(listener); removeCallbackIfUnusedLocked(userId); } } } private float getContrast(int userId) { synchronized (mGlobalsLock) { UserCallback userCallback = mUserCallbacks.get(userId); if (userCallback != null) { return userCallback.mContrast; } try { return mService.getContrast(userId); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } } private void addContrastChangeListener(ContrastChangeListener listener, Executor executor, int userId) { synchronized (mGlobalsLock) { UserCallback userCallback = getUserCallbackOrCreate(userId); userCallback.mContrastChangeListeners.put(listener, executor); } } private void removeContrastChangeListener(ContrastChangeListener listener, int userId) { synchronized (mGlobalsLock) { UserCallback userCallback = mUserCallbacks.get(userId); if (userCallback != null) { userCallback.mContrastChangeListeners.remove(listener); removeCallbackIfUnusedLocked(userId); } } } } /** Global class storing all listeners and cached values for a specific user id. */ private static class UserCallback extends IUiModeManagerCallback.Stub { private UserCallback(int userId) { try { sGlobals.mService.addCallback(this, userId); mContrast = sGlobals.mService.getContrast(userId); mForceInvertState = sGlobals.mService.getForceInvertState(userId); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** Cached contrast value */ private float mContrast = ContrastUtils.CONTRAST_DEFAULT_VALUE; /** Cached force invert state value */ @ForceInvertType private int mForceInvertState = FORCE_INVERT_TYPE_OFF; /** * Map that stores user provided {@link ContrastChangeListener} callbacks, * and the executors on which these callbacks should be called. */ private final ArrayMap<ContrastChangeListener, Executor> mContrastChangeListeners = new ArrayMap<>(); private final ArrayMap<ForceInvertStateChangeListener, Executor> mForceInvertStateChangeListeners = new ArrayMap<>(); @Override public void notifyContrastChanged(float contrast) throws RemoteException { synchronized (sGlobals.mGlobalsLock) { // if value changed in the settings, update the cached value and notify listeners Float previousContrast = mContrast; if (Math.abs(previousContrast - contrast) < 1e-10) { return; } mContrast = contrast; mContrastChangeListeners.forEach((listener, executor) -> executor.execute(() -> listener.onContrastChanged(contrast))); } } @Override public void notifyForceInvertStateChanged(@ForceInvertType int forceInvertState) throws RemoteException { final Map<ForceInvertStateChangeListener, Executor> listeners = new ArrayMap<>(); synchronized (sGlobals.mGlobalsLock) { // if value changed in the settings, update the cached value and notify listeners if (mForceInvertState == forceInvertState) { return; } mForceInvertState = forceInvertState; listeners.putAll(mForceInvertStateChangeListeners); } listeners.forEach((listener, executor) -> { final long token = Binder.clearCallingIdentity(); try { executor.execute(() -> listener.onForceInvertStateChanged(forceInvertState)); } finally { Binder.restoreCallingIdentity(token); } }); } } /** Loading Loading @@ -1520,11 +1697,14 @@ public class UiModeManager { */ @FloatRange(from = -1.0f, to = 1.0f) public float getContrast() { if (fixContrastAndForceInvertStateForMultiUser()) { return sGlobals.getContrast(getUserId()); } return sGlobals.getContrast(); } /** * Registers a {@link ContrastChangeListener} for the current user. * Registers a {@link ContrastChangeListener} for the user. * * @param executor The executor on which the listener should be called back. * @param listener The listener. Loading @@ -1534,17 +1714,25 @@ public class UiModeManager { @NonNull ContrastChangeListener listener) { Objects.requireNonNull(executor); Objects.requireNonNull(listener); if (fixContrastAndForceInvertStateForMultiUser()) { sGlobals.addContrastChangeListener(listener, executor, getUserId()); return; } sGlobals.addContrastChangeListener(listener, executor); } /** * Unregisters a {@link ContrastChangeListener} for the current user. * Unregisters a {@link ContrastChangeListener} for the user. * If the listener was not registered, does nothing and returns. * * @param listener The listener to unregister. */ public void removeContrastChangeListener(@NonNull ContrastChangeListener listener) { Objects.requireNonNull(listener); if (fixContrastAndForceInvertStateForMultiUser()) { sGlobals.removeContrastChangeListener(listener, getUserId()); return; } sGlobals.removeContrastChangeListener(listener); } Loading @@ -1557,6 +1745,9 @@ public class UiModeManager { @FlaggedApi(android.view.accessibility.Flags.FLAG_FORCE_INVERT_COLOR) @ForceInvertType public int getForceInvertState() { if (fixContrastAndForceInvertStateForMultiUser()) { return sGlobals.getForceInvertState(getUserId()); } return sGlobals.getForceInvertState(); } Loading @@ -1577,7 +1768,7 @@ public class UiModeManager { } /** * Registers a {@link ForceInvertStateChangeListener} for the current user. * Registers a {@link ForceInvertStateChangeListener} for the user. * * @param executor The executor on which the listener should be called back. * @param listener The listener. Loading @@ -1589,11 +1780,15 @@ public class UiModeManager { @NonNull ForceInvertStateChangeListener listener) { Objects.requireNonNull(executor); Objects.requireNonNull(listener); if (fixContrastAndForceInvertStateForMultiUser()) { sGlobals.addForceInvertStateChangeListener(listener, executor, getUserId()); return; } sGlobals.addForceInvertStateChangeListener(listener, executor); } /** * Unregisters a {@link ForceInvertStateChangeListener} for the current user. * Unregisters a {@link ForceInvertStateChangeListener} for the user. * If the listener was not registered, does nothing and returns. * * @param listener The listener to unregister. Loading @@ -1603,6 +1798,18 @@ public class UiModeManager { public void removeForceInvertStateChangeListener( @NonNull ForceInvertStateChangeListener listener) { Objects.requireNonNull(listener); if (fixContrastAndForceInvertStateForMultiUser()) { sGlobals.removeForceInvertStateChangeListener(listener, getUserId()); return; } sGlobals.removeForceInvertStateChangeListener(listener); } /** * Return the context user id. If this class was built with the UnsupportedAppUsage constructor * and the context is null, return the user id of this process instead. */ private int getUserId() { return mContext != null ? mContext.getUserId() : UserHandle.myUserId(); } }
core/java/android/app/ui_mode_manager.aconfig +10 −0 Original line number Diff line number Diff line Loading @@ -21,3 +21,13 @@ flag { purpose: PURPOSE_BUGFIX } } flag { namespace: "systemui" name: "fix_contrast_and_force_invert_state_for_multi_user" description: "Fixes contrast and force invert state APIs for multi user and HSUM cases." bug: "362682063" metadata { purpose: PURPOSE_BUGFIX } } No newline at end of file
services/core/java/com/android/server/UiModeManagerService.java +147 −16 File changed.Preview size limit exceeded, changes collapsed. Show changes
services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java +65 −7 Original line number Diff line number Diff line Loading @@ -91,6 +91,7 @@ import android.os.Process; import android.os.RemoteException; import android.os.UserHandle; import android.os.test.FakePermissionEnforcer; import android.platform.test.annotations.DisableFlags; import android.platform.test.annotations.EnableFlags; import android.platform.test.flag.junit.SetFlagsRule; import android.provider.Settings; Loading Loading @@ -405,9 +406,7 @@ public class UiModeManagerServiceTest extends UiServiceTestCase { @Test public void setNightModeActivated_permissionToChangeOtherUsers() throws RemoteException { SystemService.TargetUser user = mock(SystemService.TargetUser.class); doReturn(9).when(user).getUserIdentifier(); mUiManagerService.onUserSwitching(user, user); switchUser(9); when(mContext.checkCallingOrSelfPermission( eq(Manifest.permission.INTERACT_ACROSS_USERS))) .thenReturn(PackageManager.PERMISSION_DENIED); Loading Loading @@ -1575,16 +1574,31 @@ public class UiModeManagerServiceTest extends UiServiceTestCase { @Test @EnableFlags(android.view.accessibility.Flags.FLAG_FORCE_INVERT_COLOR) public void getForceInvertState_nightModeFalse_returnsOff() throws RemoteException { @DisableFlags(android.app.Flags.FLAG_FIX_CONTRAST_AND_FORCE_INVERT_STATE_FOR_MULTI_USER) public void getForceInvertState_nightModeFalse_returnsOff_legacy() throws RemoteException { mService.setNightModeActivated(false); assertThat(mUiManagerService.getForceInvertStateInternal()) .isEqualTo(FORCE_INVERT_TYPE_OFF); } @Test @EnableFlags({ android.view.accessibility.Flags.FLAG_FORCE_INVERT_COLOR, android.app.Flags.FLAG_FIX_CONTRAST_AND_FORCE_INVERT_STATE_FOR_MULTI_USER }) public void getForceInvertState_nightModeFalse_returnsOff() throws RemoteException { int testUserId = 9; switchUser(testUserId); mService.setNightModeActivated(false); assertThat(mUiManagerService.getForceInvertStateInternal(testUserId)) .isEqualTo(FORCE_INVERT_TYPE_OFF); } @Test @EnableFlags(android.view.accessibility.Flags.FLAG_FORCE_INVERT_COLOR) public void getForceInvertState_nightModeTrueAndForceInvertOff_returnsOff() @DisableFlags(android.app.Flags.FLAG_FIX_CONTRAST_AND_FORCE_INVERT_STATE_FOR_MULTI_USER) public void getForceInvertState_nightModeTrueAndForceInvertOff_returnsOff_legacy() throws RemoteException { mService.setNightModeActivated(true); Loading @@ -1598,10 +1612,30 @@ public class UiModeManagerServiceTest extends UiServiceTestCase { } @Test @EnableFlags(android.view.accessibility.Flags.FLAG_FORCE_INVERT_COLOR) public void getForceInvertState_nightModeTrueAndForceInvertOn_returnsDark() throws Exception { @EnableFlags({ android.view.accessibility.Flags.FLAG_FORCE_INVERT_COLOR, android.app.Flags.FLAG_FIX_CONTRAST_AND_FORCE_INVERT_STATE_FOR_MULTI_USER }) public void getForceInvertState_nightModeTrueAndForceInvertOff_returnsOff() throws RemoteException { int testUserId = 9; switchUser(testUserId); mService.setNightModeActivated(true); Settings.Secure.putIntForUser( mContentResolver, Settings.Secure.ACCESSIBILITY_FORCE_INVERT_COLOR_ENABLED, /* value= */ 0, /* userId= */ testUserId); assertThat(mUiManagerService.getForceInvertStateInternal(testUserId)) .isEqualTo(FORCE_INVERT_TYPE_OFF); } @Test @EnableFlags(android.view.accessibility.Flags.FLAG_FORCE_INVERT_COLOR) @DisableFlags(android.app.Flags.FLAG_FIX_CONTRAST_AND_FORCE_INVERT_STATE_FOR_MULTI_USER) public void getForceInvertState_nightModeTrueAndForceInvertOn_returnsDark_legacy() throws Exception { mService.setNightModeActivated(true); Settings.Secure.putInt( mContentResolver, Settings.Secure.ACCESSIBILITY_FORCE_INVERT_COLOR_ENABLED, Loading @@ -1611,6 +1645,30 @@ public class UiModeManagerServiceTest extends UiServiceTestCase { .isEqualTo(FORCE_INVERT_TYPE_DARK); } @Test @EnableFlags({ android.view.accessibility.Flags.FLAG_FORCE_INVERT_COLOR, android.app.Flags.FLAG_FIX_CONTRAST_AND_FORCE_INVERT_STATE_FOR_MULTI_USER }) public void getForceInvertState_nightModeTrueAndForceInvertOn_returnsDark() throws Exception { int testUserId = 9; switchUser(testUserId); mService.setNightModeActivated(true); Settings.Secure.putIntForUser( mContentResolver, Settings.Secure.ACCESSIBILITY_FORCE_INVERT_COLOR_ENABLED, /* value= */ 1, /* userId = */ testUserId); assertThat(mUiManagerService.getForceInvertStateInternal(testUserId)) .isEqualTo(FORCE_INVERT_TYPE_DARK); } private void switchUser(int userId) { SystemService.TargetUser user = mock(SystemService.TargetUser.class); doReturn(userId).when(user).getUserIdentifier(); mUiManagerService.onUserSwitching(user, user); } private void triggerDockIntent() { final Intent dockedIntent = new Intent(Intent.ACTION_DOCK_EVENT) Loading