allow locking UI mode and block launching car dock for automotive

- add config to lock UI mode change: When this is enabled,
  request to change car mode will be ignored. Apps can check
  the config using UiModeManager.isUiModeLocked()
- add config to lock day / night mode: When this is enabled,
  apps cannot change day / night mode without having
  MODIFY_DAY_NIGHT_MODE permission.
  Apps can check the config using UiModeManager.isNightModeLocked()
- add config to disable car dock intent launch for home key
  and mode change
- All new configs have default values which keeps the current behavior.
  Car products should override the configs to get desirable behavior.

bug: 22700993
Change-Id: Ic0e58f3428151e0b1c19a2e9a7d6ded32ff962a6
diff --git a/services/core/java/com/android/server/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java
index 0b67ad8..6f713cd 100644
--- a/services/core/java/com/android/server/UiModeManagerService.java
+++ b/services/core/java/com/android/server/UiModeManagerService.java
@@ -57,7 +57,6 @@
     private static final boolean LOG = false;
 
     // Enable launching of applications when entering the dock.
-    private static final boolean ENABLE_LAUNCH_CAR_DOCK_APP = true;
     private static final boolean ENABLE_LAUNCH_DESK_DOCK_APP = true;
 
     final Object mLock = new Object();
@@ -76,6 +75,13 @@
     private boolean mComputedNightMode;
     private int mCarModeEnableFlags;
 
+    // flag set by resource, whether to enable Car dock launch when starting car mode.
+    private boolean mEnableCarDockLaunch = true;
+    // flag set by resource, whether to lock UI mode to the default one or not.
+    private boolean mUiModeLocked = false;
+    // flag set by resource, whether to night mode change for normal all or not.
+    private boolean mNightModeLocked = false;
+
     int mCurUiMode = 0;
     private int mSetUiMode = 0;
     private boolean mHoldingConfiguration = false;
@@ -176,6 +182,10 @@
                 com.android.internal.R.integer.config_carDockKeepsScreenOn) == 1);
         mDeskModeKeepsScreenOn = (res.getInteger(
                 com.android.internal.R.integer.config_deskDockKeepsScreenOn) == 1);
+        mEnableCarDockLaunch = res.getBoolean(
+                com.android.internal.R.bool.config_enableCarDockHomeLaunch);
+        mUiModeLocked = res.getBoolean(com.android.internal.R.bool.config_lockUiMode);
+        mNightModeLocked = res.getBoolean(com.android.internal.R.bool.config_lockDayNightMode);
 
         final PackageManager pm = context.getPackageManager();
         mTelevision = pm.hasSystemFeature(PackageManager.FEATURE_TELEVISION)
@@ -199,6 +209,10 @@
     private final IBinder mService = new IUiModeManager.Stub() {
         @Override
         public void enableCarMode(int flags) {
+            if (isUiModeLocked()) {
+                Slog.e(TAG, "enableCarMode while UI mode is locked");
+                return;
+            }
             final long ident = Binder.clearCallingIdentity();
             try {
                 synchronized (mLock) {
@@ -214,6 +228,10 @@
 
         @Override
         public void disableCarMode(int flags) {
+            if (isUiModeLocked()) {
+                Slog.e(TAG, "disableCarMode while UI mode is locked");
+                return;
+            }
             final long ident = Binder.clearCallingIdentity();
             try {
                 synchronized (mLock) {
@@ -241,6 +259,13 @@
 
         @Override
         public void setNightMode(int mode) {
+            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;
+            }
             switch (mode) {
                 case UiModeManager.MODE_NIGHT_NO:
                 case UiModeManager.MODE_NIGHT_YES:
@@ -273,6 +298,20 @@
         }
 
         @Override
+        public boolean isUiModeLocked() {
+            synchronized (mLock) {
+                return mUiModeLocked;
+            }
+        }
+
+        @Override
+        public boolean isNightModeLocked() {
+            synchronized (mLock) {
+                return mNightModeLocked;
+            }
+        }
+
+        @Override
         protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
             if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
                     != PackageManager.PERMISSION_GRANTED) {
@@ -293,10 +332,13 @@
             pw.print("  mDockState="); pw.print(mDockState);
                     pw.print(" mLastBroadcastState="); pw.println(mLastBroadcastState);
             pw.print("  mNightMode="); pw.print(mNightMode);
+                    pw.print(" mNightModeLocked="); pw.print(mNightModeLocked);
                     pw.print(" mCarModeEnabled="); pw.print(mCarModeEnabled);
                     pw.print(" mComputedNightMode="); pw.print(mComputedNightMode);
-                    pw.print(" mCarModeEnableFlags="); pw.println(mCarModeEnableFlags);
+                    pw.print(" mCarModeEnableFlags="); pw.print(mCarModeEnableFlags);
+                    pw.print(" mEnableCarDockLaunch="); pw.println(mEnableCarDockLaunch);
             pw.print("  mCurUiMode=0x"); pw.print(Integer.toHexString(mCurUiMode));
+                    pw.print(" mUiModeLocked="); pw.print(mUiModeLocked);
                     pw.print(" mSetUiMode=0x"); pw.println(Integer.toHexString(mSetUiMode));
             pw.print("  mHoldingConfiguration="); pw.print(mHoldingConfiguration);
                     pw.print(" mSystemReady="); pw.println(mSystemReady);
@@ -356,7 +398,9 @@
 
     private void updateConfigurationLocked() {
         int uiMode = mDefaultUiModeType;
-        if (mTelevision) {
+        if (mUiModeLocked) {
+            // no-op, keeps default one
+        } else if (mTelevision) {
             uiMode = Configuration.UI_MODE_TYPE_TELEVISION;
         } else if (mWatch) {
             uiMode = Configuration.UI_MODE_TYPE_WATCH;
@@ -460,7 +504,7 @@
         } else {
             String category = null;
             if (mCarModeEnabled) {
-                if (ENABLE_LAUNCH_CAR_DOCK_APP
+                if (mEnableCarDockLaunch
                         && (enableFlags & UiModeManager.ENABLE_CAR_MODE_GO_CAR_HOME) != 0) {
                     category = Intent.CATEGORY_CAR_DOCK;
                 }
@@ -503,7 +547,7 @@
         if (UiModeManager.ACTION_ENTER_CAR_MODE.equals(action)) {
             // Only launch car home when car mode is enabled and the caller
             // has asked us to switch to it.
-            if (ENABLE_LAUNCH_CAR_DOCK_APP
+            if (mEnableCarDockLaunch
                     && (enableFlags & UiModeManager.ENABLE_CAR_MODE_GO_CAR_HOME) != 0) {
                 category = Intent.CATEGORY_CAR_DOCK;
             }
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 9a7d153..2031061 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -158,8 +158,7 @@
     static final boolean SHOW_PROCESSES_ON_ALT_MENU = false;
 
     // Whether to allow dock apps with METADATA_DOCK_HOME to temporarily take over the Home key.
-    // No longer recommended for desk docks; still useful in car docks.
-    static final boolean ENABLE_CAR_DOCK_HOME_CAPTURE = true;
+    // No longer recommended for desk docks;
     static final boolean ENABLE_DESK_DOCK_HOME_CAPTURE = false;
 
     static final int SHORT_PRESS_POWER_NOTHING = 0;
@@ -316,6 +315,10 @@
     int[] mNavigationBarHeightForRotation = new int[4];
     int[] mNavigationBarWidthForRotation = new int[4];
 
+    // Whether to allow dock apps with METADATA_DOCK_HOME to temporarily take over the Home key.
+    // This is for car dock and this is updated from resource.
+    private boolean mEnableCarDockHomeCapture = true;
+
     boolean mBootMessageNeedsHiding;
     KeyguardServiceDelegate mKeyguardDelegate;
     final Runnable mWindowManagerDrawCallback = new Runnable() {
@@ -1398,6 +1401,8 @@
         mHomeIntent.addCategory(Intent.CATEGORY_HOME);
         mHomeIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
                 | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
+        mEnableCarDockHomeCapture = context.getResources().getBoolean(
+                com.android.internal.R.bool.config_enableCarDockHomeLaunch);
         mCarDockIntent =  new Intent(Intent.ACTION_MAIN, null);
         mCarDockIntent.addCategory(Intent.CATEGORY_CAR_DOCK);
         mCarDockIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
@@ -6404,7 +6409,7 @@
      * true:
      * <ul>
      *  <li>The device is not in either car mode or desk mode
-     *  <li>The device is in car mode but ENABLE_CAR_DOCK_HOME_CAPTURE is false
+     *  <li>The device is in car mode but mEnableCarDockHomeCapture is false
      *  <li>The device is in desk mode but ENABLE_DESK_DOCK_HOME_CAPTURE is false
      *  <li>The device is in car mode but there's no CAR_DOCK app with METADATA_DOCK_HOME
      *  <li>The device is in desk mode but there's no DESK_DOCK app with METADATA_DOCK_HOME
@@ -6418,7 +6423,7 @@
         // is, when in car mode you should be taken to car home regardless
         // of whether we are actually in a car dock.
         if (mUiMode == Configuration.UI_MODE_TYPE_CAR) {
-            if (ENABLE_CAR_DOCK_HOME_CAPTURE) {
+            if (mEnableCarDockHomeCapture) {
                 intent = mCarDockIntent;
             }
         } else if (mUiMode == Configuration.UI_MODE_TYPE_DESK) {
@@ -6938,6 +6943,7 @@
         pw.print(prefix); pw.print("mSupportAutoRotation="); pw.println(mSupportAutoRotation);
         pw.print(prefix); pw.print("mUiMode="); pw.print(mUiMode);
                 pw.print(" mDockMode="); pw.print(mDockMode);
+                pw.print(" mEnableCarDockHomeCapture="); pw.print(mEnableCarDockHomeCapture);
                 pw.print(" mCarDockRotation="); pw.print(mCarDockRotation);
                 pw.print(" mDeskDockRotation="); pw.println(mDeskDockRotation);
         pw.print(prefix); pw.print("mUserRotationMode="); pw.print(mUserRotationMode);