1 /* 2 * Copyright (C) 2018 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 android.car.cluster; 18 19 import android.annotation.Nullable; 20 import android.content.Context; 21 import android.hardware.display.DisplayManager; 22 import android.hardware.display.DisplayManager.DisplayListener; 23 import android.os.SystemProperties; 24 import android.text.TextUtils; 25 import android.util.Log; 26 import android.view.Display; 27 import android.view.DisplayAddress; 28 29 /** 30 * This class provides a display for instrument cluster renderer. 31 * <p> 32 * By default it will try to provide physical secondary display if it is connected, if secondary 33 * display is not connected during creation of this class then it will start networked virtual 34 * display and listens for incoming connections. 35 * 36 * @see {@link NetworkedVirtualDisplay} 37 */ 38 public class ClusterDisplayProvider { 39 private static final String TAG = "Cluster.DisplayProvider"; 40 41 private static final String RO_CLUSTER_DISPLAY_PORT = "ro.car.cluster.displayport"; 42 private static final String PERSIST_CLUSTER_DISPLAY_PORT = 43 "persist.car.cluster.displayport"; 44 private static final int NETWORKED_DISPLAY_WIDTH = 1280; 45 private static final int NETWORKED_DISPLAY_HEIGHT = 720; 46 private static final int NETWORKED_DISPLAY_DPI = 320; 47 48 private final DisplayListener mListener; 49 private final DisplayManager mDisplayManager; 50 51 private NetworkedVirtualDisplay mNetworkedVirtualDisplay; 52 private int mClusterDisplayId = -1; 53 ClusterDisplayProvider(Context context, DisplayListener clusterDisplayListener)54 ClusterDisplayProvider(Context context, DisplayListener clusterDisplayListener) { 55 mListener = clusterDisplayListener; 56 mDisplayManager = context.getSystemService(DisplayManager.class); 57 58 Display clusterDisplay = getInstrumentClusterDisplay(mDisplayManager); 59 if (clusterDisplay != null) { 60 Log.i(TAG, String.format("Found display: %s (id: %d, owner: %s)", 61 clusterDisplay.getName(), clusterDisplay.getDisplayId(), 62 clusterDisplay.getOwnerPackageName())); 63 mClusterDisplayId = clusterDisplay.getDisplayId(); 64 clusterDisplayListener.onDisplayAdded(clusterDisplay.getDisplayId()); 65 trackClusterDisplay(null /* no need to track display by name */); 66 } else { 67 Log.i(TAG, "No physical cluster display found, starting network display"); 68 setupNetworkDisplay(context); 69 } 70 } 71 setupNetworkDisplay(Context context)72 private void setupNetworkDisplay(Context context) { 73 mNetworkedVirtualDisplay = new NetworkedVirtualDisplay(context, 74 NETWORKED_DISPLAY_WIDTH, NETWORKED_DISPLAY_HEIGHT, NETWORKED_DISPLAY_DPI); 75 String displayName = mNetworkedVirtualDisplay.start(); 76 trackClusterDisplay(displayName); 77 } 78 trackClusterDisplay(@ullable String displayName)79 private void trackClusterDisplay(@Nullable String displayName) { 80 mDisplayManager.registerDisplayListener(new DisplayListener() { 81 @Override 82 public void onDisplayAdded(int displayId) { 83 boolean clusterDisplayAdded = false; 84 85 if (displayName == null && mClusterDisplayId == -1) { 86 mClusterDisplayId = displayId; 87 clusterDisplayAdded = true; 88 } else { 89 Display display = mDisplayManager.getDisplay(displayId); 90 if (display != null && TextUtils.equals(display.getName(), displayName)) { 91 mClusterDisplayId = displayId; 92 clusterDisplayAdded = true; 93 } 94 } 95 96 if (clusterDisplayAdded) { 97 mListener.onDisplayAdded(displayId); 98 } 99 } 100 101 @Override 102 public void onDisplayRemoved(int displayId) { 103 if (displayId == mClusterDisplayId) { 104 mClusterDisplayId = -1; 105 mListener.onDisplayRemoved(displayId); 106 } 107 } 108 109 @Override 110 public void onDisplayChanged(int displayId) { 111 if (displayId == mClusterDisplayId) { 112 mListener.onDisplayChanged(displayId); 113 } 114 } 115 116 }, null); 117 } 118 getInstrumentClusterDisplay(DisplayManager displayManager)119 private static Display getInstrumentClusterDisplay(DisplayManager displayManager) { 120 Display[] displays = displayManager.getDisplays(); 121 Log.d(TAG, "There are currently " + displays.length + " displays connected."); 122 123 final int displayPortPrimary = 0; // primary port should not be instrument cluster. 124 int displayPort = SystemProperties.getInt(PERSIST_CLUSTER_DISPLAY_PORT, 125 displayPortPrimary); 126 if (displayPort == displayPortPrimary) { 127 displayPort = SystemProperties.getInt(RO_CLUSTER_DISPLAY_PORT, 128 displayPortPrimary); 129 if (displayPort == displayPortPrimary) { 130 return null; 131 } 132 } 133 // match port for system display ( = null getOwnerPackageName()) 134 // with separate check for main display as main display should be never picked up. 135 for (Display display : displays) { 136 if (display.getDisplayId() != Display.DEFAULT_DISPLAY 137 && display.getOwnerPackageName() == null 138 && display.getAddress() != null 139 && display.getAddress() instanceof DisplayAddress.Physical) { 140 final byte port = ((DisplayAddress.Physical) display.getAddress()).getPort(); 141 if (displayPort == port) { 142 return display; 143 } 144 } 145 } 146 return null; 147 } 148 149 @Override toString()150 public String toString() { 151 return getClass().getSimpleName() + "{" 152 + " clusterDisplayId = " + mClusterDisplayId 153 + "}"; 154 } 155 } 156