| /* | |
| * 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; | |
| } | |
| } | |
| } | |
| } |