diff options
author | Jeff Brown <jeffbrown@google.com> | 2012-08-28 03:27:37 -0700 |
---|---|---|
committer | Jeff Brown <jeffbrown@google.com> | 2012-08-29 15:34:17 -0700 |
commit | bd6e1500aedc5461e832f69e76341bff0e55fa2b (patch) | |
tree | a7f6e0a3524872002f2904cc43d926166c3c4515 /services/java/com/android/server/display/DisplayManagerService.java | |
parent | c53abc4d42a707caddf7ec9bb7d041125a09dbd7 (diff) |
Add initial multi-display support.
Split the DisplayManager into two parts. One part is bound
to a Context and takes care of Display compatibility and
caching Display objects on behalf of the Context. The other
part is global and takes care of communicating with the
DisplayManagerService, handling callbacks, and caching
DisplayInfo objects on behalf of the process.
Implemented support for enumerating Displays and getting
callbacks when displays are added, removed or changed.
Elaborated the roles of DisplayManagerService, DisplayAdapter,
and DisplayDevice. We now support having multiple display
adapters registered, each of which can register multiple display
devices and configure them dynamically.
Added an OverlayDisplayAdapter which is used to simulate
secondary displays by means of overlay windows. Different
configurations of overlays can be selected using a new
setting in the Developer Settings panel. The overlays can
be repositioned and resized by the user for convenience.
At the moment, all displays are mirrors of display 0 and
no display transformations are applied. This will be improved
in future patches.
Refactored the way that the window manager creates its threads.
The OverlayDisplayAdapter needs to be able to use hardware
acceleration so it must share the same UI thread as the Keyguard
and window manager policy. We now handle this explicitly as
part of starting up the system server. This puts us in a
better position to consider how we might want to share (or not
share) Loopers among components.
Overlay displays are disabled when in safe mode or in only-core
mode to reduce the number of dependencies started in these modes.
Change-Id: Ic2a661d5448dde01b095ab150697cb6791d69bb5
Diffstat (limited to 'services/java/com/android/server/display/DisplayManagerService.java')
-rw-r--r-- | services/java/com/android/server/display/DisplayManagerService.java | 534 |
1 files changed, 465 insertions, 69 deletions
diff --git a/services/java/com/android/server/display/DisplayManagerService.java b/services/java/com/android/server/display/DisplayManagerService.java index 1463780a3289..cf835ef209b4 100644 --- a/services/java/com/android/server/display/DisplayManagerService.java +++ b/services/java/com/android/server/display/DisplayManagerService.java @@ -16,52 +16,151 @@ package com.android.server.display; +import com.android.internal.util.IndentingPrintWriter; + import android.Manifest; import android.content.Context; import android.content.pm.PackageManager; -import android.hardware.display.DisplayManager; +import android.hardware.display.DisplayManagerGlobal; import android.hardware.display.IDisplayManager; +import android.hardware.display.IDisplayManagerCallback; import android.os.Binder; +import android.os.Handler; import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.RemoteException; +import android.os.SystemClock; import android.os.SystemProperties; +import android.util.Slog; +import android.util.SparseArray; import android.view.Display; import android.view.DisplayInfo; import android.view.Surface; import java.io.FileDescriptor; import java.io.PrintWriter; +import java.io.StringWriter; import java.util.ArrayList; /** - * Manages the properties, media routing and power state of attached displays. + * Manages attached displays. * <p> - * The display manager service does not own or directly control the displays. - * Instead, other components in the system register their display adapters with the - * display manager service which acts as a central controller. + * 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. + * </p><p> + * 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 built-in local 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. + * </p><p> + * 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.DisplayAdapterListener} 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. + * </p><p> + * To keep things simple, display adapters and display devices are single-threaded + * and are only accessed on the display manager's handler thread. Of course, the + * display manager must be accessible by multiple thread (especially for + * incoming binder calls) so all of the display manager's state is synchronized + * and guarded by a lock. * </p> */ public final class DisplayManagerService extends IDisplayManager.Stub { private static final String TAG = "DisplayManagerService"; + private static final boolean DEBUG = false; private static final String SYSTEM_HEADLESS = "ro.config.headless"; + private static final long WAIT_FOR_DEFAULT_DISPLAY_TIMEOUT = 10000; + + private static final int MSG_REGISTER_DEFAULT_DISPLAY_ADAPTER = 1; + private static final int MSG_REGISTER_ADDITIONAL_DISPLAY_ADAPTERS = 2; + private static final int MSG_DELIVER_DISPLAY_EVENT = 3; private final Object mLock = new Object(); private final Context mContext; private final boolean mHeadless; + private final DisplayManagerHandler mHandler; + private final DisplayAdapterListener mDisplayAdapterListener = new DisplayAdapterListener(); + private final SparseArray<CallbackRecord> mCallbacks = + new SparseArray<CallbackRecord>(); + + // List of all currently registered display adapters. private final ArrayList<DisplayAdapter> mDisplayAdapters = new ArrayList<DisplayAdapter>(); - private final DisplayInfo mDefaultDisplayInfo = new DisplayInfo(); - private DisplayDevice mDefaultDisplayDevice; - public DisplayManagerService(Context context) { + // List of all currently connected display devices. + private final ArrayList<DisplayDevice> mDisplayDevices = new ArrayList<DisplayDevice>(); + + // List of all logical displays, indexed by logical display id. + private final SparseArray<LogicalDisplay> mLogicalDisplays = new SparseArray<LogicalDisplay>(); + private int mNextNonDefaultDisplayId = Display.DEFAULT_DISPLAY + 1; + + // True if in safe mode. + // This option may disable certain display adapters. + private 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. + private boolean mOnlyCore; + + // Temporary callback list, used when sending display events to applications. + private ArrayList<CallbackRecord> mTempCallbacks = new ArrayList<CallbackRecord>(); + + public DisplayManagerService(Context context, Handler uiHandler) { mContext = context; mHeadless = SystemProperties.get(SYSTEM_HEADLESS).equals("1"); - registerDefaultDisplayAdapter(); + mHandler = new DisplayManagerHandler(uiHandler.getLooper()); + mHandler.sendEmptyMessage(MSG_REGISTER_DEFAULT_DISPLAY_ADAPTER); + } + + /** + * Pauses the boot process to wait for the first display to be initialized. + */ + public boolean waitForDefaultDisplay() { + synchronized (mLock) { + long timeout = SystemClock.uptimeMillis() + WAIT_FOR_DEFAULT_DISPLAY_TIMEOUT; + while (mLogicalDisplays.get(Display.DEFAULT_DISPLAY) == null) { + long delay = timeout - SystemClock.uptimeMillis(); + if (delay <= 0) { + return false; + } + if (DEBUG) { + Slog.d(TAG, "waitForDefaultDisplay: waiting, timeout=" + delay); + } + try { + mLock.wait(delay); + } catch (InterruptedException ex) { + } + } + } + return true; + } + + /** + * Called when the system is ready to go. + */ + public void systemReady(boolean safeMode, boolean onlyCore) { + synchronized (mLock) { + mSafeMode = safeMode; + mOnlyCore = onlyCore; + } + mHandler.sendEmptyMessage(MSG_REGISTER_ADDITIONAL_DISPLAY_ADAPTERS); } + // Runs on handler. private void registerDefaultDisplayAdapter() { + // Register default display adapter. if (mHeadless) { registerDisplayAdapter(new HeadlessDisplayAdapter(mContext)); } else { @@ -69,6 +168,34 @@ public final class DisplayManagerService extends IDisplayManager.Stub { } } + // Runs on handler. + private void registerAdditionalDisplayAdapters() { + if (shouldRegisterNonEssentialDisplayAdapters()) { + registerDisplayAdapter(new OverlayDisplayAdapter(mContext)); + } + } + + private boolean shouldRegisterNonEssentialDisplayAdapters() { + // 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. + synchronized (mLock) { + return !mSafeMode && !mOnlyCore; + } + } + + // Runs on handler. + private void registerDisplayAdapter(DisplayAdapter adapter) { + synchronized (mLock) { + mDisplayAdapters.add(adapter); + } + + adapter.register(mDisplayAdapterListener); + } + // FIXME: this isn't the right API for the long term public void getDefaultExternalDisplayDeviceInfo(DisplayDeviceInfo info) { // hardcoded assuming 720p touch screen plugged into HDMI and USB @@ -87,95 +214,224 @@ public final class DisplayManagerService extends IDisplayManager.Stub { } /** - * Set the new display orientation. + * Sets the new logical display orientation. + * * @param displayId The logical display id. * @param orientation One of the Surface.ROTATION_* constants. */ public void setDisplayOrientation(int displayId, int orientation) { synchronized (mLock) { - if (displayId != Display.DEFAULT_DISPLAY) { - throw new UnsupportedOperationException(); - } - - IBinder displayToken = mDefaultDisplayDevice.getDisplayToken(); - if (displayToken != null) { - Surface.openTransaction(); - try { - Surface.setDisplayOrientation(displayToken, orientation); - } finally { - Surface.closeTransaction(); + // TODO: update mirror transforms + LogicalDisplay display = mLogicalDisplays.get(displayId); + if (display != null && display.mPrimaryDisplayDevice != null) { + IBinder displayToken = display.mPrimaryDisplayDevice.getDisplayToken(); + if (displayToken != null) { + Surface.openTransaction(); + try { + Surface.setDisplayOrientation(displayToken, orientation); + } finally { + Surface.closeTransaction(); + } } + + display.mBaseDisplayInfo.rotation = orientation; + sendDisplayEventLocked(displayId, DisplayManagerGlobal.EVENT_DISPLAY_CHANGED); } } } /** - * Save away new DisplayInfo data. + * Overrides the display information of a particular logical display. + * This is used by the window manager to control the size and characteristics + * of the default display. + * * @param displayId The logical display id. * @param info The new data to be stored. */ - public void setDisplayInfo(int displayId, DisplayInfo info) { + public void setDisplayInfoOverrideFromWindowManager(int displayId, DisplayInfo info) { synchronized (mLock) { - if (displayId != Display.DEFAULT_DISPLAY) { - throw new UnsupportedOperationException(); + LogicalDisplay display = mLogicalDisplays.get(displayId); + if (display != null) { + if (info != null) { + if (display.mOverrideDisplayInfo == null) { + display.mOverrideDisplayInfo = new DisplayInfo(); + } + display.mOverrideDisplayInfo.copyFrom(info); + } else { + display.mOverrideDisplayInfo = null; + } + + sendDisplayEventLocked(displayId, DisplayManagerGlobal.EVENT_DISPLAY_CHANGED); } - mDefaultDisplayInfo.copyFrom(info); } } /** - * Return requested DisplayInfo. - * @param displayId The data to retrieve. - * @param outInfo The structure to receive the data. + * Returns information about the specified logical display. + * + * @param displayId The logical display id. + * @param The logical display info, or null if the display does not exist. */ @Override // Binder call - public boolean getDisplayInfo(int displayId, DisplayInfo outInfo) { + public DisplayInfo getDisplayInfo(int displayId) { synchronized (mLock) { - if (displayId != Display.DEFAULT_DISPLAY) { - return false; + LogicalDisplay display = mLogicalDisplays.get(displayId); + if (display != null) { + if (display.mOverrideDisplayInfo != null) { + return new DisplayInfo(display.mOverrideDisplayInfo); + } + return new DisplayInfo(display.mBaseDisplayInfo); } - outInfo.copyFrom(mDefaultDisplayInfo); - return true; + return null; } } - private void registerDisplayAdapter(DisplayAdapter adapter) { - mDisplayAdapters.add(adapter); - adapter.register(new DisplayAdapter.Listener() { - @Override - public void onDisplayDeviceAdded(DisplayDevice device) { - mDefaultDisplayDevice = device; - DisplayDeviceInfo deviceInfo = new DisplayDeviceInfo(); - device.getInfo(deviceInfo); - copyDisplayInfoFromDeviceInfo(mDefaultDisplayInfo, deviceInfo); + @Override // Binder call + public int[] getDisplayIds() { + synchronized (mLock) { + final int count = mLogicalDisplays.size(); + int[] displayIds = new int[count]; + for (int i = 0; i > count; i++) { + displayIds[i] = mLogicalDisplays.keyAt(i); } + return displayIds; + } + } - @Override - public void onDisplayDeviceRemoved(DisplayDevice device) { + @Override // Binder call + public void registerCallback(IDisplayManagerCallback callback) { + if (callback == null) { + throw new IllegalArgumentException("listener must not be null"); + } + + synchronized (mLock) { + int callingPid = Binder.getCallingPid(); + if (mCallbacks.get(callingPid) != null) { + throw new SecurityException("The calling process has already " + + "registered an IDisplayManagerCallback."); } - }); + + CallbackRecord record = new CallbackRecord(callingPid, callback); + try { + IBinder binder = callback.asBinder(); + binder.linkToDeath(record, 0); + } catch (RemoteException ex) { + // give up + throw new RuntimeException(ex); + } + + mCallbacks.put(callingPid, record); + } } - private void copyDisplayInfoFromDeviceInfo( - DisplayInfo displayInfo, DisplayDeviceInfo deviceInfo) { - // Bootstrap the logical display using the physical display. - displayInfo.appWidth = deviceInfo.width; - displayInfo.appHeight = deviceInfo.height; - displayInfo.logicalWidth = deviceInfo.width; - displayInfo.logicalHeight = deviceInfo.height; - displayInfo.rotation = Surface.ROTATION_0; - displayInfo.refreshRate = deviceInfo.refreshRate; - displayInfo.logicalDensityDpi = deviceInfo.densityDpi; - displayInfo.physicalXDpi = deviceInfo.xDpi; - displayInfo.physicalYDpi = deviceInfo.yDpi; - displayInfo.smallestNominalAppWidth = deviceInfo.width; - displayInfo.smallestNominalAppHeight = deviceInfo.height; - displayInfo.largestNominalAppWidth = deviceInfo.width; - displayInfo.largestNominalAppHeight = deviceInfo.height; + private void onCallbackDied(int pid) { + synchronized (mLock) { + mCallbacks.remove(pid); + } + } + + // Runs on handler. + private void handleDisplayDeviceAdded(DisplayDevice device) { + synchronized (mLock) { + if (mDisplayDevices.contains(device)) { + Slog.w(TAG, "Attempted to add already added display device: " + device); + return; + } + + mDisplayDevices.add(device); + + LogicalDisplay display = new LogicalDisplay(device); + display.updateFromPrimaryDisplayDevice(); + + boolean isDefault = (display.mPrimaryDisplayDeviceInfo.flags + & DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY) != 0; + if (isDefault && mLogicalDisplays.get(Display.DEFAULT_DISPLAY) != null) { + Slog.w(TAG, "Attempted to add a second default device: " + device); + isDefault = false; + } + + int displayId = isDefault ? Display.DEFAULT_DISPLAY : mNextNonDefaultDisplayId++; + mLogicalDisplays.put(displayId, display); + + sendDisplayEventLocked(displayId, DisplayManagerGlobal.EVENT_DISPLAY_ADDED); + + // Wake up waitForDefaultDisplay. + if (isDefault) { + mLock.notifyAll(); + } + } + } + + // Runs on handler. + private void handleDisplayDeviceChanged(DisplayDevice device) { + synchronized (mLock) { + if (!mDisplayDevices.contains(device)) { + Slog.w(TAG, "Attempted to change non-existent display device: " + device); + return; + } + + for (int i = mLogicalDisplays.size(); i-- > 0; ) { + LogicalDisplay display = mLogicalDisplays.valueAt(i); + if (display.mPrimaryDisplayDevice == device) { + final int displayId = mLogicalDisplays.keyAt(i); + display.updateFromPrimaryDisplayDevice(); + sendDisplayEventLocked(displayId, DisplayManagerGlobal.EVENT_DISPLAY_CHANGED); + } + } + } + } + + // Runs on handler. + private void handleDisplayDeviceRemoved(DisplayDevice device) { + synchronized (mLock) { + if (!mDisplayDevices.remove(device)) { + Slog.w(TAG, "Attempted to remove non-existent display device: " + device); + return; + } + + for (int i = mLogicalDisplays.size(); i-- > 0; ) { + LogicalDisplay display = mLogicalDisplays.valueAt(i); + if (display.mPrimaryDisplayDevice == device) { + final int displayId = mLogicalDisplays.keyAt(i); + mLogicalDisplays.removeAt(i); + sendDisplayEventLocked(displayId, DisplayManagerGlobal.EVENT_DISPLAY_REMOVED); + } + } + } + } + + // Posts a message to send a display event at the next opportunity. + private void sendDisplayEventLocked(int displayId, int event) { + Message msg = mHandler.obtainMessage(MSG_DELIVER_DISPLAY_EVENT, displayId, event); + mHandler.sendMessage(msg); + } + + // Runs on handler. + // This method actually sends display event notifications. + // Note that it must be very careful not to be holding the lock while sending + // is in progress. + private void deliverDisplayEvent(int displayId, int event) { + if (DEBUG) { + Slog.d(TAG, "Delivering display event: displayId=" + displayId + ", event=" + event); + } + + final int count; + synchronized (mLock) { + count = mCallbacks.size(); + mTempCallbacks.clear(); + for (int i = 0; i < count; i++) { + mTempCallbacks.add(mCallbacks.valueAt(i)); + } + } + + for (int i = 0; i < count; i++) { + mTempCallbacks.get(i).notifyDisplayEventAsync(displayId, event); + } + mTempCallbacks.clear(); } @Override // Binder call - public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) { if (mContext == null || mContext.checkCallingOrSelfPermission(Manifest.permission.DUMP) != PackageManager.PERMISSION_GRANTED) { @@ -184,19 +440,159 @@ public final class DisplayManagerService extends IDisplayManager.Stub { return; } - pw.println("DISPLAY MANAGER (dumpsys display)\n"); + pw.println("DISPLAY MANAGER (dumpsys display)"); + pw.println(" mHeadless=" + mHeadless); - pw.println("Headless: " + mHeadless); + mHandler.runWithScissors(new Runnable() { + @Override + public void run() { + dumpLocal(pw); + } + }); + } + // Runs on handler. + private void dumpLocal(PrintWriter pw) { synchronized (mLock) { + IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " "); + + pw.println(); + pw.println("Display Adapters: size=" + mDisplayAdapters.size()); for (DisplayAdapter adapter : mDisplayAdapters) { - pw.println("Adapter: " + adapter.getName()); + pw.println(" " + adapter.getName()); + adapter.dump(ipw); } - pw.println("Default display info: " + mDefaultDisplayInfo); + pw.println(); + pw.println("Display Devices: size=" + mDisplayDevices.size()); + for (DisplayDevice device : mDisplayDevices) { + pw.println(" " + device); + } + + final int logicalDisplayCount = mLogicalDisplays.size(); + pw.println(); + pw.println("Logical Displays: size=" + logicalDisplayCount); + for (int i = 0; i < logicalDisplayCount; i++) { + int displayId = mLogicalDisplays.keyAt(i); + LogicalDisplay display = mLogicalDisplays.valueAt(i); + pw.println(" Display " + displayId + ":"); + pw.println(" mPrimaryDisplayDevice=" + display.mPrimaryDisplayDevice); + pw.println(" mBaseDisplayInfo=" + display.mBaseDisplayInfo); + pw.println(" mOverrideDisplayInfo=" + + display.mOverrideDisplayInfo); + } + } + } + + private final class DisplayManagerHandler extends Handler { + public DisplayManagerHandler(Looper looper) { + super(looper, null, true /*async*/); } - pw.println("Default display: " - + DisplayManager.getInstance().getRealDisplay(Display.DEFAULT_DISPLAY)); + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_REGISTER_DEFAULT_DISPLAY_ADAPTER: + registerDefaultDisplayAdapter(); + break; + + case MSG_REGISTER_ADDITIONAL_DISPLAY_ADAPTERS: + registerAdditionalDisplayAdapters(); + break; + + case MSG_DELIVER_DISPLAY_EVENT: + deliverDisplayEvent(msg.arg1, msg.arg2); + break; + } + } + } + + private final class DisplayAdapterListener implements DisplayAdapter.Listener { + @Override + public void onDisplayDeviceEvent(DisplayDevice device, int event) { + switch (event) { + case DisplayAdapter.DISPLAY_DEVICE_EVENT_ADDED: + handleDisplayDeviceAdded(device); + break; + + case DisplayAdapter.DISPLAY_DEVICE_EVENT_CHANGED: + handleDisplayDeviceChanged(device); + break; + + case DisplayAdapter.DISPLAY_DEVICE_EVENT_REMOVED: + handleDisplayDeviceRemoved(device); + break; + } + } + } + + private final class CallbackRecord implements DeathRecipient { + private final int mPid; + private final IDisplayManagerCallback mCallback; + + public CallbackRecord(int pid, IDisplayManagerCallback callback) { + mPid = pid; + mCallback = callback; + } + + @Override + public void binderDied() { + if (DEBUG) { + Slog.d(TAG, "Display listener for pid " + mPid + " died."); + } + onCallbackDied(mPid); + } + + public void notifyDisplayEventAsync(int displayId, int event) { + try { + mCallback.onDisplayEvent(displayId, event); + } catch (RemoteException ex) { + Slog.w(TAG, "Failed to notify process " + + mPid + " that displays changed, assuming it died.", ex); + binderDied(); + } + } + } + + /** + * Each logical display is primarily associated with one display device. + * The primary display device is nominally responsible for the basic properties + * of the logical display such as its size, refresh rate, and dpi. + * + * A logical display may be mirrored onto other display devices besides its + * primary display device, but it always remains bound to its primary. + * Note that the contents of a logical display may not always be visible, even + * on its primary display device, such as in the case where the logical display's + * primary display device is currently mirroring content from a different logical display. + */ + private final static class LogicalDisplay { + public final DisplayInfo mBaseDisplayInfo = new DisplayInfo(); + public DisplayInfo mOverrideDisplayInfo; // set by the window manager + + public final DisplayDevice mPrimaryDisplayDevice; + public final DisplayDeviceInfo mPrimaryDisplayDeviceInfo = new DisplayDeviceInfo(); + + public LogicalDisplay(DisplayDevice primaryDisplayDevice) { + mPrimaryDisplayDevice = primaryDisplayDevice; + } + + public void updateFromPrimaryDisplayDevice() { + // Bootstrap the logical display using its associated primary physical display. + mPrimaryDisplayDevice.getInfo(mPrimaryDisplayDeviceInfo); + + mBaseDisplayInfo.appWidth = mPrimaryDisplayDeviceInfo.width; + mBaseDisplayInfo.appHeight = mPrimaryDisplayDeviceInfo.height; + mBaseDisplayInfo.logicalWidth = mPrimaryDisplayDeviceInfo.width; + mBaseDisplayInfo.logicalHeight = mPrimaryDisplayDeviceInfo.height; + mBaseDisplayInfo.rotation = Surface.ROTATION_0; + mBaseDisplayInfo.refreshRate = mPrimaryDisplayDeviceInfo.refreshRate; + mBaseDisplayInfo.logicalDensityDpi = mPrimaryDisplayDeviceInfo.densityDpi; + mBaseDisplayInfo.physicalXDpi = mPrimaryDisplayDeviceInfo.xDpi; + mBaseDisplayInfo.physicalYDpi = mPrimaryDisplayDeviceInfo.yDpi; + mBaseDisplayInfo.smallestNominalAppWidth = mPrimaryDisplayDeviceInfo.width; + mBaseDisplayInfo.smallestNominalAppHeight = mPrimaryDisplayDeviceInfo.height; + mBaseDisplayInfo.largestNominalAppWidth = mPrimaryDisplayDeviceInfo.width; + mBaseDisplayInfo.largestNominalAppHeight = mPrimaryDisplayDeviceInfo.height; + } } } |