src.com.android.server.display.DisplayManagerService Maven / Gradle / Ivy
Show all versions of android-all Show documentation
/*
* Copyright (C) 2012 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.display;
import static android.Manifest.permission.ADD_TRUSTED_DISPLAY;
import static android.Manifest.permission.CAPTURE_SECURE_VIDEO_OUTPUT;
import static android.Manifest.permission.CAPTURE_VIDEO_OUTPUT;
import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW;
import static android.hardware.display.DisplayManager.EventsMask;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_DISPLAY_GROUP;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_SECURE;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_TRUSTED;
import static android.hardware.display.DisplayManagerGlobal.DisplayEvent;
import static android.hardware.display.DisplayViewport.VIEWPORT_EXTERNAL;
import static android.hardware.display.DisplayViewport.VIEWPORT_INTERNAL;
import static android.hardware.display.DisplayViewport.VIEWPORT_VIRTUAL;
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.AppOpsManager;
import android.app.compat.CompatChanges;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledSince;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.ParceledListSlice;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.database.ContentObserver;
import android.graphics.ColorSpace;
import android.graphics.Point;
import android.hardware.Sensor;
import android.hardware.SensorManager;
import android.hardware.devicestate.DeviceStateManager;
import android.hardware.display.AmbientBrightnessDayStats;
import android.hardware.display.BrightnessChangeEvent;
import android.hardware.display.BrightnessConfiguration;
import android.hardware.display.BrightnessInfo;
import android.hardware.display.Curve;
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManagerGlobal;
import android.hardware.display.DisplayManagerInternal;
import android.hardware.display.DisplayManagerInternal.DisplayGroupListener;
import android.hardware.display.DisplayManagerInternal.DisplayTransactionListener;
import android.hardware.display.DisplayManagerInternal.RefreshRateLimitation;
import android.hardware.display.DisplayManagerInternal.RefreshRateRange;
import android.hardware.display.DisplayViewport;
import android.hardware.display.DisplayedContentSample;
import android.hardware.display.DisplayedContentSamplingAttributes;
import android.hardware.display.IDisplayManager;
import android.hardware.display.IDisplayManagerCallback;
import android.hardware.display.IVirtualDisplayCallback;
import android.hardware.display.VirtualDisplayConfig;
import android.hardware.display.WifiDisplayStatus;
import android.hardware.input.InputManagerInternal;
import android.media.projection.IMediaProjection;
import android.media.projection.IMediaProjectionManager;
import android.net.Uri;
import android.os.Binder;
import android.os.Handler;
import android.os.HandlerExecutor;
import android.os.IBinder;
import android.os.IBinder.DeathRecipient;
import android.os.Looper;
import android.os.Message;
import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ServiceManager;
import android.os.ShellCallback;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.Trace;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
import android.sysprop.DisplayProperties;
import android.text.TextUtils;
import android.util.ArraySet;
import android.util.EventLog;
import android.util.IntArray;
import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseIntArray;
import android.util.Spline;
import android.view.Display;
import android.view.DisplayEventReceiver;
import android.view.DisplayInfo;
import android.view.Surface;
import android.view.SurfaceControl;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.display.BrightnessSynchronizer;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.AnimationThread;
import com.android.server.DisplayThread;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.UiThread;
import com.android.server.display.DisplayDeviceConfig.SensorData;
import com.android.server.display.utils.SensorUtils;
import com.android.server.wm.SurfaceAnimationThread;
import com.android.server.wm.WindowManagerInternal;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Consumer;
/**
* Manages attached displays.
*
* The {@link DisplayManagerService} manages the global lifecycle of displays,
* decides how to configure logical displays based on the physical display devices currently
* attached, sends notifications to the system and to applications when the state
* changes, and so on.
*
* The display manager service relies on a collection of {@link DisplayAdapter} components,
* for discovering and configuring physical display devices attached to the system.
* There are separate display adapters for each manner that devices are attached:
* one display adapter for physical displays, one for simulated non-functional
* displays when the system is headless, one for simulated overlay displays used for
* development, one for wifi displays, etc.
*
* Display adapters are only weakly coupled to the display manager service.
* Display adapters communicate changes in display device state to the display manager
* service asynchronously via a {@link DisplayAdapter.Listener}, and through
* the {@link DisplayDeviceRepository.Listener}, which is ultimately registered
* by the display manager service. This separation of concerns is important for
* two main reasons. First, it neatly encapsulates the responsibilities of these
* two classes: display adapters handle individual display devices whereas
* the display manager service handles the global state. Second, it eliminates
* the potential for deadlocks resulting from asynchronous display device discovery.
*
*
* Synchronization
*
* Because the display manager may be accessed by multiple threads, the synchronization
* story gets a little complicated. In particular, the window manager may call into
* the display manager while holding a surface transaction with the expectation that
* it can apply changes immediately. Unfortunately, that means we can't just do
* everything asynchronously (*grump*).
*
* To make this work, all of the objects that belong to the display manager must
* use the same lock. We call this lock the synchronization root and it has a unique
* type {@link DisplayManagerService.SyncRoot}. Methods that require this lock are
* named with the "Locked" suffix.
*
* Where things get tricky is that the display manager is not allowed to make
* any potentially reentrant calls, especially into the window manager. We generally
* avoid this by making all potentially reentrant out-calls asynchronous.
*
*/
public final class DisplayManagerService extends SystemService {
private static final String TAG = "DisplayManagerService";
private static final boolean DEBUG = false;
// When this system property is set to 0, WFD is forcibly disabled on boot.
// When this system property is set to 1, WFD is forcibly enabled on boot.
// Otherwise WFD is enabled according to the value of config_enableWifiDisplay.
private static final String FORCE_WIFI_DISPLAY_ENABLE = "persist.debug.wfd.enable";
private static final String PROP_DEFAULT_DISPLAY_TOP_INSET = "persist.sys.displayinset.top";
private static final long WAIT_FOR_DEFAULT_DISPLAY_TIMEOUT = 10000;
// This value needs to be in sync with the threshold
// in RefreshRateConfigs::getFrameRateDivider.
private static final float THRESHOLD_FOR_REFRESH_RATES_DIVIDERS = 0.0009f;
private static final int MSG_REGISTER_DEFAULT_DISPLAY_ADAPTERS = 1;
private static final int MSG_REGISTER_ADDITIONAL_DISPLAY_ADAPTERS = 2;
private static final int MSG_DELIVER_DISPLAY_EVENT = 3;
private static final int MSG_REQUEST_TRAVERSAL = 4;
private static final int MSG_UPDATE_VIEWPORT = 5;
private static final int MSG_LOAD_BRIGHTNESS_CONFIGURATIONS = 6;
private static final int MSG_DELIVER_DISPLAY_EVENT_FRAME_RATE_OVERRIDE = 7;
private static final int MSG_DELIVER_DISPLAY_GROUP_EVENT = 8;
private final Context mContext;
private final DisplayManagerHandler mHandler;
private final Handler mUiHandler;
private final DisplayModeDirector mDisplayModeDirector;
private WindowManagerInternal mWindowManagerInternal;
private InputManagerInternal mInputManagerInternal;
private IMediaProjectionManager mProjectionService;
private int[] mUserDisabledHdrTypes = {};
private boolean mAreUserDisabledHdrTypesAllowed = true;
// The synchronization root for the display manager.
// This lock guards most of the display manager's state.
// NOTE: This is synchronized on while holding WindowManagerService.mWindowMap so never call
// into WindowManagerService methods that require mWindowMap while holding this unless you are
// very very sure that no deadlock can occur.
private final SyncRoot mSyncRoot = new SyncRoot();
// True if in safe mode.
// This option may disable certain display adapters.
public boolean mSafeMode;
// True if we are in a special boot mode where only core applications and
// services should be started. This option may disable certain display adapters.
public boolean mOnlyCore;
// All callback records indexed by calling process id.
public final SparseArray mCallbacks =
new SparseArray();
// List of all currently registered display adapters.
private final ArrayList mDisplayAdapters = new ArrayList();
/**
* Repository of all active {@link DisplayDevice}s.
*/
private final DisplayDeviceRepository mDisplayDeviceRepo;
/**
* Contains all the {@link LogicalDisplay} instances and is responsible for mapping
* {@link DisplayDevice}s to {@link LogicalDisplay}s. DisplayManagerService listens to display
* event on this object.
*/
private final LogicalDisplayMapper mLogicalDisplayMapper;
// List of all display transaction listeners.
private final CopyOnWriteArrayList mDisplayTransactionListeners =
new CopyOnWriteArrayList();
/** List of all display group listeners. */
private final CopyOnWriteArrayList mDisplayGroupListeners =
new CopyOnWriteArrayList<>();
/** All {@link DisplayPowerController}s indexed by {@link LogicalDisplay} ID. */
private final SparseArray mDisplayPowerControllers =
new SparseArray<>();
/** {@link DisplayBlanker} used by all {@link DisplayPowerController}s. */
private final DisplayBlanker mDisplayBlanker = new DisplayBlanker() {
// Synchronized to avoid race conditions when updating multiple display states.
@Override
public synchronized void requestDisplayState(int displayId, int state, float brightness,
float sdrBrightness) {
boolean allInactive = true;
boolean allOff = true;
final boolean stateChanged;
synchronized (mSyncRoot) {
final int index = mDisplayStates.indexOfKey(displayId);
if (index > -1) {
final int currentState = mDisplayStates.valueAt(index);
stateChanged = state != currentState;
if (stateChanged) {
final int size = mDisplayStates.size();
for (int i = 0; i < size; i++) {
final int displayState = i == index ? state : mDisplayStates.valueAt(i);
if (displayState != Display.STATE_OFF) {
allOff = false;
}
if (Display.isActiveState(displayState)) {
allInactive = false;
}
if (!allOff && !allInactive) {
break;
}
}
}
} else {
stateChanged = false;
}
}
// The order of operations is important for legacy reasons.
if (state == Display.STATE_OFF) {
requestDisplayStateInternal(displayId, state, brightness, sdrBrightness);
}
if (stateChanged) {
mDisplayPowerCallbacks.onDisplayStateChange(allInactive, allOff);
}
if (state != Display.STATE_OFF) {
requestDisplayStateInternal(displayId, state, brightness, sdrBrightness);
}
}
};
/**
* Used to inform {@link com.android.server.power.PowerManagerService} of changes to display
* state.
*/
private DisplayManagerInternal.DisplayPowerCallbacks mDisplayPowerCallbacks;
/** The {@link Handler} used by all {@link DisplayPowerController}s. */
private Handler mPowerHandler;
// A map from LogicalDisplay ID to display power state.
@GuardedBy("mSyncRoot")
private final SparseIntArray mDisplayStates = new SparseIntArray();
// A map from LogicalDisplay ID to display brightness.
@GuardedBy("mSyncRoot")
private final SparseArray mDisplayBrightnesses = new SparseArray<>();
// Set to true when there are pending display changes that have yet to be applied
// to the surface flinger state.
private boolean mPendingTraversal;
// The Wifi display adapter, or null if not registered.
private WifiDisplayAdapter mWifiDisplayAdapter;
// The number of active wifi display scan requests.
private int mWifiDisplayScanRequestCount;
// The virtual display adapter, or null if not registered.
private VirtualDisplayAdapter mVirtualDisplayAdapter;
// The User ID of the current user
private @UserIdInt int mCurrentUserId;
// The stable device screen height and width. These are not tied to a specific display, even
// the default display, because they need to be stable over the course of the device's entire
// life, even if the default display changes (e.g. a new monitor is plugged into a PC-like
// device).
private Point mStableDisplaySize = new Point();
// Whether the system has finished booting or not.
private boolean mSystemReady;
// The top inset of the default display.
// This gets persisted so that the boot animation knows how to transition from the display's
// full size to the size configured by the user. Right now we only persist and animate the top
// inset, but theoretically we could do it for all of them.
private int mDefaultDisplayTopInset;
// Viewports of the default display and the display that should receive touch
// input from an external source. Used by the input system.
@GuardedBy("mSyncRoot")
private final ArrayList mViewports = new ArrayList<>();
// Persistent data store for all internal settings maintained by the display manager service.
private final PersistentDataStore mPersistentDataStore = new PersistentDataStore();
// Temporary callback list, used when sending display events to applications.
// May be used outside of the lock but only on the handler thread.
private final ArrayList mTempCallbacks = new ArrayList();
// Temporary viewports, used when sending new viewport information to the
// input system. May be used outside of the lock but only on the handler thread.
private final ArrayList mTempViewports = new ArrayList<>();
// The default color mode for default displays. Overrides the usual
// Display.Display.COLOR_MODE_DEFAULT for displays with the
// DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY flag set.
private final int mDefaultDisplayDefaultColorMode;
// Lists of UIDs that are present on the displays. Maps displayId -> array of UIDs.
private final SparseArray mDisplayAccessUIDs = new SparseArray<>();
private final Injector mInjector;
// The minimum brightness curve, which guarantess that any brightness curve that dips below it
// is rejected by the system.
private final Curve mMinimumBrightnessCurve;
private final Spline mMinimumBrightnessSpline;
private final ColorSpace mWideColorSpace;
private SensorManager mSensorManager;
private BrightnessTracker mBrightnessTracker;
// Whether minimal post processing is allowed by the user.
@GuardedBy("mSyncRoot")
private boolean mMinimalPostProcessingAllowed;
// Receives notifications about changes to Settings.
private SettingsObserver mSettingsObserver;
private final boolean mAllowNonNativeRefreshRateOverride;
private final BrightnessSynchronizer mBrightnessSynchronizer;
/**
* Applications use {@link android.view.Display#getRefreshRate} and
* {@link android.view.Display.Mode#getRefreshRate} to know what is the display refresh rate.
* Starting with Android S, the platform might throttle down applications frame rate to a
* divisor of the refresh rate if it is more preferable (for example if the application called
* to {@link android.view.Surface#setFrameRate}).
* Applications will experience {@link android.view.Choreographer#postFrameCallback} callbacks
* and backpressure at the throttled frame rate.
*
* {@link android.view.Display#getRefreshRate} will always return the application frame rate
* and not the physical display refresh rate to allow applications to do frame pacing correctly.
*
* {@link android.view.Display.Mode#getRefreshRate} will return the application frame rate if
* compiled to a previous release and starting with Android S it will return the physical
* display refresh rate.
*/
@ChangeId
@EnabledSince(targetSdkVersion = android.os.Build.VERSION_CODES.S)
static final long DISPLAY_MODE_RETURNS_PHYSICAL_REFRESH_RATE = 170503758L;
public DisplayManagerService(Context context) {
this(context, new Injector());
}
@VisibleForTesting
DisplayManagerService(Context context, Injector injector) {
super(context);
mInjector = injector;
mContext = context;
mHandler = new DisplayManagerHandler(DisplayThread.get().getLooper());
mUiHandler = UiThread.getHandler();
mDisplayDeviceRepo = new DisplayDeviceRepository(mSyncRoot, mPersistentDataStore);
mLogicalDisplayMapper = new LogicalDisplayMapper(mContext, mDisplayDeviceRepo,
new LogicalDisplayListener(), mSyncRoot, mHandler);
mDisplayModeDirector = new DisplayModeDirector(context, mHandler);
mBrightnessSynchronizer = new BrightnessSynchronizer(mContext);
Resources resources = mContext.getResources();
mDefaultDisplayDefaultColorMode = mContext.getResources().getInteger(
com.android.internal.R.integer.config_defaultDisplayDefaultColorMode);
mDefaultDisplayTopInset = SystemProperties.getInt(PROP_DEFAULT_DISPLAY_TOP_INSET, -1);
float[] lux = getFloatArray(resources.obtainTypedArray(
com.android.internal.R.array.config_minimumBrightnessCurveLux));
float[] nits = getFloatArray(resources.obtainTypedArray(
com.android.internal.R.array.config_minimumBrightnessCurveNits));
mMinimumBrightnessCurve = new Curve(lux, nits);
mMinimumBrightnessSpline = Spline.createSpline(lux, nits);
mCurrentUserId = UserHandle.USER_SYSTEM;
ColorSpace[] colorSpaces = SurfaceControl.getCompositionColorSpaces();
mWideColorSpace = colorSpaces[1];
mAllowNonNativeRefreshRateOverride = mInjector.getAllowNonNativeRefreshRateOverride();
mSystemReady = false;
}
public void setupSchedulerPolicies() {
// android.display and android.anim is critical to user experience and we should make sure
// it is not in the default foregroup groups, add it to top-app to make sure it uses all
// the cores and scheduling settings for top-app when it runs.
Process.setThreadGroupAndCpuset(DisplayThread.get().getThreadId(),
Process.THREAD_GROUP_TOP_APP);
Process.setThreadGroupAndCpuset(AnimationThread.get().getThreadId(),
Process.THREAD_GROUP_TOP_APP);
Process.setThreadGroupAndCpuset(SurfaceAnimationThread.get().getThreadId(),
Process.THREAD_GROUP_TOP_APP);
}
@Override
public void onStart() {
// We need to pre-load the persistent data store so it's ready before the default display
// adapter is up so that we have it's configuration. We could load it lazily, but since
// we're going to have to read it in eventually we may as well do it here rather than after
// we've waited for the display to register itself with us.
synchronized (mSyncRoot) {
mPersistentDataStore.loadIfNeeded();
loadStableDisplayValuesLocked();
}
mHandler.sendEmptyMessage(MSG_REGISTER_DEFAULT_DISPLAY_ADAPTERS);
// If there was a runtime restart then we may have stale caches left around, so we need to
// make sure to invalidate them upon every start.
DisplayManagerGlobal.invalidateLocalDisplayInfoCaches();
publishBinderService(Context.DISPLAY_SERVICE, new BinderService(),
true /*allowIsolated*/);
publishLocalService(DisplayManagerInternal.class, new LocalService());
}
@Override
public void onBootPhase(int phase) {
if (phase == PHASE_WAIT_FOR_DEFAULT_DISPLAY) {
synchronized (mSyncRoot) {
long timeout = SystemClock.uptimeMillis()
+ mInjector.getDefaultDisplayDelayTimeout();
while (mLogicalDisplayMapper.getDisplayLocked(Display.DEFAULT_DISPLAY) == null
|| mVirtualDisplayAdapter == null) {
long delay = timeout - SystemClock.uptimeMillis();
if (delay <= 0) {
throw new RuntimeException("Timeout waiting for default display "
+ "to be initialized. DefaultDisplay="
+ mLogicalDisplayMapper.getDisplayLocked(Display.DEFAULT_DISPLAY)
+ ", mVirtualDisplayAdapter=" + mVirtualDisplayAdapter);
}
if (DEBUG) {
Slog.d(TAG, "waitForDefaultDisplay: waiting, timeout=" + delay);
}
try {
mSyncRoot.wait(delay);
} catch (InterruptedException ex) {
}
}
}
} else if (phase == PHASE_BOOT_COMPLETED) {
mDisplayModeDirector.onBootCompleted();
}
}
@Override
public void onUserSwitching(@Nullable TargetUser from, @NonNull TargetUser to) {
final int newUserId = to.getUserIdentifier();
final int userSerial = getUserManager().getUserSerialNumber(newUserId);
synchronized (mSyncRoot) {
boolean userSwitching = mCurrentUserId != newUserId;
if (userSwitching) {
mCurrentUserId = newUserId;
}
mLogicalDisplayMapper.forEachLocked(logicalDisplay -> {
if (logicalDisplay.getDisplayInfoLocked().type != Display.TYPE_INTERNAL) {
return;
}
final DisplayPowerController dpc = mDisplayPowerControllers.get(
logicalDisplay.getDisplayIdLocked());
if (dpc == null) {
return;
}
if (userSwitching) {
BrightnessConfiguration config =
getBrightnessConfigForDisplayWithPdsFallbackLocked(
logicalDisplay.getPrimaryDisplayDeviceLocked().getUniqueId(),
userSerial);
dpc.setBrightnessConfiguration(config);
}
dpc.onSwitchUser(newUserId);
});
handleSettingsChange();
}
}
// TODO: Use dependencies or a boot phase
public void windowManagerAndInputReady() {
synchronized (mSyncRoot) {
mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class);
mInputManagerInternal = LocalServices.getService(InputManagerInternal.class);
DeviceStateManager deviceStateManager =
mContext.getSystemService(DeviceStateManager.class);
deviceStateManager.registerCallback(new HandlerExecutor(mHandler),
new DeviceStateListener());
scheduleTraversalLocked(false);
}
}
/**
* Called when the system is ready to go.
*/
public void systemReady(boolean safeMode, boolean onlyCore) {
synchronized (mSyncRoot) {
mSafeMode = safeMode;
mOnlyCore = onlyCore;
mSystemReady = true;
// Just in case the top inset changed before the system was ready. At this point, any
// relevant configuration should be in place.
recordTopInsetLocked(mLogicalDisplayMapper.getDisplayLocked(Display.DEFAULT_DISPLAY));
updateSettingsLocked();
updateUserDisabledHdrTypesFromSettingsLocked();
}
mDisplayModeDirector.setDesiredDisplayModeSpecsListener(
new DesiredDisplayModeSpecsObserver());
mDisplayModeDirector.start(mSensorManager);
mHandler.sendEmptyMessage(MSG_REGISTER_ADDITIONAL_DISPLAY_ADAPTERS);
mSettingsObserver = new SettingsObserver();
mBrightnessSynchronizer.startSynchronizing();
}
@VisibleForTesting
Handler getDisplayHandler() {
return mHandler;
}
@VisibleForTesting
DisplayDeviceRepository getDisplayDeviceRepository() {
return mDisplayDeviceRepo;
}
private void loadStableDisplayValuesLocked() {
final Point size = mPersistentDataStore.getStableDisplaySize();
if (size.x > 0 && size.y > 0) {
// Just set these values directly so we don't write the display persistent data again
// unnecessarily
mStableDisplaySize.set(size.x, size.y);
} else {
final Resources res = mContext.getResources();
final int width = res.getInteger(
com.android.internal.R.integer.config_stableDeviceDisplayWidth);
final int height = res.getInteger(
com.android.internal.R.integer.config_stableDeviceDisplayHeight);
if (width > 0 && height > 0) {
setStableDisplaySizeLocked(width, height);
}
}
}
private Point getStableDisplaySizeInternal() {
Point r = new Point();
synchronized (mSyncRoot) {
if (mStableDisplaySize.x > 0 && mStableDisplaySize.y > 0) {
r.set(mStableDisplaySize.x, mStableDisplaySize.y);
}
}
return r;
}
private void registerDisplayTransactionListenerInternal(
DisplayTransactionListener listener) {
// List is self-synchronized copy-on-write.
mDisplayTransactionListeners.add(listener);
}
private void unregisterDisplayTransactionListenerInternal(
DisplayTransactionListener listener) {
// List is self-synchronized copy-on-write.
mDisplayTransactionListeners.remove(listener);
}
@VisibleForTesting
void setDisplayInfoOverrideFromWindowManagerInternal(int displayId, DisplayInfo info) {
synchronized (mSyncRoot) {
final LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(displayId);
if (display != null) {
if (display.setDisplayInfoOverrideFromWindowManagerLocked(info)) {
handleLogicalDisplayChangedLocked(display);
scheduleTraversalLocked(false);
}
}
}
}
/**
* @see DisplayManagerInternal#getNonOverrideDisplayInfo(int, DisplayInfo)
*/
private void getNonOverrideDisplayInfoInternal(int displayId, DisplayInfo outInfo) {
synchronized (mSyncRoot) {
final LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(displayId);
if (display != null) {
display.getNonOverrideDisplayInfoLocked(outInfo);
}
}
}
@VisibleForTesting
void performTraversalInternal(SurfaceControl.Transaction t) {
synchronized (mSyncRoot) {
if (!mPendingTraversal) {
return;
}
mPendingTraversal = false;
performTraversalLocked(t);
}
// List is self-synchronized copy-on-write.
for (DisplayTransactionListener listener : mDisplayTransactionListeners) {
listener.onDisplayTransaction(t);
}
}
private float clampBrightness(int displayState, float brightnessState) {
if (displayState == Display.STATE_OFF) {
brightnessState = PowerManager.BRIGHTNESS_OFF_FLOAT;
} else if (brightnessState != PowerManager.BRIGHTNESS_OFF_FLOAT
&& brightnessState < PowerManager.BRIGHTNESS_MIN) {
brightnessState = PowerManager.BRIGHTNESS_INVALID_FLOAT;
} else if (brightnessState > PowerManager.BRIGHTNESS_MAX) {
brightnessState = PowerManager.BRIGHTNESS_MAX;
}
return brightnessState;
}
private void requestDisplayStateInternal(int displayId, int state, float brightnessState,
float sdrBrightnessState) {
if (state == Display.STATE_UNKNOWN) {
state = Display.STATE_ON;
}
brightnessState = clampBrightness(state, brightnessState);
sdrBrightnessState = clampBrightness(state, sdrBrightnessState);
// Update the display state within the lock.
// Note that we do not need to schedule traversals here although it
// may happen as a side-effect of displays changing state.
final Runnable runnable;
final String traceMessage;
synchronized (mSyncRoot) {
final int index = mDisplayStates.indexOfKey(displayId);
final BrightnessPair brightnessPair =
index < 0 ? null : mDisplayBrightnesses.valueAt(index);
if (index < 0 || (mDisplayStates.valueAt(index) == state
&& brightnessPair.brightness == brightnessState
&& brightnessPair.sdrBrightness == sdrBrightnessState)) {
return; // Display no longer exists or no change.
}
traceMessage = "requestDisplayStateInternal("
+ displayId + ", "
+ Display.stateToString(state)
+ ", brightness=" + brightnessState
+ ", sdrBrightness=" + sdrBrightnessState + ")";
Trace.asyncTraceBegin(Trace.TRACE_TAG_POWER, traceMessage, displayId);
mDisplayStates.setValueAt(index, state);
brightnessPair.brightness = brightnessState;
brightnessPair.sdrBrightness = sdrBrightnessState;
runnable = updateDisplayStateLocked(mLogicalDisplayMapper.getDisplayLocked(displayId)
.getPrimaryDisplayDeviceLocked());
}
// Setting the display power state can take hundreds of milliseconds
// to complete so we defer the most expensive part of the work until
// after we have exited the critical section to avoid blocking other
// threads for a long time.
if (runnable != null) {
runnable.run();
}
Trace.asyncTraceEnd(Trace.TRACE_TAG_POWER, traceMessage, displayId);
}
private class SettingsObserver extends ContentObserver {
SettingsObserver() {
super(mHandler);
mContext.getContentResolver().registerContentObserver(
Settings.Secure.getUriFor(
Settings.Secure.MINIMAL_POST_PROCESSING_ALLOWED), false, this);
}
@Override
public void onChange(boolean selfChange, Uri uri) {
handleSettingsChange();
}
}
private void handleSettingsChange() {
synchronized (mSyncRoot) {
updateSettingsLocked();
scheduleTraversalLocked(false);
}
}
private void updateSettingsLocked() {
mMinimalPostProcessingAllowed = Settings.Secure.getIntForUser(mContext.getContentResolver(),
Settings.Secure.MINIMAL_POST_PROCESSING_ALLOWED, 1, UserHandle.USER_CURRENT) != 0;
}
private void updateUserDisabledHdrTypesFromSettingsLocked() {
mAreUserDisabledHdrTypesAllowed = (Settings.Global.getInt(
mContext.getContentResolver(),
Settings.Global.ARE_USER_DISABLED_HDR_FORMATS_ALLOWED,
1) != 0);
String userDisabledHdrTypes = Settings.Global.getString(mContext.getContentResolver(),
Settings.Global.USER_DISABLED_HDR_FORMATS);
if (userDisabledHdrTypes != null) {
try {
String[] userDisabledHdrTypeStrings =
TextUtils.split(userDisabledHdrTypes, ",");
mUserDisabledHdrTypes = new int[userDisabledHdrTypeStrings.length];
for (int i = 0; i < userDisabledHdrTypeStrings.length; i++) {
mUserDisabledHdrTypes[i] = Integer.parseInt(userDisabledHdrTypeStrings[i]);
}
} catch (NumberFormatException e) {
Slog.e(TAG,
"Failed to parse USER_DISABLED_HDR_FORMATS. "
+ "Clearing the setting.", e);
clearUserDisabledHdrTypesLocked();
}
} else {
clearUserDisabledHdrTypesLocked();
}
}
private void clearUserDisabledHdrTypesLocked() {
mUserDisabledHdrTypes = new int[]{};
synchronized (mSyncRoot) {
Settings.Global.putString(mContext.getContentResolver(),
Settings.Global.USER_DISABLED_HDR_FORMATS, "");
}
}
private DisplayInfo getDisplayInfoForFrameRateOverride(DisplayEventReceiver.FrameRateOverride[]
frameRateOverrides, DisplayInfo info, int callingUid) {
float frameRateHz = 0;
for (DisplayEventReceiver.FrameRateOverride frameRateOverride : frameRateOverrides) {
if (frameRateOverride.uid == callingUid) {
frameRateHz = frameRateOverride.frameRateHz;
break;
}
}
if (frameRateHz == 0) {
return info;
}
// Override the refresh rate only if it is a divider of the current
// refresh rate. This calculation needs to be in sync with the native code
// in RefreshRateConfigs::getFrameRateDivider
Display.Mode currentMode = info.getMode();
float numPeriods = currentMode.getRefreshRate() / frameRateHz;
float numPeriodsRound = Math.round(numPeriods);
if (Math.abs(numPeriods - numPeriodsRound) > THRESHOLD_FOR_REFRESH_RATES_DIVIDERS) {
return info;
}
frameRateHz = currentMode.getRefreshRate() / numPeriodsRound;
DisplayInfo overriddenInfo = new DisplayInfo();
overriddenInfo.copyFrom(info);
for (Display.Mode mode : info.supportedModes) {
if (!mode.equalsExceptRefreshRate(currentMode)) {
continue;
}
if (mode.getRefreshRate() >= frameRateHz - THRESHOLD_FOR_REFRESH_RATES_DIVIDERS
&& mode.getRefreshRate()
<= frameRateHz + THRESHOLD_FOR_REFRESH_RATES_DIVIDERS) {
if (DEBUG) {
Slog.d(TAG, "found matching modeId " + mode.getModeId());
}
overriddenInfo.refreshRateOverride = mode.getRefreshRate();
if (!CompatChanges.isChangeEnabled(DISPLAY_MODE_RETURNS_PHYSICAL_REFRESH_RATE,
callingUid)) {
overriddenInfo.modeId = mode.getModeId();
}
return overriddenInfo;
}
}
if (mAllowNonNativeRefreshRateOverride) {
overriddenInfo.refreshRateOverride = frameRateHz;
if (!CompatChanges.isChangeEnabled(DISPLAY_MODE_RETURNS_PHYSICAL_REFRESH_RATE,
callingUid)) {
overriddenInfo.supportedModes = Arrays.copyOf(info.supportedModes,
info.supportedModes.length + 1);
overriddenInfo.supportedModes[overriddenInfo.supportedModes.length - 1] =
new Display.Mode(Display.DISPLAY_MODE_ID_FOR_FRAME_RATE_OVERRIDE,
currentMode.getPhysicalWidth(), currentMode.getPhysicalHeight(),
overriddenInfo.refreshRateOverride);
overriddenInfo.modeId =
overriddenInfo.supportedModes[overriddenInfo.supportedModes.length - 1]
.getModeId();
}
return overriddenInfo;
}
return info;
}
private DisplayInfo getDisplayInfoInternal(int displayId, int callingUid) {
synchronized (mSyncRoot) {
final LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(displayId);
if (display != null) {
final DisplayInfo info =
getDisplayInfoForFrameRateOverride(display.getFrameRateOverrides(),
display.getDisplayInfoLocked(), callingUid);
if (info.hasAccess(callingUid)
|| isUidPresentOnDisplayInternal(callingUid, displayId)) {
return info;
}
}
return null;
}
}
private void registerCallbackInternal(IDisplayManagerCallback callback, int callingPid,
int callingUid, @EventsMask long eventsMask) {
synchronized (mSyncRoot) {
CallbackRecord record = mCallbacks.get(callingPid);
if (record != null) {
record.updateEventsMask(eventsMask);
return;
}
record = new CallbackRecord(callingPid, callingUid, callback, eventsMask);
try {
IBinder binder = callback.asBinder();
binder.linkToDeath(record, 0);
} catch (RemoteException ex) {
// give up
throw new RuntimeException(ex);
}
mCallbacks.put(callingPid, record);
}
}
private void onCallbackDied(CallbackRecord record) {
synchronized (mSyncRoot) {
mCallbacks.remove(record.mPid);
stopWifiDisplayScanLocked(record);
}
}
private void startWifiDisplayScanInternal(int callingPid) {
synchronized (mSyncRoot) {
CallbackRecord record = mCallbacks.get(callingPid);
if (record == null) {
throw new IllegalStateException("The calling process has not "
+ "registered an IDisplayManagerCallback.");
}
startWifiDisplayScanLocked(record);
}
}
private void startWifiDisplayScanLocked(CallbackRecord record) {
if (!record.mWifiDisplayScanRequested) {
record.mWifiDisplayScanRequested = true;
if (mWifiDisplayScanRequestCount++ == 0) {
if (mWifiDisplayAdapter != null) {
mWifiDisplayAdapter.requestStartScanLocked();
}
}
}
}
private void stopWifiDisplayScanInternal(int callingPid) {
synchronized (mSyncRoot) {
CallbackRecord record = mCallbacks.get(callingPid);
if (record == null) {
throw new IllegalStateException("The calling process has not "
+ "registered an IDisplayManagerCallback.");
}
stopWifiDisplayScanLocked(record);
}
}
private void stopWifiDisplayScanLocked(CallbackRecord record) {
if (record.mWifiDisplayScanRequested) {
record.mWifiDisplayScanRequested = false;
if (--mWifiDisplayScanRequestCount == 0) {
if (mWifiDisplayAdapter != null) {
mWifiDisplayAdapter.requestStopScanLocked();
}
} else if (mWifiDisplayScanRequestCount < 0) {
Slog.wtf(TAG, "mWifiDisplayScanRequestCount became negative: "
+ mWifiDisplayScanRequestCount);
mWifiDisplayScanRequestCount = 0;
}
}
}
private void connectWifiDisplayInternal(String address) {
synchronized (mSyncRoot) {
if (mWifiDisplayAdapter != null) {
mWifiDisplayAdapter.requestConnectLocked(address);
}
}
}
private void pauseWifiDisplayInternal() {
synchronized (mSyncRoot) {
if (mWifiDisplayAdapter != null) {
mWifiDisplayAdapter.requestPauseLocked();
}
}
}
private void resumeWifiDisplayInternal() {
synchronized (mSyncRoot) {
if (mWifiDisplayAdapter != null) {
mWifiDisplayAdapter.requestResumeLocked();
}
}
}
private void disconnectWifiDisplayInternal() {
synchronized (mSyncRoot) {
if (mWifiDisplayAdapter != null) {
mWifiDisplayAdapter.requestDisconnectLocked();
}
}
}
private void renameWifiDisplayInternal(String address, String alias) {
synchronized (mSyncRoot) {
if (mWifiDisplayAdapter != null) {
mWifiDisplayAdapter.requestRenameLocked(address, alias);
}
}
}
private void forgetWifiDisplayInternal(String address) {
synchronized (mSyncRoot) {
if (mWifiDisplayAdapter != null) {
mWifiDisplayAdapter.requestForgetLocked(address);
}
}
}
private WifiDisplayStatus getWifiDisplayStatusInternal() {
synchronized (mSyncRoot) {
if (mWifiDisplayAdapter != null) {
return mWifiDisplayAdapter.getWifiDisplayStatusLocked();
}
return new WifiDisplayStatus();
}
}
private void setUserDisabledHdrTypesInternal(int[] userDisabledHdrTypes) {
synchronized (mSyncRoot) {
if (userDisabledHdrTypes == null) {
Slog.e(TAG, "Null is not an expected argument to "
+ "setUserDisabledHdrTypesInternal");
return;
}
Arrays.sort(userDisabledHdrTypes);
if (Arrays.equals(mUserDisabledHdrTypes, userDisabledHdrTypes)) {
return;
}
String userDisabledFormatsString = "";
if (userDisabledHdrTypes.length != 0) {
userDisabledFormatsString = TextUtils.join(",",
Arrays.stream(userDisabledHdrTypes).boxed().toArray());
}
Settings.Global.putString(mContext.getContentResolver(),
Settings.Global.USER_DISABLED_HDR_FORMATS, userDisabledFormatsString);
mUserDisabledHdrTypes = userDisabledHdrTypes;
if (!mAreUserDisabledHdrTypesAllowed) {
mLogicalDisplayMapper.forEachLocked(
display -> {
display.setUserDisabledHdrTypes(userDisabledHdrTypes);
handleLogicalDisplayChangedLocked(display);
});
}
}
}
private void setAreUserDisabledHdrTypesAllowedInternal(
boolean areUserDisabledHdrTypesAllowed) {
synchronized (mSyncRoot) {
if (mAreUserDisabledHdrTypesAllowed == areUserDisabledHdrTypesAllowed) {
return;
}
mAreUserDisabledHdrTypesAllowed = areUserDisabledHdrTypesAllowed;
if (mUserDisabledHdrTypes.length == 0) {
return;
}
Settings.Global.putInt(mContext.getContentResolver(),
Settings.Global.ARE_USER_DISABLED_HDR_FORMATS_ALLOWED,
areUserDisabledHdrTypesAllowed ? 1 : 0);
int userDisabledHdrTypes[] = {};
if (!mAreUserDisabledHdrTypesAllowed) {
userDisabledHdrTypes = mUserDisabledHdrTypes;
}
int[] finalUserDisabledHdrTypes = userDisabledHdrTypes;
mLogicalDisplayMapper.forEachLocked(
display -> {
display.setUserDisabledHdrTypes(finalUserDisabledHdrTypes);
handleLogicalDisplayChangedLocked(display);
});
}
}
private void requestColorModeInternal(int displayId, int colorMode) {
synchronized (mSyncRoot) {
final LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(displayId);
if (display != null &&
display.getRequestedColorModeLocked() != colorMode) {
display.setRequestedColorModeLocked(colorMode);
scheduleTraversalLocked(false);
}
}
}
private int createVirtualDisplayInternal(IVirtualDisplayCallback callback,
IMediaProjection projection, int callingUid, String packageName, Surface surface,
int flags, VirtualDisplayConfig virtualDisplayConfig) {
synchronized (mSyncRoot) {
if (mVirtualDisplayAdapter == null) {
Slog.w(TAG, "Rejecting request to create private virtual display "
+ "because the virtual display adapter is not available.");
return -1;
}
DisplayDevice device = mVirtualDisplayAdapter.createVirtualDisplayLocked(
callback, projection, callingUid, packageName, surface, flags,
virtualDisplayConfig);
if (device == null) {
return -1;
}
// DisplayDevice events are handled manually for Virtual Displays.
// TODO: multi-display Fix this so that generic add/remove events are not handled in a
// different code path for virtual displays. Currently this happens so that we can
// return a valid display ID synchronously upon successful Virtual Display creation.
// This code can run on any binder thread, while onDisplayDeviceAdded() callbacks are
// called on the DisplayThread (which we don't want to wait for?).
// One option would be to actually wait here on the binder thread
// to be notified when the virtual display is created (or failed).
mDisplayDeviceRepo.onDisplayDeviceEvent(device,
DisplayAdapter.DISPLAY_DEVICE_EVENT_ADDED);
final LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(device);
if (display != null) {
return display.getDisplayIdLocked();
}
// Something weird happened and the logical display was not created.
Slog.w(TAG, "Rejecting request to create virtual display "
+ "because the logical display was not created.");
mVirtualDisplayAdapter.releaseVirtualDisplayLocked(callback.asBinder());
mDisplayDeviceRepo.onDisplayDeviceEvent(device,
DisplayAdapter.DISPLAY_DEVICE_EVENT_REMOVED);
}
return -1;
}
private void resizeVirtualDisplayInternal(IBinder appToken,
int width, int height, int densityDpi) {
synchronized (mSyncRoot) {
if (mVirtualDisplayAdapter == null) {
return;
}
mVirtualDisplayAdapter.resizeVirtualDisplayLocked(appToken, width, height, densityDpi);
}
}
private void setVirtualDisplaySurfaceInternal(IBinder appToken, Surface surface) {
synchronized (mSyncRoot) {
if (mVirtualDisplayAdapter == null) {
return;
}
mVirtualDisplayAdapter.setVirtualDisplaySurfaceLocked(appToken, surface);
}
}
private void releaseVirtualDisplayInternal(IBinder appToken) {
synchronized (mSyncRoot) {
if (mVirtualDisplayAdapter == null) {
return;
}
DisplayDevice device =
mVirtualDisplayAdapter.releaseVirtualDisplayLocked(appToken);
if (device != null) {
// TODO: multi-display - handle virtual displays the same as other display adapters.
mDisplayDeviceRepo.onDisplayDeviceEvent(device,
DisplayAdapter.DISPLAY_DEVICE_EVENT_REMOVED);
}
}
}
private void setVirtualDisplayStateInternal(IBinder appToken, boolean isOn) {
synchronized (mSyncRoot) {
if (mVirtualDisplayAdapter == null) {
return;
}
mVirtualDisplayAdapter.setVirtualDisplayStateLocked(appToken, isOn);
}
}
private void registerDefaultDisplayAdapters() {
// Register default display adapters.
synchronized (mSyncRoot) {
// main display adapter
registerDisplayAdapterLocked(new LocalDisplayAdapter(
mSyncRoot, mContext, mHandler, mDisplayDeviceRepo));
// Standalone VR devices rely on a virtual display as their primary display for
// 2D UI. We register virtual display adapter along side the main display adapter
// here so that it is ready by the time the system sends the home Intent for
// early apps like SetupWizard/Launcher. In particular, SUW is displayed using
// the virtual display inside VR before any VR-specific apps even run.
mVirtualDisplayAdapter = mInjector.getVirtualDisplayAdapter(mSyncRoot, mContext,
mHandler, mDisplayDeviceRepo);
if (mVirtualDisplayAdapter != null) {
registerDisplayAdapterLocked(mVirtualDisplayAdapter);
}
}
}
private void registerAdditionalDisplayAdapters() {
synchronized (mSyncRoot) {
if (shouldRegisterNonEssentialDisplayAdaptersLocked()) {
registerOverlayDisplayAdapterLocked();
registerWifiDisplayAdapterLocked();
}
}
}
private void registerOverlayDisplayAdapterLocked() {
registerDisplayAdapterLocked(new OverlayDisplayAdapter(
mSyncRoot, mContext, mHandler, mDisplayDeviceRepo, mUiHandler));
}
private void registerWifiDisplayAdapterLocked() {
if (mContext.getResources().getBoolean(
com.android.internal.R.bool.config_enableWifiDisplay)
|| SystemProperties.getInt(FORCE_WIFI_DISPLAY_ENABLE, -1) == 1) {
mWifiDisplayAdapter = new WifiDisplayAdapter(
mSyncRoot, mContext, mHandler, mDisplayDeviceRepo,
mPersistentDataStore);
registerDisplayAdapterLocked(mWifiDisplayAdapter);
}
}
private boolean shouldRegisterNonEssentialDisplayAdaptersLocked() {
// In safe mode, we disable non-essential display adapters to give the user
// an opportunity to fix broken settings or other problems that might affect
// system stability.
// In only-core mode, we disable non-essential display adapters to minimize
// the number of dependencies that are started while in this mode and to
// prevent problems that might occur due to the device being encrypted.
return !mSafeMode && !mOnlyCore;
}
private void registerDisplayAdapterLocked(DisplayAdapter adapter) {
mDisplayAdapters.add(adapter);
adapter.registerLocked();
}
private void handleLogicalDisplayAddedLocked(LogicalDisplay display) {
final DisplayDevice device = display.getPrimaryDisplayDeviceLocked();
final int displayId = display.getDisplayIdLocked();
final boolean isDefault = displayId == Display.DEFAULT_DISPLAY;
configureColorModeLocked(display, device);
if (!mAreUserDisabledHdrTypesAllowed) {
display.setUserDisabledHdrTypes(mUserDisabledHdrTypes);
}
if (isDefault) {
recordStableDisplayStatsIfNeededLocked(display);
recordTopInsetLocked(display);
}
addDisplayPowerControllerLocked(display);
mDisplayStates.append(displayId, Display.STATE_UNKNOWN);
final float brightnessDefault = display.getDisplayInfoLocked().brightnessDefault;
mDisplayBrightnesses.append(displayId,
new BrightnessPair(brightnessDefault, brightnessDefault));
DisplayManagerGlobal.invalidateLocalDisplayInfoCaches();
// Wake up waitForDefaultDisplay.
if (isDefault) {
mSyncRoot.notifyAll();
}
sendDisplayEventLocked(displayId, DisplayManagerGlobal.EVENT_DISPLAY_ADDED);
Runnable work = updateDisplayStateLocked(device);
if (work != null) {
work.run();
}
scheduleTraversalLocked(false);
}
private void handleLogicalDisplayChangedLocked(@NonNull LogicalDisplay display) {
updateViewportPowerStateLocked(display);
final int displayId = display.getDisplayIdLocked();
if (displayId == Display.DEFAULT_DISPLAY) {
recordTopInsetLocked(display);
}
// We don't bother invalidating the display info caches here because any changes to the
// display info will trigger a cache invalidation inside of LogicalDisplay before we hit
// this point.
sendDisplayEventLocked(displayId, DisplayManagerGlobal.EVENT_DISPLAY_CHANGED);
scheduleTraversalLocked(false);
mPersistentDataStore.saveIfNeeded();
DisplayPowerController dpc = mDisplayPowerControllers.get(displayId);
if (dpc != null) {
dpc.onDisplayChanged();
}
}
private void handleLogicalDisplayFrameRateOverridesChangedLocked(
@NonNull LogicalDisplay display) {
final int displayId = display.getDisplayIdLocked();
// We don't bother invalidating the display info caches here because any changes to the
// display info will trigger a cache invalidation inside of LogicalDisplay before we hit
// this point.
sendDisplayEventFrameRateOverrideLocked(displayId);
scheduleTraversalLocked(false);
}
private void handleLogicalDisplayRemovedLocked(@NonNull LogicalDisplay display) {
final int displayId = display.getDisplayIdLocked();
final DisplayPowerController dpc = mDisplayPowerControllers.removeReturnOld(displayId);
if (dpc != null) {
dpc.stop();
}
mDisplayStates.delete(displayId);
mDisplayBrightnesses.delete(displayId);
DisplayManagerGlobal.invalidateLocalDisplayInfoCaches();
sendDisplayEventLocked(displayId, DisplayManagerGlobal.EVENT_DISPLAY_REMOVED);
scheduleTraversalLocked(false);
}
private void handleLogicalDisplaySwappedLocked(@NonNull LogicalDisplay display) {
final DisplayDevice device = display.getPrimaryDisplayDeviceLocked();
final Runnable work = updateDisplayStateLocked(device);
if (work != null) {
mHandler.post(work);
}
final int displayId = display.getDisplayIdLocked();
DisplayPowerController dpc = mDisplayPowerControllers.get(displayId);
if (dpc != null) {
dpc.onDisplayChanged();
}
mPersistentDataStore.saveIfNeeded();
mHandler.sendEmptyMessage(MSG_LOAD_BRIGHTNESS_CONFIGURATIONS);
handleLogicalDisplayChangedLocked(display);
}
private void handleLogicalDisplayDeviceStateTransitionLocked(@NonNull LogicalDisplay display) {
final int displayId = display.getDisplayIdLocked();
final DisplayPowerController dpc = mDisplayPowerControllers.get(displayId);
if (dpc != null) {
dpc.onDeviceStateTransition();
}
}
private Runnable updateDisplayStateLocked(DisplayDevice device) {
// Blank or unblank the display immediately to match the state requested
// by the display power controller (if known).
DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
if ((info.flags & DisplayDeviceInfo.FLAG_NEVER_BLANK) == 0) {
final LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(device);
if (display == null) {
return null;
}
final int displayId = display.getDisplayIdLocked();
final int state = mDisplayStates.get(displayId);
// Only send a request for display state if display state has already been initialized.
if (state != Display.STATE_UNKNOWN) {
final BrightnessPair brightnessPair = mDisplayBrightnesses.get(displayId);
return device.requestDisplayStateLocked(state, brightnessPair.brightness,
brightnessPair.sdrBrightness);
}
}
return null;
}
private void configureColorModeLocked(LogicalDisplay display, DisplayDevice device) {
if (display.getPrimaryDisplayDeviceLocked() == device) {
int colorMode = mPersistentDataStore.getColorMode(device);
if (colorMode == Display.COLOR_MODE_INVALID) {
if ((device.getDisplayDeviceInfoLocked().flags
& DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY) != 0) {
colorMode = mDefaultDisplayDefaultColorMode;
} else {
colorMode = Display.COLOR_MODE_DEFAULT;
}
}
display.setRequestedColorModeLocked(colorMode);
}
}
// If we've never recorded stable device stats for this device before and they aren't
// explicitly configured, go ahead and record the stable device stats now based on the status
// of the default display at first boot.
private void recordStableDisplayStatsIfNeededLocked(LogicalDisplay d) {
if (mStableDisplaySize.x <= 0 && mStableDisplaySize.y <= 0) {
DisplayInfo info = d.getDisplayInfoLocked();
setStableDisplaySizeLocked(info.getNaturalWidth(), info.getNaturalHeight());
}
}
private void recordTopInsetLocked(@Nullable LogicalDisplay d) {
// We must only persist the inset after boot has completed, otherwise we will end up
// overwriting the persisted value before the masking flag has been loaded from the
// resource overlay.
if (!mSystemReady || d == null) {
return;
}
int topInset = d.getInsets().top;
if (topInset == mDefaultDisplayTopInset) {
return;
}
mDefaultDisplayTopInset = topInset;
SystemProperties.set(PROP_DEFAULT_DISPLAY_TOP_INSET, Integer.toString(topInset));
}
private void setStableDisplaySizeLocked(int width, int height) {
mStableDisplaySize = new Point(width, height);
try {
mPersistentDataStore.setStableDisplaySize(mStableDisplaySize);
} finally {
mPersistentDataStore.saveIfNeeded();
}
}
@VisibleForTesting
Curve getMinimumBrightnessCurveInternal() {
return mMinimumBrightnessCurve;
}
int getPreferredWideGamutColorSpaceIdInternal() {
return mWideColorSpace.getId();
}
void setShouldAlwaysRespectAppRequestedModeInternal(boolean enabled) {
mDisplayModeDirector.setShouldAlwaysRespectAppRequestedMode(enabled);
}
boolean shouldAlwaysRespectAppRequestedModeInternal() {
return mDisplayModeDirector.shouldAlwaysRespectAppRequestedMode();
}
void setRefreshRateSwitchingTypeInternal(@DisplayManager.SwitchingType int newValue) {
mDisplayModeDirector.setModeSwitchingType(newValue);
}
@DisplayManager.SwitchingType
int getRefreshRateSwitchingTypeInternal() {
return mDisplayModeDirector.getModeSwitchingType();
}
private void setBrightnessConfigurationForDisplayInternal(
@Nullable BrightnessConfiguration c, String uniqueId, @UserIdInt int userId,
String packageName) {
validateBrightnessConfiguration(c);
final int userSerial = getUserManager().getUserSerialNumber(userId);
synchronized (mSyncRoot) {
try {
DisplayDevice displayDevice = mDisplayDeviceRepo.getByUniqueIdLocked(uniqueId);
if (displayDevice == null) {
return;
}
mPersistentDataStore.setBrightnessConfigurationForDisplayLocked(c, displayDevice,
userSerial, packageName);
} finally {
mPersistentDataStore.saveIfNeeded();
}
if (userId != mCurrentUserId) {
return;
}
DisplayPowerController dpc = getDpcFromUniqueIdLocked(uniqueId);
if (dpc != null) {
dpc.setBrightnessConfiguration(c);
}
}
}
private DisplayPowerController getDpcFromUniqueIdLocked(String uniqueId) {
final DisplayDevice displayDevice = mDisplayDeviceRepo.getByUniqueIdLocked(uniqueId);
final LogicalDisplay logicalDisplay = mLogicalDisplayMapper.getDisplayLocked(displayDevice);
if (logicalDisplay != null) {
final int displayId = logicalDisplay.getDisplayIdLocked();
return mDisplayPowerControllers.get(displayId);
}
return null;
}
@VisibleForTesting
void validateBrightnessConfiguration(BrightnessConfiguration config) {
if (config == null) {
return;
}
if (isBrightnessConfigurationTooDark(config)) {
throw new IllegalArgumentException("brightness curve is too dark");
}
}
private boolean isBrightnessConfigurationTooDark(BrightnessConfiguration config) {
Pair curve = config.getCurve();
float[] lux = curve.first;
float[] nits = curve.second;
for (int i = 0; i < lux.length; i++) {
if (nits[i] < mMinimumBrightnessSpline.interpolate(lux[i])) {
return true;
}
}
return false;
}
private void loadBrightnessConfigurations() {
int userSerial = getUserManager().getUserSerialNumber(mContext.getUserId());
synchronized (mSyncRoot) {
mLogicalDisplayMapper.forEachLocked((logicalDisplay) -> {
final String uniqueId =
logicalDisplay.getPrimaryDisplayDeviceLocked().getUniqueId();
final BrightnessConfiguration config =
getBrightnessConfigForDisplayWithPdsFallbackLocked(uniqueId, userSerial);
if (config != null) {
final DisplayPowerController dpc = mDisplayPowerControllers.get(
logicalDisplay.getDisplayIdLocked());
if (dpc != null) {
dpc.setBrightnessConfiguration(config);
}
}
});
}
}
private void performTraversalLocked(SurfaceControl.Transaction t) {
// Clear all viewports before configuring displays so that we can keep
// track of which ones we have configured.
clearViewportsLocked();
// Configure each display device.
mLogicalDisplayMapper.forEachLocked((LogicalDisplay display) -> {
final DisplayDevice device = display.getPrimaryDisplayDeviceLocked();
if (device != null) {
configureDisplayLocked(t, device);
device.performTraversalLocked(t);
}
});
// Tell the input system about these new viewports.
if (mInputManagerInternal != null) {
mHandler.sendEmptyMessage(MSG_UPDATE_VIEWPORT);
}
}
private void setDisplayPropertiesInternal(int displayId, boolean hasContent,
float requestedRefreshRate, int requestedModeId, float requestedMinRefreshRate,
float requestedMaxRefreshRate, boolean preferMinimalPostProcessing,
boolean inTraversal) {
synchronized (mSyncRoot) {
final LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(displayId);
if (display == null) {
return;
}
boolean shouldScheduleTraversal = false;
if (display.hasContentLocked() != hasContent) {
if (DEBUG) {
Slog.d(TAG, "Display " + displayId + " hasContent flag changed: "
+ "hasContent=" + hasContent + ", inTraversal=" + inTraversal);
}
display.setHasContentLocked(hasContent);
shouldScheduleTraversal = true;
}
if (requestedModeId == 0 && requestedRefreshRate != 0) {
// Scan supported modes returned by display.getInfo() to find a mode with the same
// size as the default display mode but with the specified refresh rate instead.
Display.Mode mode = display.getDisplayInfoLocked().findDefaultModeByRefreshRate(
requestedRefreshRate);
if (mode != null) {
requestedModeId = mode.getModeId();
} else {
Slog.e(TAG, "Couldn't find a mode for the requestedRefreshRate: "
+ requestedRefreshRate + " on Display: " + displayId);
}
}
mDisplayModeDirector.getAppRequestObserver().setAppRequest(
displayId, requestedModeId, requestedMinRefreshRate, requestedMaxRefreshRate);
if (display.getDisplayInfoLocked().minimalPostProcessingSupported) {
boolean mppRequest = mMinimalPostProcessingAllowed && preferMinimalPostProcessing;
if (display.getRequestedMinimalPostProcessingLocked() != mppRequest) {
display.setRequestedMinimalPostProcessingLocked(mppRequest);
shouldScheduleTraversal = true;
}
}
if (shouldScheduleTraversal) {
scheduleTraversalLocked(inTraversal);
}
}
}
private void setDisplayOffsetsInternal(int displayId, int x, int y) {
synchronized (mSyncRoot) {
final LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(displayId);
if (display == null) {
return;
}
if (display.getDisplayOffsetXLocked() != x
|| display.getDisplayOffsetYLocked() != y) {
if (DEBUG) {
Slog.d(TAG, "Display " + displayId + " burn-in offset set to ("
+ x + ", " + y + ")");
}
display.setDisplayOffsetsLocked(x, y);
scheduleTraversalLocked(false);
}
}
}
private void setDisplayScalingDisabledInternal(int displayId, boolean disable) {
synchronized (mSyncRoot) {
final LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(displayId);
if (display == null) {
return;
}
if (display.isDisplayScalingDisabled() != disable) {
if (DEBUG) {
Slog.d(TAG, "Display " + displayId + " content scaling disabled = " + disable);
}
display.setDisplayScalingDisabledLocked(disable);
scheduleTraversalLocked(false);
}
}
}
// Updates the lists of UIDs that are present on displays.
private void setDisplayAccessUIDsInternal(SparseArray newDisplayAccessUIDs) {
synchronized (mSyncRoot) {
mDisplayAccessUIDs.clear();
for (int i = newDisplayAccessUIDs.size() - 1; i >= 0; i--) {
mDisplayAccessUIDs.append(newDisplayAccessUIDs.keyAt(i),
newDisplayAccessUIDs.valueAt(i));
}
}
}
// Checks if provided UID's content is present on the display and UID has access to it.
private boolean isUidPresentOnDisplayInternal(int uid, int displayId) {
synchronized (mSyncRoot) {
final IntArray displayUIDs = mDisplayAccessUIDs.get(displayId);
return displayUIDs != null && displayUIDs.indexOf(uid) != -1;
}
}
@Nullable
private IBinder getDisplayToken(int displayId) {
synchronized (mSyncRoot) {
final LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(displayId);
if (display != null) {
final DisplayDevice device = display.getPrimaryDisplayDeviceLocked();
if (device != null) {
return device.getDisplayTokenLocked();
}
}
}
return null;
}
private SurfaceControl.ScreenshotHardwareBuffer systemScreenshotInternal(int displayId) {
synchronized (mSyncRoot) {
final IBinder token = getDisplayToken(displayId);
if (token == null) {
return null;
}
final LogicalDisplay logicalDisplay = mLogicalDisplayMapper.getDisplayLocked(displayId);
if (logicalDisplay == null) {
return null;
}
final DisplayInfo displayInfo = logicalDisplay.getDisplayInfoLocked();
final SurfaceControl.DisplayCaptureArgs captureArgs =
new SurfaceControl.DisplayCaptureArgs.Builder(token)
.setSize(displayInfo.getNaturalWidth(), displayInfo.getNaturalHeight())
.setUseIdentityTransform(true)
.setCaptureSecureLayers(true)
.setAllowProtected(true)
.build();
return SurfaceControl.captureDisplay(captureArgs);
}
}
private SurfaceControl.ScreenshotHardwareBuffer userScreenshotInternal(int displayId) {
synchronized (mSyncRoot) {
final IBinder token = getDisplayToken(displayId);
if (token == null) {
return null;
}
final SurfaceControl.DisplayCaptureArgs captureArgs =
new SurfaceControl.DisplayCaptureArgs.Builder(token)
.build();
return SurfaceControl.captureDisplay(captureArgs);
}
}
@VisibleForTesting
DisplayedContentSamplingAttributes getDisplayedContentSamplingAttributesInternal(
int displayId) {
final IBinder token = getDisplayToken(displayId);
if (token == null) {
return null;
}
return SurfaceControl.getDisplayedContentSamplingAttributes(token);
}
@VisibleForTesting
boolean setDisplayedContentSamplingEnabledInternal(
int displayId, boolean enable, int componentMask, int maxFrames) {
final IBinder token = getDisplayToken(displayId);
if (token == null) {
return false;
}
return SurfaceControl.setDisplayedContentSamplingEnabled(
token, enable, componentMask, maxFrames);
}
@VisibleForTesting
DisplayedContentSample getDisplayedContentSampleInternal(int displayId,
long maxFrames, long timestamp) {
final IBinder token = getDisplayToken(displayId);
if (token == null) {
return null;
}
return SurfaceControl.getDisplayedContentSample(token, maxFrames, timestamp);
}
void resetBrightnessConfigurations() {
mPersistentDataStore.setBrightnessConfigurationForUser(null, mContext.getUserId(),
mContext.getPackageName());
mLogicalDisplayMapper.forEachLocked((logicalDisplay -> {
if (logicalDisplay.getDisplayInfoLocked().type != Display.TYPE_INTERNAL) {
return;
}
final String uniqueId = logicalDisplay.getPrimaryDisplayDeviceLocked().getUniqueId();
setBrightnessConfigurationForDisplayInternal(null, uniqueId, mContext.getUserId(),
mContext.getPackageName());
}));
}
void setAutoBrightnessLoggingEnabled(boolean enabled) {
synchronized (mSyncRoot) {
final DisplayPowerController displayPowerController = mDisplayPowerControllers.get(
Display.DEFAULT_DISPLAY);
if (displayPowerController != null) {
displayPowerController.setAutoBrightnessLoggingEnabled(enabled);
}
}
}
void setDisplayWhiteBalanceLoggingEnabled(boolean enabled) {
synchronized (mSyncRoot) {
final DisplayPowerController displayPowerController = mDisplayPowerControllers.get(
Display.DEFAULT_DISPLAY);
if (displayPowerController != null) {
displayPowerController.setDisplayWhiteBalanceLoggingEnabled(enabled);
}
}
}
void setDisplayModeDirectorLoggingEnabled(boolean enabled) {
synchronized (mSyncRoot) {
if (mDisplayModeDirector != null) {
mDisplayModeDirector.setLoggingEnabled(enabled);
}
}
}
void setAmbientColorTemperatureOverride(float cct) {
synchronized (mSyncRoot) {
final DisplayPowerController displayPowerController = mDisplayPowerControllers.get(
Display.DEFAULT_DISPLAY);
if (displayPowerController != null) {
displayPowerController.setAmbientColorTemperatureOverride(cct);
}
}
}
private void clearViewportsLocked() {
mViewports.clear();
}
private Optional getViewportType(DisplayDeviceInfo info) {
// Get the corresponding viewport type.
switch (info.touch) {
case DisplayDeviceInfo.TOUCH_INTERNAL:
return Optional.of(VIEWPORT_INTERNAL);
case DisplayDeviceInfo.TOUCH_EXTERNAL:
return Optional.of(VIEWPORT_EXTERNAL);
case DisplayDeviceInfo.TOUCH_VIRTUAL:
if (!TextUtils.isEmpty(info.uniqueId)) {
return Optional.of(VIEWPORT_VIRTUAL);
}
// fallthrough
default:
Slog.w(TAG, "Display " + info + " does not support input device matching.");
}
return Optional.empty();
}
private void configureDisplayLocked(SurfaceControl.Transaction t, DisplayDevice device) {
final DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
final boolean ownContent = (info.flags & DisplayDeviceInfo.FLAG_OWN_CONTENT_ONLY) != 0;
// Mirror the part of WM hierarchy that corresponds to the provided window token.
IBinder windowTokenClientToMirror = device.getWindowTokenClientToMirrorLocked();
// Find the logical display that the display device is showing.
// Certain displays only ever show their own content.
LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(device);
if (!ownContent && windowTokenClientToMirror == null) {
if (display != null && !display.hasContentLocked()) {
// If the display does not have any content of its own, then
// automatically mirror the requested logical display contents if possible.
display = mLogicalDisplayMapper.getDisplayLocked(
device.getDisplayIdToMirrorLocked());
}
if (display == null) {
display = mLogicalDisplayMapper.getDisplayLocked(Display.DEFAULT_DISPLAY);
}
}
// Apply the logical display configuration to the display device.
if (display == null) {
// TODO: no logical display for the device, blank it
Slog.w(TAG, "Missing logical display to use for physical display device: "
+ device.getDisplayDeviceInfoLocked());
return;
}
display.configureDisplayLocked(t, device, info.state == Display.STATE_OFF);
final Optional viewportType = getViewportType(info);
if (viewportType.isPresent()) {
populateViewportLocked(viewportType.get(), display.getDisplayIdLocked(), device, info);
}
}
/**
* Get internal or external viewport. Create it if does not currently exist.
* @param viewportType - either INTERNAL or EXTERNAL
* @return the viewport with the requested type
*/
private DisplayViewport getViewportLocked(int viewportType, String uniqueId) {
if (viewportType != VIEWPORT_INTERNAL && viewportType != VIEWPORT_EXTERNAL
&& viewportType != VIEWPORT_VIRTUAL) {
Slog.wtf(TAG, "Cannot call getViewportByTypeLocked for type "
+ DisplayViewport.typeToString(viewportType));
return null;
}
DisplayViewport viewport;
final int count = mViewports.size();
for (int i = 0; i < count; i++) {
viewport = mViewports.get(i);
if (viewport.type == viewportType && uniqueId.equals(viewport.uniqueId)) {
return viewport;
}
}
// Creates the viewport if none exists.
viewport = new DisplayViewport();
viewport.type = viewportType;
viewport.uniqueId = uniqueId;
mViewports.add(viewport);
return viewport;
}
private void populateViewportLocked(int viewportType, int displayId, DisplayDevice device,
DisplayDeviceInfo info) {
final DisplayViewport viewport = getViewportLocked(viewportType, info.uniqueId);
device.populateViewportLocked(viewport);
viewport.valid = true;
viewport.displayId = displayId;
viewport.isActive = Display.isActiveState(info.state);
}
private void updateViewportPowerStateLocked(LogicalDisplay display) {
final DisplayDevice device = display.getPrimaryDisplayDeviceLocked();
final DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
final Optional viewportType = getViewportType(info);
if (viewportType.isPresent()) {
for (DisplayViewport d : mViewports) {
if (d.type == viewportType.get() && info.uniqueId.equals(d.uniqueId)) {
// Update display view port power state
d.isActive = Display.isActiveState(info.state);
}
}
if (mInputManagerInternal != null) {
mHandler.sendEmptyMessage(MSG_UPDATE_VIEWPORT);
}
}
}
private void sendDisplayEventLocked(int displayId, @DisplayEvent int event) {
Message msg = mHandler.obtainMessage(MSG_DELIVER_DISPLAY_EVENT, displayId, event);
mHandler.sendMessage(msg);
}
private void sendDisplayGroupEvent(int groupId, int event) {
Message msg = mHandler.obtainMessage(MSG_DELIVER_DISPLAY_GROUP_EVENT, groupId, event);
mHandler.sendMessage(msg);
}
private void sendDisplayEventFrameRateOverrideLocked(int displayId) {
Message msg = mHandler.obtainMessage(MSG_DELIVER_DISPLAY_EVENT_FRAME_RATE_OVERRIDE,
displayId, DisplayManagerGlobal.EVENT_DISPLAY_CHANGED);
mHandler.sendMessage(msg);
}
// Requests that performTraversals be called at a
// later time to apply changes to surfaces and displays.
private void scheduleTraversalLocked(boolean inTraversal) {
if (!mPendingTraversal && mWindowManagerInternal != null) {
mPendingTraversal = true;
if (!inTraversal) {
mHandler.sendEmptyMessage(MSG_REQUEST_TRAVERSAL);
}
}
}
// Runs on Handler thread.
// Delivers display event notifications to callbacks.
private void deliverDisplayEvent(int displayId, ArraySet uids,
@DisplayEvent int event) {
if (DEBUG) {
Slog.d(TAG, "Delivering display event: displayId="
+ displayId + ", event=" + event);
}
// Grab the lock and copy the callbacks.
final int count;
synchronized (mSyncRoot) {
count = mCallbacks.size();
mTempCallbacks.clear();
for (int i = 0; i < count; i++) {
if (uids == null || uids.contains(mCallbacks.valueAt(i).mUid)) {
mTempCallbacks.add(mCallbacks.valueAt(i));
}
}
}
// After releasing the lock, send the notifications out.
for (int i = 0; i < mTempCallbacks.size(); i++) {
mTempCallbacks.get(i).notifyDisplayEventAsync(displayId, event);
}
mTempCallbacks.clear();
}
// Runs on Handler thread.
// Delivers display group event notifications to callbacks.
private void deliverDisplayGroupEvent(int groupId, int event) {
if (DEBUG) {
Slog.d(TAG, "Delivering display group event: groupId=" + groupId + ", event="
+ event);
}
switch (event) {
case LogicalDisplayMapper.DISPLAY_GROUP_EVENT_ADDED:
for (DisplayGroupListener listener : mDisplayGroupListeners) {
listener.onDisplayGroupAdded(groupId);
}
break;
case LogicalDisplayMapper.DISPLAY_GROUP_EVENT_CHANGED:
for (DisplayGroupListener listener : mDisplayGroupListeners) {
listener.onDisplayGroupChanged(groupId);
}
break;
case LogicalDisplayMapper.DISPLAY_GROUP_EVENT_REMOVED:
for (DisplayGroupListener listener : mDisplayGroupListeners) {
listener.onDisplayGroupRemoved(groupId);
}
break;
}
}
private IMediaProjectionManager getProjectionService() {
if (mProjectionService == null) {
IBinder b = ServiceManager.getService(Context.MEDIA_PROJECTION_SERVICE);
mProjectionService = IMediaProjectionManager.Stub.asInterface(b);
}
return mProjectionService;
}
private UserManager getUserManager() {
return mContext.getSystemService(UserManager.class);
}
private void dumpInternal(PrintWriter pw) {
pw.println("DISPLAY MANAGER (dumpsys display)");
synchronized (mSyncRoot) {
pw.println(" mOnlyCode=" + mOnlyCore);
pw.println(" mSafeMode=" + mSafeMode);
pw.println(" mPendingTraversal=" + mPendingTraversal);
pw.println(" mViewports=" + mViewports);
pw.println(" mDefaultDisplayDefaultColorMode=" + mDefaultDisplayDefaultColorMode);
pw.println(" mWifiDisplayScanRequestCount=" + mWifiDisplayScanRequestCount);
pw.println(" mStableDisplaySize=" + mStableDisplaySize);
pw.println(" mMinimumBrightnessCurve=" + mMinimumBrightnessCurve);
pw.println();
if (!mAreUserDisabledHdrTypesAllowed) {
pw.println(" mUserDisabledHdrTypes: size=" + mUserDisabledHdrTypes.length);
for (int type : mUserDisabledHdrTypes) {
pw.println(" " + type);
}
}
pw.println();
final int displayStateCount = mDisplayStates.size();
pw.println("Display States: size=" + displayStateCount);
for (int i = 0; i < displayStateCount; i++) {
final int displayId = mDisplayStates.keyAt(i);
final int displayState = mDisplayStates.valueAt(i);
final BrightnessPair brightnessPair = mDisplayBrightnesses.valueAt(i);
pw.println(" Display Id=" + displayId);
pw.println(" Display State=" + Display.stateToString(displayState));
pw.println(" Display Brightness=" + brightnessPair.brightness);
pw.println(" Display SdrBrightness=" + brightnessPair.sdrBrightness);
}
IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
ipw.increaseIndent();
pw.println();
pw.println("Display Adapters: size=" + mDisplayAdapters.size());
for (DisplayAdapter adapter : mDisplayAdapters) {
pw.println(" " + adapter.getName());
adapter.dumpLocked(ipw);
}
pw.println();
pw.println("Display Devices: size=" + mDisplayDeviceRepo.sizeLocked());
mDisplayDeviceRepo.forEachLocked(device -> {
pw.println(" " + device.getDisplayDeviceInfoLocked());
device.dumpLocked(ipw);
});
pw.println();
mLogicalDisplayMapper.dumpLocked(pw);
final int callbackCount = mCallbacks.size();
pw.println();
pw.println("Callbacks: size=" + callbackCount);
for (int i = 0; i < callbackCount; i++) {
CallbackRecord callback = mCallbacks.valueAt(i);
pw.println(" " + i + ": mPid=" + callback.mPid
+ ", mWifiDisplayScanRequested=" + callback.mWifiDisplayScanRequested);
}
final int displayPowerControllerCount = mDisplayPowerControllers.size();
pw.println();
pw.println("Display Power Controllers: size=" + displayPowerControllerCount);
for (int i = 0; i < displayPowerControllerCount; i++) {
mDisplayPowerControllers.valueAt(i).dump(pw);
}
if (mBrightnessTracker != null) {
pw.println();
mBrightnessTracker.dump(pw);
}
pw.println();
mPersistentDataStore.dump(pw);
}
pw.println();
mDisplayModeDirector.dump(pw);
}
private static float[] getFloatArray(TypedArray array) {
int length = array.length();
float[] floatArray = new float[length];
for (int i = 0; i < length; i++) {
floatArray[i] = array.getFloat(i, Float.NaN);
}
array.recycle();
return floatArray;
}
/**
* This is the object that everything in the display manager locks on.
* We make it an inner class within the {@link DisplayManagerService} to so that it is
* clear that the object belongs to the display manager service and that it is
* a unique object with a special purpose.
*/
public static final class SyncRoot {
}
@VisibleForTesting
static class Injector {
VirtualDisplayAdapter getVirtualDisplayAdapter(SyncRoot syncRoot, Context context,
Handler handler, DisplayAdapter.Listener displayAdapterListener) {
return new VirtualDisplayAdapter(syncRoot, context, handler, displayAdapterListener);
}
long getDefaultDisplayDelayTimeout() {
return WAIT_FOR_DEFAULT_DISPLAY_TIMEOUT;
}
boolean getAllowNonNativeRefreshRateOverride() {
return DisplayProperties
.debug_allow_non_native_refresh_rate_override().orElse(false);
}
}
@VisibleForTesting
DisplayDeviceInfo getDisplayDeviceInfoInternal(int displayId) {
synchronized (mSyncRoot) {
final LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(displayId);
if (display != null) {
final DisplayDevice displayDevice = display.getPrimaryDisplayDeviceLocked();
return displayDevice.getDisplayDeviceInfoLocked();
}
return null;
}
}
@VisibleForTesting
int getDisplayIdToMirrorInternal(int displayId) {
synchronized (mSyncRoot) {
final LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(displayId);
if (display != null) {
final DisplayDevice displayDevice = display.getPrimaryDisplayDeviceLocked();
return displayDevice.getDisplayIdToMirrorLocked();
}
return Display.INVALID_DISPLAY;
}
}
@VisibleForTesting
Surface getVirtualDisplaySurfaceInternal(IBinder appToken) {
synchronized (mSyncRoot) {
if (mVirtualDisplayAdapter == null) {
return null;
}
return mVirtualDisplayAdapter.getVirtualDisplaySurfaceLocked(appToken);
}
}
private void initializeDisplayPowerControllersLocked() {
mLogicalDisplayMapper.forEachLocked(this::addDisplayPowerControllerLocked);
}
private void addDisplayPowerControllerLocked(LogicalDisplay display) {
if (mPowerHandler == null) {
// initPowerManagement has not yet been called.
return;
}
if (mBrightnessTracker == null) {
mBrightnessTracker = new BrightnessTracker(mContext, null);
}
final BrightnessSetting brightnessSetting = new BrightnessSetting(mPersistentDataStore,
display, mSyncRoot);
final DisplayPowerController displayPowerController = new DisplayPowerController(
mContext, mDisplayPowerCallbacks, mPowerHandler, mSensorManager,
mDisplayBlanker, display, mBrightnessTracker, brightnessSetting,
() -> handleBrightnessChange(display));
mDisplayPowerControllers.append(display.getDisplayIdLocked(), displayPowerController);
}
private void handleBrightnessChange(LogicalDisplay display) {
sendDisplayEventLocked(display.getDisplayIdLocked(),
DisplayManagerGlobal.EVENT_DISPLAY_BRIGHTNESS_CHANGED);
}
private DisplayDevice getDeviceForDisplayLocked(int displayId) {
final LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(displayId);
return display == null ? null : display.getPrimaryDisplayDeviceLocked();
}
private BrightnessConfiguration getBrightnessConfigForDisplayWithPdsFallbackLocked(
String uniqueId, int userSerial) {
BrightnessConfiguration config =
mPersistentDataStore.getBrightnessConfigurationForDisplayLocked(
uniqueId, userSerial);
if (config == null) {
// Get from global configurations
config = mPersistentDataStore.getBrightnessConfiguration(userSerial);
}
return config;
}
private final class DisplayManagerHandler extends Handler {
public DisplayManagerHandler(Looper looper) {
super(looper, null, true /*async*/);
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_REGISTER_DEFAULT_DISPLAY_ADAPTERS:
registerDefaultDisplayAdapters();
break;
case MSG_REGISTER_ADDITIONAL_DISPLAY_ADAPTERS:
registerAdditionalDisplayAdapters();
break;
case MSG_DELIVER_DISPLAY_EVENT:
deliverDisplayEvent(msg.arg1, null, msg.arg2);
break;
case MSG_REQUEST_TRAVERSAL:
mWindowManagerInternal.requestTraversalFromDisplayManager();
break;
case MSG_UPDATE_VIEWPORT: {
final boolean changed;
synchronized (mSyncRoot) {
changed = !mTempViewports.equals(mViewports);
if (changed) {
mTempViewports.clear();
for (DisplayViewport d : mViewports) {
mTempViewports.add(d.makeCopy());
}
}
}
if (changed) {
mInputManagerInternal.setDisplayViewports(mTempViewports);
}
break;
}
case MSG_LOAD_BRIGHTNESS_CONFIGURATIONS:
loadBrightnessConfigurations();
break;
case MSG_DELIVER_DISPLAY_EVENT_FRAME_RATE_OVERRIDE:
ArraySet uids;
synchronized (mSyncRoot) {
int displayId = msg.arg1;
final LogicalDisplay display =
mLogicalDisplayMapper.getDisplayLocked(displayId);
if (display == null) {
break;
}
uids = display.getPendingFrameRateOverrideUids();
display.clearPendingFrameRateOverrideUids();
}
deliverDisplayEvent(msg.arg1, uids, msg.arg2);
break;
case MSG_DELIVER_DISPLAY_GROUP_EVENT:
deliverDisplayGroupEvent(msg.arg1, msg.arg2);
break;
}
}
}
private final class LogicalDisplayListener implements LogicalDisplayMapper.Listener {
@Override
public void onLogicalDisplayEventLocked(LogicalDisplay display, int event) {
switch (event) {
case LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_ADDED:
handleLogicalDisplayAddedLocked(display);
break;
case LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_CHANGED:
handleLogicalDisplayChangedLocked(display);
break;
case LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_REMOVED:
handleLogicalDisplayRemovedLocked(display);
break;
case LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_SWAPPED:
handleLogicalDisplaySwappedLocked(display);
break;
case LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_FRAME_RATE_OVERRIDES_CHANGED:
handleLogicalDisplayFrameRateOverridesChangedLocked(display);
break;
case LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_DEVICE_STATE_TRANSITION:
handleLogicalDisplayDeviceStateTransitionLocked(display);
break;
}
}
@Override
public void onDisplayGroupEventLocked(int groupId, int event) {
sendDisplayGroupEvent(groupId, event);
}
@Override
public void onTraversalRequested() {
synchronized (mSyncRoot) {
scheduleTraversalLocked(false);
}
}
}
private final class CallbackRecord implements DeathRecipient {
public final int mPid;
public final int mUid;
private final IDisplayManagerCallback mCallback;
private @EventsMask AtomicLong mEventsMask;
public boolean mWifiDisplayScanRequested;
CallbackRecord(int pid, int uid, IDisplayManagerCallback callback,
@EventsMask long eventsMask) {
mPid = pid;
mUid = uid;
mCallback = callback;
mEventsMask = new AtomicLong(eventsMask);
}
public void updateEventsMask(@EventsMask long eventsMask) {
mEventsMask.set(eventsMask);
}
@Override
public void binderDied() {
if (DEBUG) {
Slog.d(TAG, "Display listener for pid " + mPid + " died.");
}
onCallbackDied(this);
}
public void notifyDisplayEventAsync(int displayId, @DisplayEvent int event) {
if (!shouldSendEvent(event)) {
return;
}
try {
mCallback.onDisplayEvent(displayId, event);
} catch (RemoteException ex) {
Slog.w(TAG, "Failed to notify process "
+ mPid + " that displays changed, assuming it died.", ex);
binderDied();
}
}
private boolean shouldSendEvent(@DisplayEvent int event) {
final long mask = mEventsMask.get();
switch (event) {
case DisplayManagerGlobal.EVENT_DISPLAY_ADDED:
return (mask & DisplayManager.EVENT_FLAG_DISPLAY_ADDED) != 0;
case DisplayManagerGlobal.EVENT_DISPLAY_CHANGED:
return (mask & DisplayManager.EVENT_FLAG_DISPLAY_CHANGED) != 0;
case DisplayManagerGlobal.EVENT_DISPLAY_BRIGHTNESS_CHANGED:
return (mask & DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS) != 0;
case DisplayManagerGlobal.EVENT_DISPLAY_REMOVED:
return (mask & DisplayManager.EVENT_FLAG_DISPLAY_REMOVED) != 0;
default:
// This should never happen.
Slog.e(TAG, "Unknown display event " + event);
return true;
}
}
}
@VisibleForTesting
final class BinderService extends IDisplayManager.Stub {
/**
* Returns information about the specified logical display.
*
* @param displayId The logical display id.
* @return The logical display info, return {@code null} if the display does not exist or
* the calling UID isn't present on the display. The returned object must be treated as
* immutable.
*/
@Override // Binder call
public DisplayInfo getDisplayInfo(int displayId) {
final int callingUid = Binder.getCallingUid();
final long token = Binder.clearCallingIdentity();
try {
return getDisplayInfoInternal(displayId, callingUid);
} finally {
Binder.restoreCallingIdentity(token);
}
}
/**
* Returns the list of all display ids.
*/
@Override // Binder call
public int[] getDisplayIds() {
final int callingUid = Binder.getCallingUid();
final long token = Binder.clearCallingIdentity();
try {
synchronized (mSyncRoot) {
return mLogicalDisplayMapper.getDisplayIdsLocked(callingUid);
}
} finally {
Binder.restoreCallingIdentity(token);
}
}
@Override // Binder call
public boolean isUidPresentOnDisplay(int uid, int displayId) {
final long token = Binder.clearCallingIdentity();
try {
return isUidPresentOnDisplayInternal(uid, displayId);
} finally {
Binder.restoreCallingIdentity(token);
}
}
/**
* Returns the stable device display size, in pixels.
*/
@Override // Binder call
public Point getStableDisplaySize() {
final long token = Binder.clearCallingIdentity();
try {
return getStableDisplaySizeInternal();
} finally {
Binder.restoreCallingIdentity(token);
}
}
@Override // Binder call
public void registerCallback(IDisplayManagerCallback callback) {
registerCallbackWithEventMask(callback, DisplayManager.EVENT_FLAG_DISPLAY_ADDED
| DisplayManager.EVENT_FLAG_DISPLAY_CHANGED
| DisplayManager.EVENT_FLAG_DISPLAY_REMOVED);
}
@Override // Binder call
public void registerCallbackWithEventMask(IDisplayManagerCallback callback,
@EventsMask long eventsMask) {
if (callback == null) {
throw new IllegalArgumentException("listener must not be null");
}
final int callingPid = Binder.getCallingPid();
final int callingUid = Binder.getCallingUid();
final long token = Binder.clearCallingIdentity();
try {
registerCallbackInternal(callback, callingPid, callingUid, eventsMask);
} finally {
Binder.restoreCallingIdentity(token);
}
}
@Override // Binder call
public void startWifiDisplayScan() {
mContext.enforceCallingOrSelfPermission(Manifest.permission.CONFIGURE_WIFI_DISPLAY,
"Permission required to start wifi display scans");
final int callingPid = Binder.getCallingPid();
final long token = Binder.clearCallingIdentity();
try {
startWifiDisplayScanInternal(callingPid);
} finally {
Binder.restoreCallingIdentity(token);
}
}
@Override // Binder call
public void stopWifiDisplayScan() {
mContext.enforceCallingOrSelfPermission(Manifest.permission.CONFIGURE_WIFI_DISPLAY,
"Permission required to stop wifi display scans");
final int callingPid = Binder.getCallingPid();
final long token = Binder.clearCallingIdentity();
try {
stopWifiDisplayScanInternal(callingPid);
} finally {
Binder.restoreCallingIdentity(token);
}
}
@Override // Binder call
public void connectWifiDisplay(String address) {
if (address == null) {
throw new IllegalArgumentException("address must not be null");
}
mContext.enforceCallingOrSelfPermission(Manifest.permission.CONFIGURE_WIFI_DISPLAY,
"Permission required to connect to a wifi display");
final long token = Binder.clearCallingIdentity();
try {
connectWifiDisplayInternal(address);
} finally {
Binder.restoreCallingIdentity(token);
}
}
@Override // Binder call
public void disconnectWifiDisplay() {
// This request does not require special permissions.
// Any app can request disconnection from the currently active wifi display.
// This exception should no longer be needed once wifi display control moves
// to the media router service.
final long token = Binder.clearCallingIdentity();
try {
disconnectWifiDisplayInternal();
} finally {
Binder.restoreCallingIdentity(token);
}
}
@Override // Binder call
public void renameWifiDisplay(String address, String alias) {
if (address == null) {
throw new IllegalArgumentException("address must not be null");
}
mContext.enforceCallingOrSelfPermission(Manifest.permission.CONFIGURE_WIFI_DISPLAY,
"Permission required to rename to a wifi display");
final long token = Binder.clearCallingIdentity();
try {
renameWifiDisplayInternal(address, alias);
} finally {
Binder.restoreCallingIdentity(token);
}
}
@Override // Binder call
public void forgetWifiDisplay(String address) {
if (address == null) {
throw new IllegalArgumentException("address must not be null");
}
mContext.enforceCallingOrSelfPermission(Manifest.permission.CONFIGURE_WIFI_DISPLAY,
"Permission required to forget to a wifi display");
final long token = Binder.clearCallingIdentity();
try {
forgetWifiDisplayInternal(address);
} finally {
Binder.restoreCallingIdentity(token);
}
}
@Override // Binder call
public void pauseWifiDisplay() {
mContext.enforceCallingOrSelfPermission(Manifest.permission.CONFIGURE_WIFI_DISPLAY,
"Permission required to pause a wifi display session");
final long token = Binder.clearCallingIdentity();
try {
pauseWifiDisplayInternal();
} finally {
Binder.restoreCallingIdentity(token);
}
}
@Override // Binder call
public void resumeWifiDisplay() {
mContext.enforceCallingOrSelfPermission(Manifest.permission.CONFIGURE_WIFI_DISPLAY,
"Permission required to resume a wifi display session");
final long token = Binder.clearCallingIdentity();
try {
resumeWifiDisplayInternal();
} finally {
Binder.restoreCallingIdentity(token);
}
}
@Override // Binder call
public WifiDisplayStatus getWifiDisplayStatus() {
// This request does not require special permissions.
// Any app can get information about available wifi displays.
final long token = Binder.clearCallingIdentity();
try {
return getWifiDisplayStatusInternal();
} finally {
Binder.restoreCallingIdentity(token);
}
}
@Override // Binder call
public void setUserDisabledHdrTypes(int[] userDisabledFormats) {
mContext.enforceCallingOrSelfPermission(
Manifest.permission.WRITE_SECURE_SETTINGS,
"Permission required to write the user settings.");
final long token = Binder.clearCallingIdentity();
try {
setUserDisabledHdrTypesInternal(userDisabledFormats);
} finally {
Binder.restoreCallingIdentity(token);
}
}
@Override // Binder call
public void setAreUserDisabledHdrTypesAllowed(boolean areUserDisabledHdrTypesAllowed) {
mContext.enforceCallingOrSelfPermission(
Manifest.permission.WRITE_SECURE_SETTINGS,
"Permission required to write the user settings.");
final long token = Binder.clearCallingIdentity();
try {
setAreUserDisabledHdrTypesAllowedInternal(areUserDisabledHdrTypesAllowed);
} finally {
Binder.restoreCallingIdentity(token);
}
}
@Override // Binder call
public boolean areUserDisabledHdrTypesAllowed() {
synchronized (mSyncRoot) {
return mAreUserDisabledHdrTypesAllowed;
}
}
@Override // Binder call
public int[] getUserDisabledHdrTypes() {
return mUserDisabledHdrTypes;
}
@Override // Binder call
public void requestColorMode(int displayId, int colorMode) {
mContext.enforceCallingOrSelfPermission(
Manifest.permission.CONFIGURE_DISPLAY_COLOR_MODE,
"Permission required to change the display color mode");
final long token = Binder.clearCallingIdentity();
try {
requestColorModeInternal(displayId, colorMode);
} finally {
Binder.restoreCallingIdentity(token);
}
}
@Override // Binder call
public int createVirtualDisplay(VirtualDisplayConfig virtualDisplayConfig,
IVirtualDisplayCallback callback, IMediaProjection projection, String packageName) {
final int callingUid = Binder.getCallingUid();
if (!validatePackageName(callingUid, packageName)) {
throw new SecurityException("packageName must match the calling uid");
}
if (callback == null) {
throw new IllegalArgumentException("appToken must not be null");
}
if (virtualDisplayConfig == null) {
throw new IllegalArgumentException("virtualDisplayConfig must not be null");
}
final Surface surface = virtualDisplayConfig.getSurface();
int flags = virtualDisplayConfig.getFlags();
if (surface != null && surface.isSingleBuffered()) {
throw new IllegalArgumentException("Surface can't be single-buffered");
}
if ((flags & VIRTUAL_DISPLAY_FLAG_PUBLIC) != 0) {
flags |= VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR;
// Public displays can't be allowed to show content when locked.
if ((flags & VIRTUAL_DISPLAY_FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD) != 0) {
throw new IllegalArgumentException(
"Public display must not be marked as SHOW_WHEN_LOCKED_INSECURE");
}
}
if ((flags & VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY) != 0) {
flags &= ~VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR;
}
if ((flags & VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR) != 0) {
flags &= ~VIRTUAL_DISPLAY_FLAG_OWN_DISPLAY_GROUP;
}
if (projection != null) {
try {
if (!getProjectionService().isValidMediaProjection(projection)) {
throw new SecurityException("Invalid media projection");
}
flags = projection.applyVirtualDisplayFlags(flags);
} catch (RemoteException e) {
throw new SecurityException("unable to validate media projection or flags");
}
}
if (callingUid != Process.SYSTEM_UID &&
(flags & VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR) != 0) {
if (!canProjectVideo(projection)) {
throw new SecurityException("Requires CAPTURE_VIDEO_OUTPUT or "
+ "CAPTURE_SECURE_VIDEO_OUTPUT permission, or an appropriate "
+ "MediaProjection token in order to create a screen sharing virtual "
+ "display.");
}
}
if (callingUid != Process.SYSTEM_UID && (flags & VIRTUAL_DISPLAY_FLAG_SECURE) != 0) {
if (!canProjectSecureVideo(projection)) {
throw new SecurityException("Requires CAPTURE_SECURE_VIDEO_OUTPUT "
+ "or an appropriate MediaProjection token to create a "
+ "secure virtual display.");
}
}
if (callingUid != Process.SYSTEM_UID && (flags & VIRTUAL_DISPLAY_FLAG_TRUSTED) != 0) {
if (!checkCallingPermission(ADD_TRUSTED_DISPLAY, "createVirtualDisplay()")) {
EventLog.writeEvent(0x534e4554, "162627132", callingUid,
"Attempt to create a trusted display without holding permission!");
throw new SecurityException("Requires ADD_TRUSTED_DISPLAY permission to "
+ "create a trusted virtual display.");
}
}
if (callingUid != Process.SYSTEM_UID
&& (flags & VIRTUAL_DISPLAY_FLAG_OWN_DISPLAY_GROUP) != 0) {
if (!checkCallingPermission(ADD_TRUSTED_DISPLAY, "createVirtualDisplay()")) {
throw new SecurityException("Requires ADD_TRUSTED_DISPLAY permission to "
+ "create a virtual display which is not in the default DisplayGroup.");
}
}
if ((flags & VIRTUAL_DISPLAY_FLAG_TRUSTED) == 0) {
flags &= ~VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS;
}
// Sometimes users can have sensitive information in system decoration windows. An app
// could create a virtual display with system decorations support and read the user info
// from the surface.
// We should only allow adding flag VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS
// to trusted virtual displays.
final int trustedDisplayWithSysDecorFlag =
(VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS
| VIRTUAL_DISPLAY_FLAG_TRUSTED);
if ((flags & trustedDisplayWithSysDecorFlag)
== VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS
&& !checkCallingPermission(INTERNAL_SYSTEM_WINDOW, "createVirtualDisplay()")) {
throw new SecurityException("Requires INTERNAL_SYSTEM_WINDOW permission");
}
final long token = Binder.clearCallingIdentity();
try {
return createVirtualDisplayInternal(callback, projection, callingUid, packageName,
surface, flags, virtualDisplayConfig);
} finally {
Binder.restoreCallingIdentity(token);
}
}
@Override // Binder call
public void resizeVirtualDisplay(IVirtualDisplayCallback callback,
int width, int height, int densityDpi) {
if (width <= 0 || height <= 0 || densityDpi <= 0) {
throw new IllegalArgumentException("width, height, and densityDpi must be "
+ "greater than 0");
}
final long token = Binder.clearCallingIdentity();
try {
resizeVirtualDisplayInternal(callback.asBinder(), width, height, densityDpi);
} finally {
Binder.restoreCallingIdentity(token);
}
}
@Override // Binder call
public void setVirtualDisplaySurface(IVirtualDisplayCallback callback, Surface surface) {
if (surface != null && surface.isSingleBuffered()) {
throw new IllegalArgumentException("Surface can't be single-buffered");
}
final long token = Binder.clearCallingIdentity();
try {
setVirtualDisplaySurfaceInternal(callback.asBinder(), surface);
} finally {
Binder.restoreCallingIdentity(token);
}
}
@Override // Binder call
public void releaseVirtualDisplay(IVirtualDisplayCallback callback) {
final long token = Binder.clearCallingIdentity();
try {
releaseVirtualDisplayInternal(callback.asBinder());
} finally {
Binder.restoreCallingIdentity(token);
}
}
@Override // Binder call
public void setVirtualDisplayState(IVirtualDisplayCallback callback, boolean isOn) {
final long token = Binder.clearCallingIdentity();
try {
setVirtualDisplayStateInternal(callback.asBinder(), isOn);
} finally {
Binder.restoreCallingIdentity(token);
}
}
@Override // Binder call
public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
final long token = Binder.clearCallingIdentity();
try {
dumpInternal(pw);
} finally {
Binder.restoreCallingIdentity(token);
}
}
@Override // Binder call
public ParceledListSlice getBrightnessEvents(String callingPackage) {
mContext.enforceCallingOrSelfPermission(
Manifest.permission.BRIGHTNESS_SLIDER_USAGE,
"Permission to read brightness events.");
final int callingUid = Binder.getCallingUid();
AppOpsManager appOpsManager = mContext.getSystemService(AppOpsManager.class);
final int mode = appOpsManager.noteOp(AppOpsManager.OP_GET_USAGE_STATS,
callingUid, callingPackage);
final boolean hasUsageStats;
if (mode == AppOpsManager.MODE_DEFAULT) {
// The default behavior here is to check if PackageManager has given the app
// permission.
hasUsageStats = mContext.checkCallingPermission(
Manifest.permission.PACKAGE_USAGE_STATS)
== PackageManager.PERMISSION_GRANTED;
} else {
hasUsageStats = mode == AppOpsManager.MODE_ALLOWED;
}
final int userId = UserHandle.getUserId(callingUid);
final long token = Binder.clearCallingIdentity();
try {
synchronized (mSyncRoot) {
return mDisplayPowerControllers.get(Display.DEFAULT_DISPLAY)
.getBrightnessEvents(userId, hasUsageStats);
}
} finally {
Binder.restoreCallingIdentity(token);
}
}
@Override // Binder call
public ParceledListSlice getAmbientBrightnessStats() {
mContext.enforceCallingOrSelfPermission(
Manifest.permission.ACCESS_AMBIENT_LIGHT_STATS,
"Permission required to to access ambient light stats.");
final int callingUid = Binder.getCallingUid();
final int userId = UserHandle.getUserId(callingUid);
final long token = Binder.clearCallingIdentity();
try {
synchronized (mSyncRoot) {
return mDisplayPowerControllers.get(Display.DEFAULT_DISPLAY)
.getAmbientBrightnessStats(userId);
}
} finally {
Binder.restoreCallingIdentity(token);
}
}
@Override // Binder call
public void setBrightnessConfigurationForUser(
BrightnessConfiguration c, @UserIdInt int userId, String packageName) {
mContext.enforceCallingOrSelfPermission(
Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS,
"Permission required to change the display's brightness configuration");
if (userId != UserHandle.getCallingUserId()) {
mContext.enforceCallingOrSelfPermission(
Manifest.permission.INTERACT_ACROSS_USERS,
"Permission required to change the display brightness"
+ " configuration of another user");
}
final long token = Binder.clearCallingIdentity();
try {
synchronized (mSyncRoot) {
mLogicalDisplayMapper.forEachLocked(logicalDisplay -> {
if (logicalDisplay.getDisplayInfoLocked().type != Display.TYPE_INTERNAL) {
return;
}
final DisplayDevice displayDevice =
logicalDisplay.getPrimaryDisplayDeviceLocked();
setBrightnessConfigurationForDisplayInternal(c, displayDevice.getUniqueId(),
userId, packageName);
});
}
} finally {
Binder.restoreCallingIdentity(token);
}
}
@Override // Binder call
public void setBrightnessConfigurationForDisplay(BrightnessConfiguration c,
String uniqueId, int userId, String packageName) {
mContext.enforceCallingOrSelfPermission(
Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS,
"Permission required to change the display's brightness configuration");
if (userId != UserHandle.getCallingUserId()) {
mContext.enforceCallingOrSelfPermission(
Manifest.permission.INTERACT_ACROSS_USERS,
"Permission required to change the display brightness"
+ " configuration of another user");
}
final long token = Binder.clearCallingIdentity();
try {
setBrightnessConfigurationForDisplayInternal(c, uniqueId, userId, packageName);
} finally {
Binder.restoreCallingIdentity(token);
}
}
@Override // Binder call
public BrightnessConfiguration getBrightnessConfigurationForDisplay(String uniqueId,
int userId) {
mContext.enforceCallingOrSelfPermission(
Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS,
"Permission required to read the display's brightness configuration");
if (userId != UserHandle.getCallingUserId()) {
mContext.enforceCallingOrSelfPermission(
Manifest.permission.INTERACT_ACROSS_USERS,
"Permission required to read the display brightness"
+ " configuration of another user");
}
final long token = Binder.clearCallingIdentity();
final int userSerial = getUserManager().getUserSerialNumber(userId);
try {
synchronized (mSyncRoot) {
// Get from per-display configurations
BrightnessConfiguration config =
getBrightnessConfigForDisplayWithPdsFallbackLocked(
uniqueId, userSerial);
if (config == null) {
// Get default configuration
DisplayPowerController dpc = getDpcFromUniqueIdLocked(uniqueId);
if (dpc != null) {
config = dpc.getDefaultBrightnessConfiguration();
}
}
return config;
}
} finally {
Binder.restoreCallingIdentity(token);
}
}
@Override // Binder call
public BrightnessConfiguration getBrightnessConfigurationForUser(int userId) {
final String uniqueId;
synchronized (mSyncRoot) {
DisplayDevice displayDevice = mLogicalDisplayMapper.getDisplayLocked(
Display.DEFAULT_DISPLAY).getPrimaryDisplayDeviceLocked();
uniqueId = displayDevice.getUniqueId();
}
return getBrightnessConfigurationForDisplay(uniqueId, userId);
}
@Override // Binder call
public BrightnessConfiguration getDefaultBrightnessConfiguration() {
mContext.enforceCallingOrSelfPermission(
Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS,
"Permission required to read the display's default brightness configuration");
final long token = Binder.clearCallingIdentity();
try {
synchronized (mSyncRoot) {
return mDisplayPowerControllers.get(Display.DEFAULT_DISPLAY)
.getDefaultBrightnessConfiguration();
}
} finally {
Binder.restoreCallingIdentity(token);
}
}
@Override
public BrightnessInfo getBrightnessInfo(int displayId) {
mContext.enforceCallingOrSelfPermission(
Manifest.permission.CONTROL_DISPLAY_BRIGHTNESS,
"Permission required to read the display's brightness info.");
final long token = Binder.clearCallingIdentity();
try {
synchronized (mSyncRoot) {
DisplayPowerController dpc = mDisplayPowerControllers.get(displayId);
if (dpc != null) {
return dpc.getBrightnessInfo();
}
}
} finally {
Binder.restoreCallingIdentity(token);
}
return null;
}
@Override // Binder call
public boolean isMinimalPostProcessingRequested(int displayId) {
synchronized (mSyncRoot) {
return mLogicalDisplayMapper.getDisplayLocked(displayId)
.getRequestedMinimalPostProcessingLocked();
}
}
@Override // Binder call
public void setTemporaryBrightness(int displayId, float brightness) {
mContext.enforceCallingOrSelfPermission(
Manifest.permission.CONTROL_DISPLAY_BRIGHTNESS,
"Permission required to set the display's brightness");
final long token = Binder.clearCallingIdentity();
try {
synchronized (mSyncRoot) {
mDisplayPowerControllers.get(displayId)
.setTemporaryBrightness(brightness);
}
} finally {
Binder.restoreCallingIdentity(token);
}
}
@Override // Binder call
public void setBrightness(int displayId, float brightness) {
mContext.enforceCallingOrSelfPermission(
Manifest.permission.CONTROL_DISPLAY_BRIGHTNESS,
"Permission required to set the display's brightness");
if (!isValidBrightness(brightness)) {
Slog.w(TAG, "Attempted to set invalid brightness" + brightness);
return;
}
final long token = Binder.clearCallingIdentity();
try {
synchronized (mSyncRoot) {
DisplayPowerController dpc = mDisplayPowerControllers.get(displayId);
if (dpc != null) {
dpc.putScreenBrightnessSetting(brightness);
}
mPersistentDataStore.saveIfNeeded();
}
} finally {
Binder.restoreCallingIdentity(token);
}
}
@Override // Binder call
public float getBrightness(int displayId) {
float brightness = PowerManager.BRIGHTNESS_INVALID_FLOAT;
mContext.enforceCallingOrSelfPermission(
Manifest.permission.CONTROL_DISPLAY_BRIGHTNESS,
"Permission required to set the display's brightness");
final long token = Binder.clearCallingIdentity();
try {
synchronized (mSyncRoot) {
DisplayPowerController dpc = mDisplayPowerControllers.get(displayId);
if (dpc != null) {
brightness = dpc.getScreenBrightnessSetting();
}
}
} finally {
Binder.restoreCallingIdentity(token);
}
return brightness;
}
@Override // Binder call
public void setTemporaryAutoBrightnessAdjustment(float adjustment) {
mContext.enforceCallingOrSelfPermission(
Manifest.permission.CONTROL_DISPLAY_BRIGHTNESS,
"Permission required to set the display's auto brightness adjustment");
final long token = Binder.clearCallingIdentity();
try {
synchronized (mSyncRoot) {
mDisplayPowerControllers.get(Display.DEFAULT_DISPLAY)
.setTemporaryAutoBrightnessAdjustment(adjustment);
}
} finally {
Binder.restoreCallingIdentity(token);
}
}
@Override // Binder call
public void onShellCommand(FileDescriptor in, FileDescriptor out,
FileDescriptor err, String[] args, ShellCallback callback,
ResultReceiver resultReceiver) {
new DisplayManagerShellCommand(DisplayManagerService.this).exec(this, in, out, err,
args, callback, resultReceiver);
}
@Override // Binder call
public Curve getMinimumBrightnessCurve() {
final long token = Binder.clearCallingIdentity();
try {
return getMinimumBrightnessCurveInternal();
} finally {
Binder.restoreCallingIdentity(token);
}
}
@Override // Binder call
public int getPreferredWideGamutColorSpaceId() {
final long token = Binder.clearCallingIdentity();
try {
return getPreferredWideGamutColorSpaceIdInternal();
} finally {
Binder.restoreCallingIdentity(token);
}
}
@Override // Binder call
public void setShouldAlwaysRespectAppRequestedMode(boolean enabled) {
mContext.enforceCallingOrSelfPermission(
Manifest.permission.OVERRIDE_DISPLAY_MODE_REQUESTS,
"Permission required to override display mode requests.");
final long token = Binder.clearCallingIdentity();
try {
setShouldAlwaysRespectAppRequestedModeInternal(enabled);
} finally {
Binder.restoreCallingIdentity(token);
}
}
@Override // Binder call
public boolean shouldAlwaysRespectAppRequestedMode() {
mContext.enforceCallingOrSelfPermission(
Manifest.permission.OVERRIDE_DISPLAY_MODE_REQUESTS,
"Permission required to override display mode requests.");
final long token = Binder.clearCallingIdentity();
try {
return shouldAlwaysRespectAppRequestedModeInternal();
} finally {
Binder.restoreCallingIdentity(token);
}
}
@Override // Binder call
public void setRefreshRateSwitchingType(int newValue) {
mContext.enforceCallingOrSelfPermission(
Manifest.permission.MODIFY_REFRESH_RATE_SWITCHING_TYPE,
"Permission required to modify refresh rate switching type.");
final long token = Binder.clearCallingIdentity();
try {
setRefreshRateSwitchingTypeInternal(newValue);
} finally {
Binder.restoreCallingIdentity(token);
}
}
@Override // Binder call
public int getRefreshRateSwitchingType() {
final long token = Binder.clearCallingIdentity();
try {
return getRefreshRateSwitchingTypeInternal();
} finally {
Binder.restoreCallingIdentity(token);
}
}
private boolean validatePackageName(int uid, String packageName) {
if (packageName != null) {
String[] packageNames = mContext.getPackageManager().getPackagesForUid(uid);
if (packageNames != null) {
for (String n : packageNames) {
if (n.equals(packageName)) {
return true;
}
}
}
}
return false;
}
private boolean canProjectVideo(IMediaProjection projection) {
if (projection != null) {
try {
if (projection.canProjectVideo()) {
return true;
}
} catch (RemoteException e) {
Slog.e(TAG, "Unable to query projection service for permissions", e);
}
}
if (checkCallingPermission(CAPTURE_VIDEO_OUTPUT, "canProjectVideo()")) {
return true;
}
return canProjectSecureVideo(projection);
}
private boolean canProjectSecureVideo(IMediaProjection projection) {
if (projection != null) {
try {
if (projection.canProjectSecureVideo()){
return true;
}
} catch (RemoteException e) {
Slog.e(TAG, "Unable to query projection service for permissions", e);
}
}
return checkCallingPermission(CAPTURE_SECURE_VIDEO_OUTPUT, "canProjectSecureVideo()");
}
private boolean checkCallingPermission(String permission, String func) {
if (mContext.checkCallingPermission(permission) == PackageManager.PERMISSION_GRANTED) {
return true;
}
final String msg = "Permission Denial: " + func + " from pid=" + Binder.getCallingPid()
+ ", uid=" + Binder.getCallingUid() + " requires " + permission;
Slog.w(TAG, msg);
return false;
}
}
private static boolean isValidBrightness(float brightness) {
return !Float.isNaN(brightness)
&& (brightness >= PowerManager.BRIGHTNESS_MIN)
&& (brightness <= PowerManager.BRIGHTNESS_MAX);
}
private final class LocalService extends DisplayManagerInternal {
@Override
public void initPowerManagement(final DisplayPowerCallbacks callbacks, Handler handler,
SensorManager sensorManager) {
synchronized (mSyncRoot) {
mDisplayPowerCallbacks = callbacks;
mSensorManager = sensorManager;
mPowerHandler = handler;
initializeDisplayPowerControllersLocked();
}
mHandler.sendEmptyMessage(MSG_LOAD_BRIGHTNESS_CONFIGURATIONS);
}
@Override
public boolean requestPowerState(int groupId, DisplayPowerRequest request,
boolean waitForNegativeProximity) {
synchronized (mSyncRoot) {
final DisplayGroup displayGroup = mLogicalDisplayMapper.getDisplayGroupLocked(
groupId);
if (displayGroup == null) {
return true;
}
final int size = displayGroup.getSizeLocked();
boolean ready = true;
for (int i = 0; i < size; i++) {
final int id = displayGroup.getIdLocked(i);
final DisplayDevice displayDevice = mLogicalDisplayMapper.getDisplayLocked(
id).getPrimaryDisplayDeviceLocked();
final int flags = displayDevice.getDisplayDeviceInfoLocked().flags;
if ((flags & DisplayDeviceInfo.FLAG_NEVER_BLANK) == 0) {
final DisplayPowerController displayPowerController =
mDisplayPowerControllers.get(id);
ready &= displayPowerController.requestPowerState(request,
waitForNegativeProximity);
}
}
return ready;
}
}
@Override
public boolean isProximitySensorAvailable() {
synchronized (mSyncRoot) {
return mDisplayPowerControllers.get(Display.DEFAULT_DISPLAY)
.isProximitySensorAvailable();
}
}
@Override
public void registerDisplayGroupListener(DisplayGroupListener listener) {
mDisplayGroupListeners.add(listener);
}
@Override
public void unregisterDisplayGroupListener(DisplayGroupListener listener) {
mDisplayGroupListeners.remove(listener);
}
@Override
public SurfaceControl.ScreenshotHardwareBuffer systemScreenshot(int displayId) {
return systemScreenshotInternal(displayId);
}
@Override
public SurfaceControl.ScreenshotHardwareBuffer userScreenshot(int displayId) {
return userScreenshotInternal(displayId);
}
@Override
public DisplayInfo getDisplayInfo(int displayId) {
return getDisplayInfoInternal(displayId, Process.myUid());
}
@Override
public Point getDisplayPosition(int displayId) {
synchronized (mSyncRoot) {
final LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(displayId);
if (display != null) {
return display.getDisplayPosition();
}
return null;
}
}
@Override
public void registerDisplayTransactionListener(DisplayTransactionListener listener) {
if (listener == null) {
throw new IllegalArgumentException("listener must not be null");
}
registerDisplayTransactionListenerInternal(listener);
}
@Override
public void unregisterDisplayTransactionListener(DisplayTransactionListener listener) {
if (listener == null) {
throw new IllegalArgumentException("listener must not be null");
}
unregisterDisplayTransactionListenerInternal(listener);
}
@Override
public void setDisplayInfoOverrideFromWindowManager(int displayId, DisplayInfo info) {
setDisplayInfoOverrideFromWindowManagerInternal(displayId, info);
}
@Override
public void getNonOverrideDisplayInfo(int displayId, DisplayInfo outInfo) {
getNonOverrideDisplayInfoInternal(displayId, outInfo);
}
@Override
public void performTraversal(SurfaceControl.Transaction t) {
performTraversalInternal(t);
}
@Override
public void setDisplayProperties(int displayId, boolean hasContent,
float requestedRefreshRate, int requestedMode, float requestedMinRefreshRate,
float requestedMaxRefreshRate, boolean requestedMinimalPostProcessing,
boolean inTraversal) {
setDisplayPropertiesInternal(displayId, hasContent, requestedRefreshRate,
requestedMode, requestedMinRefreshRate, requestedMaxRefreshRate,
requestedMinimalPostProcessing, inTraversal);
}
@Override
public void setDisplayOffsets(int displayId, int x, int y) {
setDisplayOffsetsInternal(displayId, x, y);
}
@Override
public void setDisplayScalingDisabled(int displayId, boolean disableScaling) {
setDisplayScalingDisabledInternal(displayId, disableScaling);
}
@Override
public void setDisplayAccessUIDs(SparseArray newDisplayAccessUIDs) {
setDisplayAccessUIDsInternal(newDisplayAccessUIDs);
}
@Override
public void persistBrightnessTrackerState() {
synchronized (mSyncRoot) {
mDisplayPowerControllers.get(Display.DEFAULT_DISPLAY)
.persistBrightnessTrackerState();
}
}
@Override
public void onOverlayChanged() {
synchronized (mSyncRoot) {
mDisplayDeviceRepo.forEachLocked(DisplayDevice::onOverlayChangedLocked);
}
}
@Override
public DisplayedContentSamplingAttributes getDisplayedContentSamplingAttributes(
int displayId) {
return getDisplayedContentSamplingAttributesInternal(displayId);
}
@Override
public boolean setDisplayedContentSamplingEnabled(
int displayId, boolean enable, int componentMask, int maxFrames) {
return setDisplayedContentSamplingEnabledInternal(
displayId, enable, componentMask, maxFrames);
}
@Override
public DisplayedContentSample getDisplayedContentSample(int displayId,
long maxFrames, long timestamp) {
return getDisplayedContentSampleInternal(displayId, maxFrames, timestamp);
}
@Override
public void ignoreProximitySensorUntilChanged() {
mDisplayPowerControllers.get(Display.DEFAULT_DISPLAY)
.ignoreProximitySensorUntilChanged();
}
@Override
public int getRefreshRateSwitchingType() {
return getRefreshRateSwitchingTypeInternal();
}
@Override
public RefreshRateRange getRefreshRateForDisplayAndSensor(int displayId, String sensorName,
String sensorType) {
final SensorManager sensorManager;
synchronized (mSyncRoot) {
sensorManager = mSensorManager;
}
if (sensorManager == null) {
return null;
}
// Verify that the specified sensor exists.
final Sensor sensor = SensorUtils.findSensor(sensorManager, sensorType, sensorName,
SensorUtils.NO_FALLBACK);
if (sensor == null) {
return null;
}
synchronized (mSyncRoot) {
final LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(displayId);
if (display == null) {
return null;
}
final DisplayDevice device = display.getPrimaryDisplayDeviceLocked();
if (device == null) {
return null;
}
final DisplayDeviceConfig config = device.getDisplayDeviceConfig();
SensorData sensorData = config.getProximitySensor();
if (sensorData.matches(sensorName, sensorType)) {
return new RefreshRateRange(sensorData.minRefreshRate,
sensorData.maxRefreshRate);
}
}
return null;
}
@Override
public List getRefreshRateLimitations(int displayId) {
final DisplayDeviceConfig config;
synchronized (mSyncRoot) {
final DisplayDevice device = getDeviceForDisplayLocked(displayId);
if (device == null) {
return null;
}
config = device.getDisplayDeviceConfig();
}
return config.getRefreshRateLimitations();
}
@Override
public IBinder getWindowTokenClientToMirror(int displayId) {
final DisplayDevice device;
synchronized (mSyncRoot) {
device = getDeviceForDisplayLocked(displayId);
if (device == null) {
return null;
}
}
return device.getWindowTokenClientToMirrorLocked();
}
@Override
public void setWindowTokenClientToMirror(int displayId, IBinder windowToken) {
synchronized (mSyncRoot) {
final DisplayDevice device = getDeviceForDisplayLocked(displayId);
if (device != null) {
device.setWindowTokenClientToMirrorLocked(windowToken);
}
}
}
@Override
public Point getDisplaySurfaceDefaultSize(int displayId) {
final DisplayDevice device;
synchronized (mSyncRoot) {
device = getDeviceForDisplayLocked(displayId);
if (device == null) {
return null;
}
}
return device.getDisplaySurfaceDefaultSize();
}
}
class DesiredDisplayModeSpecsObserver
implements DisplayModeDirector.DesiredDisplayModeSpecsListener {
private final Consumer mSpecsChangedConsumer = display -> {
int displayId = display.getDisplayIdLocked();
DisplayModeDirector.DesiredDisplayModeSpecs desiredDisplayModeSpecs =
mDisplayModeDirector.getDesiredDisplayModeSpecs(displayId);
DisplayModeDirector.DesiredDisplayModeSpecs existingDesiredDisplayModeSpecs =
display.getDesiredDisplayModeSpecsLocked();
if (DEBUG) {
Slog.i(TAG,
"Comparing display specs: " + desiredDisplayModeSpecs
+ ", existing: " + existingDesiredDisplayModeSpecs);
}
if (!desiredDisplayModeSpecs.equals(existingDesiredDisplayModeSpecs)) {
display.setDesiredDisplayModeSpecsLocked(desiredDisplayModeSpecs);
mChanged = true;
}
};
@GuardedBy("mSyncRoot")
private boolean mChanged = false;
public void onDesiredDisplayModeSpecsChanged() {
synchronized (mSyncRoot) {
mChanged = false;
mLogicalDisplayMapper.forEachLocked(mSpecsChangedConsumer);
if (mChanged) {
scheduleTraversalLocked(false);
mChanged = false;
}
}
}
}
/**
* Listens to changes in device state and reports the state to LogicalDisplayMapper.
*/
class DeviceStateListener implements DeviceStateManager.DeviceStateCallback {
@Override
public void onStateChanged(int deviceState) {
synchronized (mSyncRoot) {
mLogicalDisplayMapper.setDeviceStateLocked(deviceState);
}
}
};
private class BrightnessPair {
public float brightness;
public float sdrBrightness;
BrightnessPair(float brightness, float sdrBrightness) {
this.brightness = brightness;
this.sdrBrightness = sdrBrightness;
}
}
/**
* Functional interface for providing time.
* TODO(b/184781936): merge with PowerManagerService.Clock
*/
@VisibleForTesting
public interface Clock {
/**
* Returns current time in milliseconds since boot, not counting time spent in deep sleep.
*/
long uptimeMillis();
}
}