DarkMode Tile affects all users + security bug

When setNightModeActivated is called, the change affects the current logged in user
Also, permission checks are made before changing dark theme
Test: manual
Bug: 168039904
Merged-In: I7786d9dde10f7547f5dd9fb00661fb89fde9f05c
Change-Id: I7786d9dde10f7547f5dd9fb00661fb89fde9f05c
(cherry picked from commit f16e87a992d7d6c1d367770bd569707a29cb15b4)
diff --git a/core/java/android/app/UiModeManager.java b/core/java/android/app/UiModeManager.java
index 7c6eff1..06d1b74 100644
--- a/core/java/android/app/UiModeManager.java
+++ b/core/java/android/app/UiModeManager.java
@@ -510,6 +510,9 @@
     }
 
     /**
+     * Activating night mode for the current user
+     *
+     * @return {@code true} if the change is successful
      * @hide
      */
     public boolean setNightModeActivated(boolean active) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java
index 2f58272..acd2846 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java
@@ -60,7 +60,7 @@
     private static final String PATTERN_HOUR_MINUTE = "h:mm a";
     private static final String PATTERN_HOUR_NINUTE_24 = "HH:mm";
 
-    private final ColorDisplayManager mManager;
+    private ColorDisplayManager mManager;
     private final LocationController mLocationController;
     private NightDisplayListener mListener;
     private boolean mIsListening;
@@ -105,6 +105,8 @@
             mListener.setCallback(null);
         }
 
+        mManager = getHost().getUserContext().getSystemService(ColorDisplayManager.class);
+
         // Make a new controller for the new user.
         mListener = new NightDisplayListener(mContext, newUserId, new Handler(Looper.myLooper()));
         if (mIsListening) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java
index 7b83c20..f777553 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java
@@ -50,16 +50,15 @@
     public static DateTimeFormatter formatter = DateTimeFormatter.ofPattern("hh:mm a");
     private final Icon mIcon = ResourceIcon.get(
             com.android.internal.R.drawable.ic_qs_ui_mode_night);
-    private final UiModeManager mUiModeManager;
+    private UiModeManager mUiModeManager;
     private final BatteryController mBatteryController;
     private final LocationController mLocationController;
-
     @Inject
     public UiModeNightTile(QSHost host, ConfigurationController configurationController,
             BatteryController batteryController, LocationController locationController) {
         super(host);
         mBatteryController = batteryController;
-        mUiModeManager = mContext.getSystemService(UiModeManager.class);
+        mUiModeManager = host.getUserContext().getSystemService(UiModeManager.class);
         mLocationController = locationController;
         configurationController.observe(getLifecycle(), this);
         batteryController.observe(getLifecycle(), this);
diff --git a/services/core/java/com/android/server/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java
index b5aea75..daae1a1 100644
--- a/services/core/java/com/android/server/UiModeManagerService.java
+++ b/services/core/java/com/android/server/UiModeManagerService.java
@@ -134,6 +134,7 @@
     int mCurUiMode = 0;
     private int mSetUiMode = 0;
     private boolean mHoldingConfiguration = false;
+    private int mCurrentUser;
 
     private Configuration mConfiguration = new Configuration();
     boolean mSystemReady;
@@ -323,6 +324,7 @@
     @Override
     public void onSwitchUser(int userHandle) {
         super.onSwitchUser(userHandle);
+      	mCurrentUser = userHandle;
         getContext().getContentResolver().unregisterContentObserver(mSetupWizardObserver);
         verifySetupWizardCompleted();
     }
@@ -728,16 +730,30 @@
 
         @Override
         public boolean setNightModeActivated(boolean active) {
+            if (isNightModeLocked() && (getContext().checkCallingOrSelfPermission(
+                    android.Manifest.permission.MODIFY_DAY_NIGHT_MODE)
+                    != PackageManager.PERMISSION_GRANTED)) {
+                Slog.e(TAG, "Night mode locked, requires MODIFY_DAY_NIGHT_MODE permission");
+                return false;
+            }
+            final int user = Binder.getCallingUserHandle().getIdentifier();
+            if (user != mCurrentUser && getContext().checkCallingOrSelfPermission(
+                    android.Manifest.permission.INTERACT_ACROSS_USERS)
+                    != PackageManager.PERMISSION_GRANTED) {
+                Slog.e(TAG, "Target user is not current user,"
+                        + " INTERACT_ACROSS_USERS permission is required");
+                return false;
+
+            }
             synchronized (mLock) {
-                final int user = UserHandle.getCallingUserId();
                 final long ident = Binder.clearCallingIdentity();
                 try {
                     if (mNightMode == MODE_NIGHT_AUTO || mNightMode == MODE_NIGHT_CUSTOM) {
                         unregisterScreenOffEventLocked();
                         mOverrideNightModeOff = !active;
                         mOverrideNightModeOn = active;
-                        mOverrideNightModeUser = user;
-                        persistNightModeOverrides(user);
+                        mOverrideNightModeUser = mCurrentUser;
+                        persistNightModeOverrides(mCurrentUser);
                     } else if (mNightMode == UiModeManager.MODE_NIGHT_NO
                             && active) {
                         mNightMode = UiModeManager.MODE_NIGHT_YES;
@@ -747,7 +763,7 @@
                     }
                     updateConfigurationLocked();
                     applyConfigurationExternallyLocked();
-                    persistNightMode(user);
+                    persistNightMode(mCurrentUser);
                     return true;
                 } finally {
                     Binder.restoreCallingIdentity(ident);
diff --git a/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java
index b100c84..4538300 100644
--- a/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java
@@ -16,6 +16,7 @@
 
 package com.android.server;
 
+import android.Manifest;
 import android.app.AlarmManager;
 import android.app.IUiModeManager;
 import android.content.BroadcastReceiver;
@@ -24,6 +25,7 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.os.Handler;
@@ -66,6 +68,7 @@
 import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -227,6 +230,15 @@
     }
 
     @Test
+    public void setNightModeActivated_permissiontoChangeOtherUsers() throws RemoteException {
+        mUiManagerService.onSwitchUser(9);
+        when(mContext.checkCallingOrSelfPermission(
+                eq(Manifest.permission.INTERACT_ACROSS_USERS)))
+                .thenReturn(PackageManager.PERMISSION_DENIED);
+        assertFalse(mService.setNightModeActivated(true));
+    }
+
+    @Test
     public void autoNightModeSwitch_batterySaverOn() throws RemoteException {
         mService.setNightMode(MODE_NIGHT_NO);
         when(mTwilightState.isNight()).thenReturn(false);