blob: 6b5366a39a4376dfead9b99548af4e585182a261 [file] [log] [blame]
/*
* Copyright (C) 2008 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.annotation.Nullable;
import android.app.Activity;
import android.app.ActivityManager;
import android.app.IUiModeManager;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.StatusBarManager;
import android.app.UiModeManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.os.BatteryManager;
import android.os.Binder;
import android.os.Handler;
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ServiceManager;
import android.os.ShellCallback;
import android.os.ShellCommand;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.provider.Settings;
import android.service.dreams.Sandman;
import android.service.vr.IVrManager;
import android.service.vr.IVrStateCallbacks;
import android.text.TextUtils;
import android.util.Slog;
import java.io.File;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
import com.android.internal.R;
import com.android.internal.app.DisableCarModeActivity;
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.internal.notification.SystemNotificationChannels;
import com.android.internal.util.DumpUtils;
import com.android.server.power.ShutdownThread;
import com.android.server.twilight.TwilightListener;
import com.android.server.twilight.TwilightManager;
import com.android.server.twilight.TwilightState;
final class UiModeManagerService extends SystemService {
private static final String TAG = UiModeManager.class.getSimpleName();
private static final boolean LOG = false;
// Enable launching of applications when entering the dock.
private static final boolean ENABLE_LAUNCH_DESK_DOCK_APP = true;
final Object mLock = new Object();
private int mDockState = Intent.EXTRA_DOCK_STATE_UNDOCKED;
private int mLastBroadcastState = Intent.EXTRA_DOCK_STATE_UNDOCKED;
private int mNightMode = UiModeManager.MODE_NIGHT_NO;
private boolean mCarModeEnabled = false;
private boolean mCharging = false;
private int mDefaultUiModeType;
private boolean mCarModeKeepsScreenOn;
private boolean mDeskModeKeepsScreenOn;
private boolean mTelevision;
private boolean mWatch;
private boolean mVrHeadset;
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;
private Configuration mConfiguration = new Configuration();
boolean mSystemReady;
private final Handler mHandler = new Handler();
private TwilightManager mTwilightManager;
private NotificationManager mNotificationManager;
private StatusBarManager mStatusBarManager;
private PowerManager.WakeLock mWakeLock;
public UiModeManagerService(Context context) {
super(context);
}
private static Intent buildHomeIntent(String category) {
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(category);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
return intent;
}
// The broadcast receiver which receives the result of the ordered broadcast sent when
// the dock state changes. The original ordered broadcast is sent with an initial result
// code of RESULT_OK. If any of the registered broadcast receivers changes this value, e.g.,
// to RESULT_CANCELED, then the intent to start a dock app will not be sent.
private final BroadcastReceiver mResultReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (getResultCode() != Activity.RESULT_OK) {
if (LOG) {
Slog.v(TAG, "Handling broadcast result for action " + intent.getAction()
+ ": canceled: " + getResultCode());
}
return;
}
final int enableFlags = intent.getIntExtra("enableFlags", 0);
final int disableFlags = intent.getIntExtra("disableFlags", 0);
synchronized (mLock) {
updateAfterBroadcastLocked(intent.getAction(), enableFlags, disableFlags);
}
}
};
private final BroadcastReceiver mDockModeReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
int state = intent.getIntExtra(Intent.EXTRA_DOCK_STATE,
Intent.EXTRA_DOCK_STATE_UNDOCKED);
updateDockState(state);
}
};
private final BroadcastReceiver mBatteryReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
mCharging = (intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0) != 0);
synchronized (mLock) {
if (mSystemReady) {
updateLocked(0, 0);
}
}
}
};
private final TwilightListener mTwilightListener = new TwilightListener() {
@Override
public void onTwilightStateChanged(@Nullable TwilightState state) {
synchronized (mLock) {
if (mNightMode == UiModeManager.MODE_NIGHT_AUTO) {
updateComputedNightModeLocked();
updateLocked(0, 0);
}
}
}
};
private final IVrStateCallbacks mVrStateCallbacks = new IVrStateCallbacks.Stub() {
@Override
public void onVrStateChanged(boolean enabled) {
synchronized (mLock) {
mVrHeadset = enabled;
if (mSystemReady) {
updateLocked(0, 0);
}
}
}
};
@Override
public void onStart() {
final Context context = getContext();
final PowerManager powerManager =
(PowerManager) context.getSystemService(Context.POWER_SERVICE);
mWakeLock = powerManager.newWakeLock(PowerManager.FULL_WAKE_LOCK, TAG);
context.registerReceiver(mDockModeReceiver,
new IntentFilter(Intent.ACTION_DOCK_EVENT));
context.registerReceiver(mBatteryReceiver,
new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
mConfiguration.setToDefaults();
final Resources res = context.getResources();
mDefaultUiModeType = res.getInteger(
com.android.internal.R.integer.config_defaultUiModeType);
mCarModeKeepsScreenOn = (res.getInteger(
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)
|| pm.hasSystemFeature(PackageManager.FEATURE_LEANBACK);
mWatch = pm.hasSystemFeature(PackageManager.FEATURE_WATCH);
final int defaultNightMode = res.getInteger(
com.android.internal.R.integer.config_defaultNightMode);
mNightMode = Settings.Secure.getInt(context.getContentResolver(),
Settings.Secure.UI_NIGHT_MODE, defaultNightMode);
// Update the initial, static configurations.
SystemServerInitThreadPool.get().submit(() -> {
synchronized (mLock) {
updateConfigurationLocked();
sendConfigurationLocked();
}
}, TAG + ".onStart");
publishBinderService(Context.UI_MODE_SERVICE, mService);
}
private final IUiModeManager.Stub 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) {
setCarModeLocked(true, flags);
if (mSystemReady) {
updateLocked(flags, 0);
}
}
} finally {
Binder.restoreCallingIdentity(ident);
}
}
@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) {
setCarModeLocked(false, 0);
if (mSystemReady) {
updateLocked(0, flags);
}
}
} finally {
Binder.restoreCallingIdentity(ident);
}
}
@Override
public int getCurrentModeType() {
final long ident = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
return mCurUiMode & Configuration.UI_MODE_TYPE_MASK;
}
} finally {
Binder.restoreCallingIdentity(ident);
}
}
@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:
case UiModeManager.MODE_NIGHT_AUTO:
break;
default:
throw new IllegalArgumentException("Unknown mode: " + mode);
}
final long ident = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
if (mNightMode != mode) {
Settings.Secure.putInt(getContext().getContentResolver(),
Settings.Secure.UI_NIGHT_MODE, mode);
mNightMode = mode;
updateLocked(0, 0);
}
}
} finally {
Binder.restoreCallingIdentity(ident);
}
}
@Override
public int getNightMode() {
synchronized (mLock) {
return mNightMode;
}
}
@Override
public boolean isUiModeLocked() {
synchronized (mLock) {
return mUiModeLocked;
}
}
@Override
public boolean isNightModeLocked() {
synchronized (mLock) {
return mNightModeLocked;
}
}
@Override
public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
String[] args, ShellCallback callback, ResultReceiver resultReceiver) {
new Shell(mService).exec(mService, in, out, err, args, callback, resultReceiver);
}
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) return;
dumpImpl(pw);
}
};
void dumpImpl(PrintWriter pw) {
synchronized (mLock) {
pw.println("Current UI Mode Service state:");
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.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);
if (mTwilightManager != null) {
// We may not have a TwilightManager.
pw.print(" mTwilightService.getLastTwilightState()=");
pw.println(mTwilightManager.getLastTwilightState());
}
}
}
@Override
public void onBootPhase(int phase) {
if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
synchronized (mLock) {
mTwilightManager = getLocalService(TwilightManager.class);
mSystemReady = true;
mCarModeEnabled = mDockState == Intent.EXTRA_DOCK_STATE_CAR;
updateComputedNightModeLocked();
registerVrStateListener();
updateLocked(0, 0);
}
}
}
void setCarModeLocked(boolean enabled, int flags) {
if (mCarModeEnabled != enabled) {
mCarModeEnabled = enabled;
}
mCarModeEnableFlags = flags;
}
private void updateDockState(int newState) {
synchronized (mLock) {
if (newState != mDockState) {
mDockState = newState;
setCarModeLocked(mDockState == Intent.EXTRA_DOCK_STATE_CAR, 0);
if (mSystemReady) {
updateLocked(UiModeManager.ENABLE_CAR_MODE_GO_CAR_HOME, 0);
}
}
}
}
private static boolean isDeskDockState(int state) {
switch (state) {
case Intent.EXTRA_DOCK_STATE_DESK:
case Intent.EXTRA_DOCK_STATE_LE_DESK:
case Intent.EXTRA_DOCK_STATE_HE_DESK:
return true;
default:
return false;
}
}
private void updateConfigurationLocked() {
int uiMode = mDefaultUiModeType;
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;
} else if (mCarModeEnabled) {
uiMode = Configuration.UI_MODE_TYPE_CAR;
} else if (isDeskDockState(mDockState)) {
uiMode = Configuration.UI_MODE_TYPE_DESK;
} else if (mVrHeadset) {
uiMode = Configuration.UI_MODE_TYPE_VR_HEADSET;
}
if (mNightMode == UiModeManager.MODE_NIGHT_AUTO) {
if (mTwilightManager != null) {
mTwilightManager.registerListener(mTwilightListener, mHandler);
}
updateComputedNightModeLocked();
uiMode |= mComputedNightMode ? Configuration.UI_MODE_NIGHT_YES
: Configuration.UI_MODE_NIGHT_NO;
} else {
if (mTwilightManager != null) {
mTwilightManager.unregisterListener(mTwilightListener);
}
uiMode |= mNightMode << 4;
}
if (LOG) {
Slog.d(TAG,
"updateConfigurationLocked: mDockState=" + mDockState
+ "; mCarMode=" + mCarModeEnabled
+ "; mNightMode=" + mNightMode
+ "; uiMode=" + uiMode);
}
mCurUiMode = uiMode;
if (!mHoldingConfiguration) {
mConfiguration.uiMode = uiMode;
}
}
private void sendConfigurationLocked() {
if (mSetUiMode != mConfiguration.uiMode) {
mSetUiMode = mConfiguration.uiMode;
try {
ActivityManager.getService().updateConfiguration(mConfiguration);
} catch (RemoteException e) {
Slog.w(TAG, "Failure communicating with activity manager", e);
}
}
}
void updateLocked(int enableFlags, int disableFlags) {
String action = null;
String oldAction = null;
if (mLastBroadcastState == Intent.EXTRA_DOCK_STATE_CAR) {
adjustStatusBarCarModeLocked();
oldAction = UiModeManager.ACTION_EXIT_CAR_MODE;
} else if (isDeskDockState(mLastBroadcastState)) {
oldAction = UiModeManager.ACTION_EXIT_DESK_MODE;
}
if (mCarModeEnabled) {
if (mLastBroadcastState != Intent.EXTRA_DOCK_STATE_CAR) {
adjustStatusBarCarModeLocked();
if (oldAction != null) {
sendForegroundBroadcastToAllUsers(oldAction);
}
mLastBroadcastState = Intent.EXTRA_DOCK_STATE_CAR;
action = UiModeManager.ACTION_ENTER_CAR_MODE;
}
} else if (isDeskDockState(mDockState)) {
if (!isDeskDockState(mLastBroadcastState)) {
if (oldAction != null) {
sendForegroundBroadcastToAllUsers(oldAction);
}
mLastBroadcastState = mDockState;
action = UiModeManager.ACTION_ENTER_DESK_MODE;
}
} else {
mLastBroadcastState = Intent.EXTRA_DOCK_STATE_UNDOCKED;
action = oldAction;
}
if (action != null) {
if (LOG) {
Slog.v(TAG, String.format(
"updateLocked: preparing broadcast: action=%s enable=0x%08x disable=0x%08x",
action, enableFlags, disableFlags));
}
// Send the ordered broadcast; the result receiver will receive after all
// broadcasts have been sent. If any broadcast receiver changes the result
// code from the initial value of RESULT_OK, then the result receiver will
// not launch the corresponding dock application. This gives apps a chance
// to override the behavior and stay in their app even when the device is
// placed into a dock.
Intent intent = new Intent(action);
intent.putExtra("enableFlags", enableFlags);
intent.putExtra("disableFlags", disableFlags);
intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
getContext().sendOrderedBroadcastAsUser(intent, UserHandle.CURRENT, null,
mResultReceiver, null, Activity.RESULT_OK, null, null);
// Attempting to make this transition a little more clean, we are going
// to hold off on doing a configuration change until we have finished
// the broadcast and started the home activity.
mHoldingConfiguration = true;
updateConfigurationLocked();
} else {
String category = null;
if (mCarModeEnabled) {
if (mEnableCarDockLaunch
&& (enableFlags & UiModeManager.ENABLE_CAR_MODE_GO_CAR_HOME) != 0) {
category = Intent.CATEGORY_CAR_DOCK;
}
} else if (isDeskDockState(mDockState)) {
if (ENABLE_LAUNCH_DESK_DOCK_APP
&& (enableFlags & UiModeManager.ENABLE_CAR_MODE_GO_CAR_HOME) != 0) {
category = Intent.CATEGORY_DESK_DOCK;
}
} else {
if ((disableFlags & UiModeManager.DISABLE_CAR_MODE_GO_HOME) != 0) {
category = Intent.CATEGORY_HOME;
}
}
if (LOG) {
Slog.v(TAG, "updateLocked: null action, mDockState="
+ mDockState +", category=" + category);
}
sendConfigurationAndStartDreamOrDockAppLocked(category);
}
// keep screen on when charging and in car mode
boolean keepScreenOn = mCharging &&
((mCarModeEnabled && mCarModeKeepsScreenOn &&
(mCarModeEnableFlags & UiModeManager.ENABLE_CAR_MODE_ALLOW_SLEEP) == 0) ||
(mCurUiMode == Configuration.UI_MODE_TYPE_DESK && mDeskModeKeepsScreenOn));
if (keepScreenOn != mWakeLock.isHeld()) {
if (keepScreenOn) {
mWakeLock.acquire();
} else {
mWakeLock.release();
}
}
}
private void sendForegroundBroadcastToAllUsers(String action) {
getContext().sendBroadcastAsUser(new Intent(action)
.addFlags(Intent.FLAG_RECEIVER_FOREGROUND), UserHandle.ALL);
}
private void updateAfterBroadcastLocked(String action, int enableFlags, int disableFlags) {
// Launch a dock activity
String category = null;
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 (mEnableCarDockLaunch
&& (enableFlags & UiModeManager.ENABLE_CAR_MODE_GO_CAR_HOME) != 0) {
category = Intent.CATEGORY_CAR_DOCK;
}
} else if (UiModeManager.ACTION_ENTER_DESK_MODE.equals(action)) {
// Only launch car home when desk mode is enabled and the caller
// has asked us to switch to it. Currently re-using the car
// mode flag since we don't have a formal API for "desk mode".
if (ENABLE_LAUNCH_DESK_DOCK_APP
&& (enableFlags & UiModeManager.ENABLE_CAR_MODE_GO_CAR_HOME) != 0) {
category = Intent.CATEGORY_DESK_DOCK;
}
} else {
// Launch the standard home app if requested.
if ((disableFlags & UiModeManager.DISABLE_CAR_MODE_GO_HOME) != 0) {
category = Intent.CATEGORY_HOME;
}
}
if (LOG) {
Slog.v(TAG, String.format(
"Handling broadcast result for action %s: enable=0x%08x, disable=0x%08x, "
+ "category=%s",
action, enableFlags, disableFlags, category));
}
sendConfigurationAndStartDreamOrDockAppLocked(category);
}
private void sendConfigurationAndStartDreamOrDockAppLocked(String category) {
// Update the configuration but don't send it yet.
mHoldingConfiguration = false;
updateConfigurationLocked();
// Start the dock app, if there is one.
boolean dockAppStarted = false;
if (category != null) {
// Now we are going to be careful about switching the
// configuration and starting the activity -- we need to
// do this in a specific order under control of the
// activity manager, to do it cleanly. So compute the
// new config, but don't set it yet, and let the
// activity manager take care of both the start and config
// change.
Intent homeIntent = buildHomeIntent(category);
if (Sandman.shouldStartDockApp(getContext(), homeIntent)) {
try {
int result = ActivityManager.getService().startActivityWithConfig(
null, null, homeIntent, null, null, null, 0, 0,
mConfiguration, null, UserHandle.USER_CURRENT);
if (ActivityManager.isStartResultSuccessful(result)) {
dockAppStarted = true;
} else if (result != ActivityManager.START_INTENT_NOT_RESOLVED) {
Slog.e(TAG, "Could not start dock app: " + homeIntent
+ ", startActivityWithConfig result " + result);
}
} catch (RemoteException ex) {
Slog.e(TAG, "Could not start dock app: " + homeIntent, ex);
}
}
}
// Send the new configuration.
sendConfigurationLocked();
// If we did not start a dock app, then start dreaming if supported.
if (category != null && !dockAppStarted) {
Sandman.startDreamWhenDockedIfAppropriate(getContext());
}
}
private void adjustStatusBarCarModeLocked() {
final Context context = getContext();
if (mStatusBarManager == null) {
mStatusBarManager = (StatusBarManager)
context.getSystemService(Context.STATUS_BAR_SERVICE);
}
// Fear not: StatusBarManagerService manages a list of requests to disable
// features of the status bar; these are ORed together to form the
// active disabled list. So if (for example) the device is locked and
// the status bar should be totally disabled, the calls below will
// have no effect until the device is unlocked.
if (mStatusBarManager != null) {
mStatusBarManager.disable(mCarModeEnabled
? StatusBarManager.DISABLE_NOTIFICATION_TICKER
: StatusBarManager.DISABLE_NONE);
}
if (mNotificationManager == null) {
mNotificationManager = (NotificationManager)
context.getSystemService(Context.NOTIFICATION_SERVICE);
}
if (mNotificationManager != null) {
if (mCarModeEnabled) {
Intent carModeOffIntent = new Intent(context, DisableCarModeActivity.class);
Notification.Builder n =
new Notification.Builder(context, SystemNotificationChannels.CAR_MODE)
.setSmallIcon(R.drawable.stat_notify_car_mode)
.setDefaults(Notification.DEFAULT_LIGHTS)
.setOngoing(true)
.setWhen(0)
.setColor(context.getColor(
com.android.internal.R.color.system_notification_accent_color))
.setContentTitle(
context.getString(R.string.car_mode_disable_notification_title))
.setContentText(
context.getString(R.string.car_mode_disable_notification_message))
.setContentIntent(
PendingIntent.getActivityAsUser(context, 0, carModeOffIntent, 0,
null, UserHandle.CURRENT));
mNotificationManager.notifyAsUser(null,
SystemMessage.NOTE_CAR_MODE_DISABLE, n.build(), UserHandle.ALL);
} else {
mNotificationManager.cancelAsUser(null,
SystemMessage.NOTE_CAR_MODE_DISABLE, UserHandle.ALL);
}
}
}
private void updateComputedNightModeLocked() {
if (mTwilightManager != null) {
TwilightState state = mTwilightManager.getLastTwilightState();
if (state != null) {
mComputedNightMode = state.isNight();
}
}
}
private void registerVrStateListener() {
IVrManager vrManager = IVrManager.Stub.asInterface(ServiceManager.getService(
Context.VR_SERVICE));
try {
if (vrManager != null) {
vrManager.registerListener(mVrStateCallbacks);
}
} catch (RemoteException e) {
Slog.e(TAG, "Failed to register VR mode state listener: " + e);
}
}
/**
* Handles "adb shell" commands.
*/
private static class Shell extends ShellCommand {
public static final String NIGHT_MODE_STR_YES = "yes";
public static final String NIGHT_MODE_STR_NO = "no";
public static final String NIGHT_MODE_STR_AUTO = "auto";
public static final String NIGHT_MODE_STR_UNKNOWN = "unknown";
private final IUiModeManager mInterface;
Shell(IUiModeManager iface) {
mInterface = iface;
}
@Override
public void onHelp() {
final PrintWriter pw = getOutPrintWriter();
pw.println("UiModeManager service (uimode) commands:");
pw.println(" help");
pw.println(" Print this help text.");
pw.println(" night [yes|no|auto]");
pw.println(" Set or read night mode.");
}
@Override
public int onCommand(String cmd) {
if (cmd == null) {
return handleDefaultCommands(cmd);
}
try {
switch (cmd) {
case "night":
return handleNightMode();
default:
return handleDefaultCommands(cmd);
}
} catch (RemoteException e) {
final PrintWriter err = getErrPrintWriter();
err.println("Remote exception: " + e);
}
return -1;
}
private int handleNightMode() throws RemoteException {
final PrintWriter err = getErrPrintWriter();
final String modeStr = getNextArg();
if (modeStr == null) {
printCurrentNightMode();
return 0;
}
final int mode = strToNightMode(modeStr);
if (mode >= 0) {
mInterface.setNightMode(mode);
printCurrentNightMode();
return 0;
} else {
err.println("Error: mode must be '" + NIGHT_MODE_STR_YES + "', '"
+ NIGHT_MODE_STR_NO + "', or '" + NIGHT_MODE_STR_AUTO + "'");
return -1;
}
}
private void printCurrentNightMode() throws RemoteException {
final PrintWriter pw = getOutPrintWriter();
final int currMode = mInterface.getNightMode();
final String currModeStr = nightModeToStr(currMode);
pw.println("Night mode: " + currModeStr);
}
private static String nightModeToStr(int mode) {
switch (mode) {
case UiModeManager.MODE_NIGHT_YES:
return NIGHT_MODE_STR_YES;
case UiModeManager.MODE_NIGHT_NO:
return NIGHT_MODE_STR_NO;
case UiModeManager.MODE_NIGHT_AUTO:
return NIGHT_MODE_STR_AUTO;
default:
return NIGHT_MODE_STR_UNKNOWN;
}
}
private static int strToNightMode(String modeStr) {
switch (modeStr) {
case NIGHT_MODE_STR_YES:
return UiModeManager.MODE_NIGHT_YES;
case NIGHT_MODE_STR_NO:
return UiModeManager.MODE_NIGHT_NO;
case NIGHT_MODE_STR_AUTO:
return UiModeManager.MODE_NIGHT_AUTO;
default:
return -1;
}
}
}
}