Merge "DO NOT MERGE Initialize VolumeUI when volume change event is received." into qt-qpr1-dev
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 1fd9caf..d7aedab 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -321,6 +321,7 @@
             218  [(log_from_module) = "permissioncontroller"];
         ExclusionRectStateChanged exclusion_rect_state_changed = 223;
         BackGesture back_gesture_reported_reported = 224;
+        VmsClientConnectionStateChanged vms_client_connection_state_changed = 230;
     }
 
     // Pulled events will start at field 10000.
@@ -388,6 +389,7 @@
         CoolingDevice cooling_device = 10059;
         AppOps app_ops = 10060;
         ProcessSystemIonHeapSize process_system_ion_heap_size = 10061;
+        VmsClientStats vms_client_stats = 10065;
     }
 
     // DO NOT USE field numbers above 100,000 in AOSP.
@@ -3708,6 +3710,33 @@
     optional Result result = 9;
 }
 
+/**
+ * Logs when a Vehicle Maps Service client's connection state has changed
+ *
+ * Logged from:
+ *   packages/services/Car/service/src/com/android/car/stats/VmsClientLog.java
+ */
+message VmsClientConnectionStateChanged {
+    // The UID of the VMS client app
+    optional int32 uid = 1 [(is_uid) = true];
+
+    enum State {
+        UNKNOWN = 0;
+        // Attempting to connect to the client
+        CONNECTING = 1;
+        // Client connection established
+        CONNECTED = 2;
+        // Client connection closed unexpectedly
+        DISCONNECTED = 3;
+        // Client connection closed by VMS
+        TERMINATED = 4;
+        // Error establishing the client connection
+        CONNECTION_ERROR = 5;
+    }
+
+    optional State state  = 2;
+}
+
 //////////////////////////////////////////////////////////////////////
 // Pulled atoms below this line //
 //////////////////////////////////////////////////////////////////////
@@ -6819,3 +6848,32 @@
     }
     optional Category category = 6;
 }
+
+/**
+ * Pulls client metrics on data transferred via Vehicle Maps Service.
+ * Metrics are keyed by uid + layer.
+ *
+ * Pulled from:
+ *   packages/services/Car/service/src/com/android/car/stats/CarStatsService.java
+ */
+message VmsClientStats {
+    // UID of the VMS client app
+    optional int32 uid = 1 [(is_uid) = true];
+
+    // VMS layer definition
+    optional int32 layer_type = 2;
+    optional int32 layer_channel = 3;
+    optional int32 layer_version = 4;
+
+    // Bytes and packets sent by the client for the layer
+    optional int64 tx_bytes = 5;
+    optional int64 tx_packets = 6;
+
+    // Bytes and packets received by the client for the layer
+    optional int64 rx_bytes = 7;
+    optional int64 rx_packets = 8;
+
+    // Bytes and packets dropped due to client error
+    optional int64 dropped_bytes = 9;
+    optional int64 dropped_packets = 10;
+}
diff --git a/core/java/android/app/IUiModeManager.aidl b/core/java/android/app/IUiModeManager.aidl
index cae54b6..1380f0c 100644
--- a/core/java/android/app/IUiModeManager.aidl
+++ b/core/java/android/app/IUiModeManager.aidl
@@ -61,4 +61,9 @@
      * Tells if Night mode is locked or not.
      */
     boolean isNightModeLocked();
+
+    /**
+    * @hide
+    */
+    boolean setNightModeActivated(boolean active);
 }
diff --git a/core/java/android/app/UiModeManager.java b/core/java/android/app/UiModeManager.java
index 46316e1..d8cfb7e 100644
--- a/core/java/android/app/UiModeManager.java
+++ b/core/java/android/app/UiModeManager.java
@@ -317,4 +317,18 @@
         }
         return true;
     }
+
+    /**
+     * @hide*
+     */
+    public boolean setNightModeActivated(boolean active) {
+        if (mService != null) {
+            try {
+                return mService.setNightModeActivated(active);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+        return false;
+    }
 }
diff --git a/core/java/android/bluetooth/le/ScanFilter.java b/core/java/android/bluetooth/le/ScanFilter.java
index 038994f..7511fd0 100644
--- a/core/java/android/bluetooth/le/ScanFilter.java
+++ b/core/java/android/bluetooth/le/ScanFilter.java
@@ -671,8 +671,6 @@
 
         /**
          * Set filter on on manufacturerData. A negative manufacturerId is considered as invalid id.
-         * <p>
-         * Note the first two bytes of the {@code manufacturerData} is the manufacturerId.
          *
          * @throws IllegalArgumentException If the {@code manufacturerId} is invalid.
          */
diff --git a/core/java/android/os/BinderProxy.java b/core/java/android/os/BinderProxy.java
index 97c0a13..c35b05f 100644
--- a/core/java/android/os/BinderProxy.java
+++ b/core/java/android/os/BinderProxy.java
@@ -241,32 +241,37 @@
             }
 
             Map<String, Integer> counts = new HashMap<>();
-            for (ArrayList<WeakReference<BinderProxy>> a : mMainIndexValues) {
-                if (a != null) {
-                    for (WeakReference<BinderProxy> weakRef : a) {
-                        BinderProxy bp = weakRef.get();
-                        String key;
-                        if (bp == null) {
-                            key = "<cleared weak-ref>";
-                        } else {
-                            try {
-                                key = bp.getInterfaceDescriptor();
-                                if ((key == null || key.isEmpty()) && !bp.isBinderAlive()) {
-                                    key = "<proxy to dead node>";
-                                }
-                            } catch (Throwable t) {
-                                key = "<exception during getDescriptor>";
-                            }
-                        }
-                        Integer i = counts.get(key);
-                        if (i == null) {
-                            counts.put(key, 1);
-                        } else {
-                            counts.put(key, i + 1);
-                        }
+            final ArrayList<WeakReference<BinderProxy>> proxiesToQuery =
+                    new ArrayList<WeakReference<BinderProxy>>();
+            synchronized (sProxyMap) {
+                for (ArrayList<WeakReference<BinderProxy>> a : mMainIndexValues) {
+                    if (a != null) {
+                        proxiesToQuery.addAll(a);
                     }
                 }
             }
+            for (WeakReference<BinderProxy> weakRef : proxiesToQuery) {
+                BinderProxy bp = weakRef.get();
+                String key;
+                if (bp == null) {
+                    key = "<cleared weak-ref>";
+                } else {
+                    try {
+                        key = bp.getInterfaceDescriptor();
+                        if ((key == null || key.isEmpty()) && !bp.isBinderAlive()) {
+                            key = "<proxy to dead node>";
+                        }
+                    } catch (Throwable t) {
+                        key = "<exception during getDescriptor>";
+                    }
+                }
+                Integer i = counts.get(key);
+                if (i == null) {
+                    counts.put(key, 1);
+                } else {
+                    counts.put(key, i + 1);
+                }
+            }
             Map.Entry<String, Integer>[] sorted = counts.entrySet().toArray(
                     new Map.Entry[counts.size()]);
 
@@ -354,9 +359,7 @@
      * @hide
      */
     public static InterfaceCount[] getSortedInterfaceCounts(int num) {
-        synchronized (sProxyMap) {
-            return sProxyMap.getSortedInterfaceCounts(num);
-        }
+        return sProxyMap.getSortedInterfaceCounts(num);
     }
 
     /**
@@ -376,10 +379,8 @@
      */
     public static void dumpProxyDebugInfo() {
         if (Build.IS_DEBUGGABLE) {
-            synchronized (sProxyMap) {
-                sProxyMap.dumpProxyInterfaceCounts();
-                sProxyMap.dumpPerUidProxyCounts();
-            }
+            sProxyMap.dumpProxyInterfaceCounts();
+            sProxyMap.dumpPerUidProxyCounts();
         }
     }
 
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
index efb5538..31fb118 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
@@ -591,24 +591,7 @@
         mCarUxRestrictionManagerWrapper = new CarUxRestrictionManagerWrapper();
 
         mNotificationDataManager = new NotificationDataManager();
-        mNotificationDataManager.setOnUnseenCountUpdateListener(
-                () -> {
-                    if (mNavigationBarView != null && mNotificationDataManager != null) {
-                        Boolean hasUnseen =
-                                mNotificationDataManager.getUnseenNotificationCount() > 0;
-                        if (mNavigationBarView != null) {
-                            mNavigationBarView.toggleNotificationUnseenIndicator(hasUnseen);
-                        }
-
-                        if (mLeftNavigationBarView != null) {
-                            mLeftNavigationBarView.toggleNotificationUnseenIndicator(hasUnseen);
-                        }
-
-                        if (mRightNavigationBarView != null) {
-                            mRightNavigationBarView.toggleNotificationUnseenIndicator(hasUnseen);
-                        }
-                    }
-                });
+        mNotificationDataManager.setOnUnseenCountUpdateListener(this::onUnseenCountUpdate);
 
         mEnableHeadsUpNotificationWhenNotificationShadeOpen = mContext.getResources().getBoolean(
                 R.bool.config_enableHeadsUpNotificationWhenNotificationShadeOpen);
@@ -734,6 +717,29 @@
     }
 
     /**
+     * This method is called whenever there is an update to the number of unseen notifications.
+     * This method can be extended by OEMs to customize the desired logic.
+     */
+    protected void onUnseenCountUpdate() {
+        if (mNavigationBarView != null && mNotificationDataManager != null) {
+            Boolean hasUnseen =
+                    mNotificationDataManager.getUnseenNotificationCount() > 0;
+
+            if (mNavigationBarView != null) {
+                mNavigationBarView.toggleNotificationUnseenIndicator(hasUnseen);
+            }
+
+            if (mLeftNavigationBarView != null) {
+                mLeftNavigationBarView.toggleNotificationUnseenIndicator(hasUnseen);
+            }
+
+            if (mRightNavigationBarView != null) {
+                mRightNavigationBarView.toggleNotificationUnseenIndicator(hasUnseen);
+            }
+        }
+    }
+
+    /**
      * @return true if the notification panel is currently visible
      */
     boolean isNotificationPanelOpen() {
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 6ffea1b..f28c319 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -894,6 +894,10 @@
     <string name="quick_settings_ui_mode_night_label">Dark theme</string>
     <!-- QuickSettings: Label for the dark theme tile when enabled by battery saver. [CHAR LIMIT=40] -->
     <string name="quick_settings_ui_mode_night_label_battery_saver">Dark theme\nBattery saver</string>
+    <!-- QuickSettings: Secondary text for when the Dark Mode will be enabled at sunset. [CHAR LIMIT=20] -->
+    <string name="quick_settings_dark_mode_secondary_label_on_at_sunset">On at sunset</string>
+    <!-- QuickSettings: Secondary text for when the Dark Mode will be on until sunrise. [CHAR LIMIT=20] -->
+    <string name="quick_settings_dark_mode_secondary_label_until_sunrise">Until sunrise</string>
 
     <!-- QuickSettings: NFC tile [CHAR LIMIT=NONE] -->
     <string name="quick_settings_nfc_label">NFC</string>
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index ff02e71..a24432f 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -86,6 +86,7 @@
 import com.android.internal.util.ScreenshotHelper;
 import com.android.internal.view.RotationPolicy;
 import com.android.internal.widget.LockPatternUtils;
+import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.systemui.Dependency;
 import com.android.systemui.Interpolators;
 import com.android.systemui.MultiListLayout;
@@ -213,11 +214,13 @@
         Dependency.get(ConfigurationController.class).addCallback(this);
 
         mActivityStarter = Dependency.get(ActivityStarter.class);
+        KeyguardUpdateMonitor keyguardUpdateMonitor = KeyguardUpdateMonitor.getInstance(context);
         UnlockMethodCache unlockMethodCache = UnlockMethodCache.getInstance(context);
         unlockMethodCache.addListener(
                 () -> {
                     if (mDialog != null && mDialog.mPanelController != null) {
-                        boolean locked = !unlockMethodCache.canSkipBouncer();
+                        boolean locked = !unlockMethodCache.canSkipBouncer()
+                                && keyguardUpdateMonitor.isKeyguardVisible();
                         mDialog.mPanelController.onDeviceLockStateChanged(locked);
                     }
                 });
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 dd0ea5e..dc9a2ce 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java
@@ -21,7 +21,7 @@
 import android.content.res.Configuration;
 import android.provider.Settings;
 import android.service.quicksettings.Tile;
-import android.widget.Switch;
+import android.text.TextUtils;
 
 import com.android.internal.logging.nano.MetricsProto;
 import com.android.systemui.R;
@@ -79,24 +79,33 @@
             return;
         }
         boolean newState = !mState.value;
-        mUiModeManager.setNightMode(newState ? UiModeManager.MODE_NIGHT_YES
-                : UiModeManager.MODE_NIGHT_NO);
+        mUiModeManager.setNightModeActivated(newState);
         refreshState(newState);
     }
 
     @Override
     protected void handleUpdateState(BooleanState state, Object arg) {
+        int uiMode = mUiModeManager.getNightMode();
         boolean powerSave = mBatteryController.isPowerSave();
+        boolean isAuto = uiMode == UiModeManager.MODE_NIGHT_AUTO;
         boolean nightMode = (mContext.getResources().getConfiguration().uiMode
-                & Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES;
+                        & Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES;
 
+        if (isAuto) {
+            state.secondaryLabel = mContext.getResources().getString(nightMode
+                    ? R.string.quick_settings_dark_mode_secondary_label_until_sunrise
+                    : R.string.quick_settings_dark_mode_secondary_label_on_at_sunset);
+        } else {
+            state.secondaryLabel = null;
+        }
         state.value = nightMode;
         state.label = mContext.getString(powerSave
                 ? R.string.quick_settings_ui_mode_night_label_battery_saver
                 : R.string.quick_settings_ui_mode_night_label);
-        state.contentDescription = state.label;
         state.icon = mIcon;
-        state.expandedAccessibilityClassName = Switch.class.getName();
+        state.contentDescription = TextUtils.isEmpty(state.secondaryLabel)
+                ? state.label
+                : TextUtils.concat(state.label, ", ", state.secondaryLabel);
         if (powerSave) {
             state.state = Tile.STATE_UNAVAILABLE;
         } else {
diff --git a/services/core/java/com/android/server/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java
index 6b03897..56db8898 100644
--- a/services/core/java/com/android/server/UiModeManagerService.java
+++ b/services/core/java/com/android/server/UiModeManagerService.java
@@ -53,8 +53,8 @@
 import android.service.vr.IVrManager;
 import android.service.vr.IVrStateCallbacks;
 import android.util.Slog;
-
 import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.DisableCarModeActivity;
 import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
 import com.android.internal.notification.SystemNotificationChannels;
@@ -62,10 +62,13 @@
 import com.android.server.twilight.TwilightListener;
 import com.android.server.twilight.TwilightManager;
 import com.android.server.twilight.TwilightState;
+import com.android.server.wm.WindowManagerInternal;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 
+import static android.content.Intent.ACTION_SCREEN_OFF;
+
 final class UiModeManagerService extends SystemService {
     private static final String TAG = UiModeManager.class.getSimpleName();
     private static final boolean LOG = false;
@@ -79,6 +82,10 @@
 
     private int mLastBroadcastState = Intent.EXTRA_DOCK_STATE_UNDOCKED;
     private int mNightMode = UiModeManager.MODE_NIGHT_NO;
+    // we use the override auto mode
+    // for example: force night mode off in the night time while in auto mode
+    private int mNightModeOverride = mNightMode;
+    protected static final String OVERRIDE_NIGHT_MODE = Secure.UI_NIGHT_MODE + "_override";
 
     private boolean mCarModeEnabled = false;
     private boolean mCharging = false;
@@ -112,6 +119,7 @@
     private TwilightManager mTwilightManager;
     private NotificationManager mNotificationManager;
     private StatusBarManager mStatusBarManager;
+    private WindowManagerInternal mWindowManager;
 
     private PowerManager.WakeLock mWakeLock;
 
@@ -121,6 +129,17 @@
         super(context);
     }
 
+    @VisibleForTesting
+    protected UiModeManagerService(Context context, WindowManagerInternal wm,
+                                   PowerManager.WakeLock wl, TwilightManager tm,
+                                   boolean setupWizardComplete) {
+        super(context);
+        mWindowManager = wm;
+        mWakeLock = wl;
+        mTwilightManager = tm;
+        mSetupWizardComplete = setupWizardComplete;
+    }
+
     private static Intent buildHomeIntent(String category) {
         Intent intent = new Intent(Intent.ACTION_MAIN);
         intent.addCategory(category);
@@ -182,8 +201,23 @@
         public void onTwilightStateChanged(@Nullable TwilightState state) {
             synchronized (mLock) {
                 if (mNightMode == UiModeManager.MODE_NIGHT_AUTO) {
-                    updateComputedNightModeLocked();
-                    updateLocked(0, 0);
+                    final IntentFilter intentFilter =
+                            new IntentFilter(ACTION_SCREEN_OFF);
+                    getContext().registerReceiver(mOnScreenOffHandler, intentFilter);
+                }
+            }
+        }
+    };
+
+    private final BroadcastReceiver mOnScreenOffHandler = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            synchronized (mLock) {
+                updateLocked(0, 0);
+                try {
+                    getContext().unregisterReceiver(mOnScreenOffHandler);
+                } catch (IllegalArgumentException e) {
+                    // we ignore this exception if the receiver is unregistered already.
                 }
             }
         }
@@ -220,8 +254,10 @@
     private final ContentObserver mDarkThemeObserver = new ContentObserver(mHandler) {
         @Override
         public void onChange(boolean selfChange, Uri uri) {
-            final int mode = Secure.getIntForUser(getContext().getContentResolver(),
-                    Secure.UI_NIGHT_MODE, mNightMode, 0);
+            int mode = Secure.getIntForUser(getContext().getContentResolver(), Secure.UI_NIGHT_MODE,
+                    mNightMode, 0);
+            mode = mode == UiModeManager.MODE_NIGHT_AUTO
+                    ? UiModeManager.MODE_NIGHT_YES : UiModeManager.MODE_NIGHT_NO;
             SystemProperties.set(SYSTEM_PROPERTY_DEVICE_THEME, Integer.toString(mode));
         }
     };
@@ -240,6 +276,7 @@
         final PowerManager powerManager =
                 (PowerManager) context.getSystemService(Context.POWER_SERVICE);
         mWakeLock = powerManager.newWakeLock(PowerManager.FULL_WAKE_LOCK, TAG);
+        mWindowManager = LocalServices.getService(WindowManagerInternal.class);
 
         // If setup isn't complete for this user listen for completion so we can unblock
         // being able to send a night mode configuration change event
@@ -306,6 +343,16 @@
                 false, mDarkThemeObserver, 0);
     }
 
+    @VisibleForTesting
+    protected IUiModeManager getService() {
+        return mService;
+    }
+
+    @VisibleForTesting
+    protected Configuration getConfiguration() {
+        return mConfiguration;
+    }
+
     // Records whether setup wizard has happened or not and adds an observer for this user if not.
     private void verifySetupWizardCompleted() {
         final Context context = getContext();
@@ -340,8 +387,11 @@
         if (mSetupWizardComplete) {
             mNightMode = Secure.getIntForUser(context.getContentResolver(),
                     Secure.UI_NIGHT_MODE, defaultNightMode, userId);
+            mNightModeOverride = Secure.getIntForUser(context.getContentResolver(),
+                    OVERRIDE_NIGHT_MODE, defaultNightMode, userId);
         } else {
             mNightMode = defaultNightMode;
+            mNightModeOverride = defaultNightMode;
         }
 
         return oldNightMode != mNightMode;
@@ -424,14 +474,30 @@
             try {
                 synchronized (mLock) {
                     if (mNightMode != mode) {
+                        if (mNightMode == UiModeManager.MODE_NIGHT_AUTO) {
+                            try {
+                                getContext().unregisterReceiver(mOnScreenOffHandler);
+                            } catch (IllegalArgumentException e) {
+                                // we ignore this exception if the receiver is unregistered already.
+                            }
+                        }
                         // Only persist setting if not in car mode
                         if (!mCarModeEnabled) {
                             Secure.putIntForUser(getContext().getContentResolver(),
                                     Secure.UI_NIGHT_MODE, mode, user);
+                            Secure.putIntForUser(getContext().getContentResolver(),
+                                    OVERRIDE_NIGHT_MODE, mNightModeOverride, user);
                         }
 
                         mNightMode = mode;
-                        updateLocked(0, 0);
+                        mNightModeOverride = mode;
+                        //on screen off will update configuration instead
+                        if (mNightMode != UiModeManager.MODE_NIGHT_AUTO) {
+                            updateLocked(0, 0);
+                        } else {
+                            getContext().registerReceiver(
+                                    mOnScreenOffHandler, new IntentFilter(ACTION_SCREEN_OFF));
+                        }
                     }
                 }
             } finally {
@@ -471,6 +537,34 @@
             if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) return;
             dumpImpl(pw);
         }
+
+        @Override
+        public boolean setNightModeActivated(boolean active) {
+            synchronized (mLock) {
+                final long ident = Binder.clearCallingIdentity();
+                try {
+                    if (mNightMode == UiModeManager.MODE_NIGHT_AUTO) {
+                        try {
+                            getContext().unregisterReceiver(mOnScreenOffHandler);
+                        } catch (IllegalArgumentException e) {
+                        }
+                        mNightModeOverride = active
+                                ? UiModeManager.MODE_NIGHT_YES : UiModeManager.MODE_NIGHT_NO;
+                    } else if (mNightMode == UiModeManager.MODE_NIGHT_NO
+                            && active) {
+                        mNightMode = UiModeManager.MODE_NIGHT_YES;
+                    } else if (mNightMode == UiModeManager.MODE_NIGHT_YES
+                            && !active) {
+                        mNightMode = UiModeManager.MODE_NIGHT_NO;
+                    }
+                    updateConfigurationLocked();
+                    sendConfigurationLocked();
+                    return true;
+                } finally {
+                    Binder.restoreCallingIdentity(ident);
+                }
+            }
+        }
     };
 
     void dumpImpl(PrintWriter pw) {
@@ -848,6 +942,20 @@
             if (state != null) {
                 mComputedNightMode = state.isNight();
             }
+            if (mNightModeOverride == UiModeManager.MODE_NIGHT_YES && !mComputedNightMode) {
+                mComputedNightMode = true;
+                return;
+            }
+            if (mNightModeOverride == UiModeManager.MODE_NIGHT_NO && mComputedNightMode) {
+                mComputedNightMode = false;
+                return;
+            }
+
+            mNightModeOverride = mNightMode;
+            final int user = UserHandle.getCallingUserId();
+            Secure.putIntForUser(getContext().getContentResolver(),
+                    OVERRIDE_NIGHT_MODE, mNightModeOverride, user);
+
         }
     }
 
diff --git a/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java
new file mode 100644
index 0000000..338f837
--- /dev/null
+++ b/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 20019 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;
+
+import android.app.IUiModeManager;
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.os.PowerManager;
+import android.os.RemoteException;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import com.android.server.twilight.TwilightManager;
+import com.android.server.wm.WindowManagerInternal;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import static android.app.UiModeManager.MODE_NIGHT_AUTO;
+import static android.app.UiModeManager.MODE_NIGHT_NO;
+import static android.app.UiModeManager.MODE_NIGHT_YES;
+import static junit.framework.TestCase.assertFalse;
+import static junit.framework.TestCase.assertTrue;
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.BDDMockito.given;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class UiModeManagerServiceTest extends UiServiceTestCase {
+    private UiModeManagerService mUiManagerService;
+    private IUiModeManager mService;
+    @Mock
+    private ContentResolver mContentResolver;
+    @Mock
+    private WindowManagerInternal mWindowManager;
+    @Mock
+    private Context mContext;
+    @Mock
+    private Resources mResources;
+    @Mock
+    TwilightManager mTwilightManager;
+    @Mock
+    PowerManager.WakeLock mWakeLock;
+    private Set<BroadcastReceiver> mScreenOffRecievers;
+
+    @Before
+    public void setUp() {
+        mUiManagerService = new UiModeManagerService(mContext, mWindowManager, mWakeLock,
+                mTwilightManager, true);
+        mScreenOffRecievers = new HashSet<>();
+        mService = mUiManagerService.getService();
+        when(mContext.checkCallingOrSelfPermission(anyString()))
+                .thenReturn(PackageManager.PERMISSION_GRANTED);
+        when(mContext.getResources()).thenReturn(mResources);
+        when(mContext.getContentResolver()).thenReturn(mContentResolver);
+        when(mContext.registerReceiver(any(), any())).then(inv -> {
+            mScreenOffRecievers.add(inv.getArgument(0));
+            return null;
+        });
+    }
+
+    @Test
+    public void setAutoMode_screenOffRegistered() throws RemoteException {
+        try {
+            mService.setNightMode(MODE_NIGHT_NO);
+        } catch (SecurityException e) { /* we should ignore this update config exception*/ }
+        mService.setNightMode(MODE_NIGHT_AUTO);
+        verify(mContext).registerReceiver(any(BroadcastReceiver.class), any());
+    }
+
+    @Test
+    public void setAutoMode_screenOffUnRegistered() throws RemoteException {
+        try {
+            mService.setNightMode(MODE_NIGHT_AUTO);
+        } catch (SecurityException e) { /* we should ignore this update config exception*/ }
+        try {
+            mService.setNightMode(MODE_NIGHT_NO);
+        } catch (SecurityException e) { /*we should ignore this update config exception*/ }
+        given(mContext.registerReceiver(any(), any())).willThrow(SecurityException.class);
+        verify(mContext).unregisterReceiver(any(BroadcastReceiver.class));
+    }
+
+    @Test
+    public void setNightModeActive_fromNightModeYesToNoWhenFalse() throws RemoteException {
+        try {
+            mService.setNightMode(MODE_NIGHT_YES);
+        } catch (SecurityException e) { /* we should ignore this update config exception*/ }
+        try {
+            mService.setNightModeActivated(false);
+        } catch (SecurityException e) { /* we should ignore this update config exception*/ }
+        assertEquals(MODE_NIGHT_NO, mService.getNightMode());
+    }
+
+    @Test
+    public void setNightModeActive_fromNightModeNoToYesWhenTrue() throws RemoteException {
+        try {
+            mService.setNightMode(MODE_NIGHT_NO);
+        } catch (SecurityException e) { /* we should ignore this update config exception*/ }
+        try {
+            mService.setNightModeActivated(true);
+        } catch (SecurityException e) { /* we should ignore this update config exception*/ }
+        assertEquals(MODE_NIGHT_YES, mService.getNightMode());
+    }
+
+    @Test
+    public void setNightModeActive_autoNightModeNoChanges() throws RemoteException {
+        try {
+            mService.setNightMode(MODE_NIGHT_AUTO);
+        } catch (SecurityException e) { /* we should ignore this update config exception*/ }
+        try {
+            mService.setNightModeActivated(true);
+        } catch (SecurityException e) { /* we should ignore this update config exception*/ }
+        assertEquals(MODE_NIGHT_AUTO, mService.getNightMode());
+    }
+
+    @Test
+    public void isNightModeActive_nightModeYes() throws RemoteException {
+        try {
+            mService.setNightMode(MODE_NIGHT_YES);
+        } catch (SecurityException e) { /* we should ignore this update config exception*/ }
+        assertTrue(isNightModeActivated());
+    }
+
+    @Test
+    public void isNightModeActive_nightModeNo() throws RemoteException {
+        try {
+            mService.setNightMode(MODE_NIGHT_NO);
+        } catch (SecurityException e) { /* we should ignore this update config exception*/ }
+        assertFalse(isNightModeActivated());
+    }
+
+    private boolean isNightModeActivated() {
+        return (mUiManagerService.getConfiguration().uiMode
+                & Configuration.UI_MODE_NIGHT_YES) != 0;
+    }
+}