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