Home | History | Annotate | Download | only in display
      1 /*
      2  * Copyright (C) 2012 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.server.display;
     18 
     19 import android.content.Context;
     20 import android.os.Handler;
     21 import android.os.IBinder;
     22 import android.os.Looper;
     23 import android.os.SystemProperties;
     24 import android.util.SparseArray;
     25 import android.view.Display;
     26 import android.view.DisplayEventReceiver;
     27 import android.view.Surface;
     28 import android.view.SurfaceControl;
     29 import android.view.SurfaceControl.PhysicalDisplayInfo;
     30 
     31 import java.io.PrintWriter;
     32 
     33 /**
     34  * A display adapter for the local displays managed by Surface Flinger.
     35  * <p>
     36  * Display adapters are guarded by the {@link DisplayManagerService.SyncRoot} lock.
     37  * </p>
     38  */
     39 final class LocalDisplayAdapter extends DisplayAdapter {
     40     private static final String TAG = "LocalDisplayAdapter";
     41 
     42     private static final int[] BUILT_IN_DISPLAY_IDS_TO_SCAN = new int[] {
     43             SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN,
     44             SurfaceControl.BUILT_IN_DISPLAY_ID_HDMI,
     45     };
     46 
     47     private final SparseArray<LocalDisplayDevice> mDevices =
     48             new SparseArray<LocalDisplayDevice>();
     49     private HotplugDisplayEventReceiver mHotplugReceiver;
     50 
     51     private final SurfaceControl.PhysicalDisplayInfo mTempPhys = new SurfaceControl.PhysicalDisplayInfo();
     52 
     53     // Called with SyncRoot lock held.
     54     public LocalDisplayAdapter(DisplayManagerService.SyncRoot syncRoot,
     55             Context context, Handler handler, Listener listener) {
     56         super(syncRoot, context, handler, listener, TAG);
     57     }
     58 
     59     @Override
     60     public void registerLocked() {
     61         super.registerLocked();
     62 
     63         mHotplugReceiver = new HotplugDisplayEventReceiver(getHandler().getLooper());
     64 
     65         for (int builtInDisplayId : BUILT_IN_DISPLAY_IDS_TO_SCAN) {
     66             tryConnectDisplayLocked(builtInDisplayId);
     67         }
     68     }
     69 
     70     private void tryConnectDisplayLocked(int builtInDisplayId) {
     71         IBinder displayToken = SurfaceControl.getBuiltInDisplay(builtInDisplayId);
     72         if (displayToken != null && SurfaceControl.getDisplayInfo(displayToken, mTempPhys)) {
     73             LocalDisplayDevice device = mDevices.get(builtInDisplayId);
     74             if (device == null) {
     75                 // Display was added.
     76                 device = new LocalDisplayDevice(displayToken, builtInDisplayId, mTempPhys);
     77                 mDevices.put(builtInDisplayId, device);
     78                 sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_ADDED);
     79             } else if (device.updatePhysicalDisplayInfoLocked(mTempPhys)) {
     80                 // Display properties changed.
     81                 sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_CHANGED);
     82             }
     83         } else {
     84             // The display is no longer available. Ignore the attempt to add it.
     85             // If it was connected but has already been disconnected, we'll get a
     86             // disconnect event that will remove it from mDevices.
     87         }
     88     }
     89 
     90     private void tryDisconnectDisplayLocked(int builtInDisplayId) {
     91         LocalDisplayDevice device = mDevices.get(builtInDisplayId);
     92         if (device != null) {
     93             // Display was removed.
     94             mDevices.remove(builtInDisplayId);
     95             sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_REMOVED);
     96         }
     97     }
     98 
     99     private final class LocalDisplayDevice extends DisplayDevice {
    100         private final int mBuiltInDisplayId;
    101         private final SurfaceControl.PhysicalDisplayInfo mPhys;
    102 
    103         private DisplayDeviceInfo mInfo;
    104         private boolean mHavePendingChanges;
    105         private boolean mBlanked;
    106 
    107         public LocalDisplayDevice(IBinder displayToken, int builtInDisplayId,
    108                 SurfaceControl.PhysicalDisplayInfo phys) {
    109             super(LocalDisplayAdapter.this, displayToken);
    110             mBuiltInDisplayId = builtInDisplayId;
    111             mPhys = new SurfaceControl.PhysicalDisplayInfo(phys);
    112         }
    113 
    114         public boolean updatePhysicalDisplayInfoLocked(SurfaceControl.PhysicalDisplayInfo phys) {
    115             if (!mPhys.equals(phys)) {
    116                 mPhys.copyFrom(phys);
    117                 mHavePendingChanges = true;
    118                 return true;
    119             }
    120             return false;
    121         }
    122 
    123         @Override
    124         public void applyPendingDisplayDeviceInfoChangesLocked() {
    125             if (mHavePendingChanges) {
    126                 mInfo = null;
    127                 mHavePendingChanges = false;
    128             }
    129         }
    130 
    131         @Override
    132         public DisplayDeviceInfo getDisplayDeviceInfoLocked() {
    133             if (mInfo == null) {
    134                 mInfo = new DisplayDeviceInfo();
    135                 mInfo.width = mPhys.width;
    136                 mInfo.height = mPhys.height;
    137                 mInfo.refreshRate = mPhys.refreshRate;
    138 
    139                 // Assume that all built-in displays that have secure output (eg. HDCP) also
    140                 // support compositing from gralloc protected buffers.
    141                 if (mPhys.secure) {
    142                     mInfo.flags = DisplayDeviceInfo.FLAG_SECURE
    143                             | DisplayDeviceInfo.FLAG_SUPPORTS_PROTECTED_BUFFERS;
    144                 }
    145 
    146                 if (mBuiltInDisplayId == SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN) {
    147                     mInfo.name = getContext().getResources().getString(
    148                             com.android.internal.R.string.display_manager_built_in_display_name);
    149                     mInfo.flags |= DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY
    150                             | DisplayDeviceInfo.FLAG_ROTATES_WITH_CONTENT;
    151                     mInfo.type = Display.TYPE_BUILT_IN;
    152                     mInfo.densityDpi = (int)(mPhys.density * 160 + 0.5f);
    153                     mInfo.xDpi = mPhys.xDpi;
    154                     mInfo.yDpi = mPhys.yDpi;
    155                     mInfo.touch = DisplayDeviceInfo.TOUCH_INTERNAL;
    156                 } else {
    157                     mInfo.type = Display.TYPE_HDMI;
    158                     mInfo.flags |= DisplayDeviceInfo.FLAG_PRESENTATION;
    159                     mInfo.name = getContext().getResources().getString(
    160                             com.android.internal.R.string.display_manager_hdmi_display_name);
    161                     mInfo.touch = DisplayDeviceInfo.TOUCH_EXTERNAL;
    162                     mInfo.setAssumedDensityForExternalDisplay(mPhys.width, mPhys.height);
    163 
    164                     // For demonstration purposes, allow rotation of the external display.
    165                     // In the future we might allow the user to configure this directly.
    166                     if ("portrait".equals(SystemProperties.get("persist.demo.hdmirotation"))) {
    167                         mInfo.rotation = Surface.ROTATION_270;
    168                     }
    169                 }
    170             }
    171             return mInfo;
    172         }
    173 
    174         @Override
    175         public void blankLocked() {
    176             mBlanked = true;
    177             SurfaceControl.blankDisplay(getDisplayTokenLocked());
    178         }
    179 
    180         @Override
    181         public void unblankLocked() {
    182             mBlanked = false;
    183             SurfaceControl.unblankDisplay(getDisplayTokenLocked());
    184         }
    185 
    186         @Override
    187         public void dumpLocked(PrintWriter pw) {
    188             super.dumpLocked(pw);
    189             pw.println("mBuiltInDisplayId=" + mBuiltInDisplayId);
    190             pw.println("mPhys=" + mPhys);
    191             pw.println("mBlanked=" + mBlanked);
    192         }
    193     }
    194 
    195     private final class HotplugDisplayEventReceiver extends DisplayEventReceiver {
    196         public HotplugDisplayEventReceiver(Looper looper) {
    197             super(looper);
    198         }
    199 
    200         @Override
    201         public void onHotplug(long timestampNanos, int builtInDisplayId, boolean connected) {
    202             synchronized (getSyncRoot()) {
    203                 if (connected) {
    204                     tryConnectDisplayLocked(builtInDisplayId);
    205                 } else {
    206                     tryDisconnectDisplayLocked(builtInDisplayId);
    207                 }
    208             }
    209         }
    210     }
    211 }