1 /* 2 * Copyright (C) 2019 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.view; 18 19 import android.annotation.NonNull; 20 import android.annotation.Nullable; 21 import android.os.Parcel; 22 import android.os.Parcelable; 23 24 /** Display identifier that is stable across reboots. 25 * 26 * @hide 27 */ 28 public abstract class DisplayAddress implements Parcelable { 29 /** 30 * Creates an address for a physical display given its stable ID. 31 * 32 * A physical display ID is stable if the display can be identified using EDID information. 33 * 34 * @param physicalDisplayId A physical display ID. 35 * @return The {@link Physical} address. 36 * @see com.android.server.display.DisplayControl#getPhysicalDisplayIds 37 */ 38 @NonNull fromPhysicalDisplayId(long physicalDisplayId)39 public static Physical fromPhysicalDisplayId(long physicalDisplayId) { 40 return new Physical(physicalDisplayId); 41 } 42 43 /** 44 * Creates an address for a physical display given its port and model. 45 * 46 * @param port A port in the range [0, 255]. 47 * @param model A positive integer, or {@code null} if the model cannot be identified. 48 * @return The {@link Physical} address. 49 */ 50 @NonNull fromPortAndModel(int port, Long model)51 public static Physical fromPortAndModel(int port, Long model) { 52 return new Physical(port, model); 53 } 54 55 /** 56 * Creates an address for a network display given its MAC address. 57 * 58 * @param macAddress A MAC address in colon notation. 59 * @return The {@link Network} address. 60 */ 61 @NonNull fromMacAddress(String macAddress)62 public static Network fromMacAddress(String macAddress) { 63 return new Network(macAddress); 64 } 65 66 /** 67 * Address for a physically connected display. 68 * 69 * A {@link Physical} address is represented by a 64-bit identifier combining the port and model 70 * of a display. The port, located in the least significant byte, uniquely identifies a physical 71 * connector on the device for display output like eDP or HDMI. The model, located in the upper 72 * bits, uniquely identifies a display model across manufacturers by encoding EDID information. 73 * While the port is always stable, the model may not be available if EDID identification is not 74 * supported by the platform, in which case the address is not unique. 75 */ 76 public static final class Physical extends DisplayAddress { 77 private static final long UNKNOWN_MODEL = 0; 78 private static final int MODEL_SHIFT = 8; 79 80 private final long mPhysicalDisplayId; 81 82 /** 83 * Stable display ID combining port and model. 84 * 85 * @return An ID in the range [0, 2^64) interpreted as signed. 86 * @see com.android.server.display.DisplayControl#getPhysicalDisplayIds 87 */ getPhysicalDisplayId()88 public long getPhysicalDisplayId() { 89 return mPhysicalDisplayId; 90 } 91 92 /** 93 * Physical port to which the display is connected. 94 * 95 * @return A port in the range [0, 255]. 96 */ getPort()97 public int getPort() { 98 return (int) (mPhysicalDisplayId & 0xFF); 99 } 100 101 /** 102 * Model identifier unique across manufacturers. 103 * 104 * @return A positive integer, or {@code null} if the model cannot be identified. 105 */ 106 @Nullable getModel()107 public Long getModel() { 108 final long model = mPhysicalDisplayId >>> MODEL_SHIFT; 109 return model == UNKNOWN_MODEL ? null : model; 110 } 111 112 @Override equals(@ullable Object other)113 public boolean equals(@Nullable Object other) { 114 return other instanceof Physical 115 && mPhysicalDisplayId == ((Physical) other).mPhysicalDisplayId; 116 } 117 118 @Override toString()119 public String toString() { 120 final StringBuilder builder = new StringBuilder("{") 121 .append("port=").append(getPort()); 122 123 final Long model = getModel(); 124 if (model != null) { 125 builder.append(", model=0x").append(Long.toHexString(model)); 126 } 127 128 return builder.append("}").toString(); 129 } 130 131 @Override hashCode()132 public int hashCode() { 133 return Long.hashCode(mPhysicalDisplayId); 134 } 135 136 @Override writeToParcel(Parcel out, int flags)137 public void writeToParcel(Parcel out, int flags) { 138 out.writeLong(mPhysicalDisplayId); 139 } 140 141 /** 142 * This method is meant to check to see if the ports match 143 * @param a1 Address to compare 144 * @param a2 Address to compare 145 * 146 * @return true if the arguments have the same port, and at least one does not specify 147 * a model. 148 */ isPortMatch(DisplayAddress a1, DisplayAddress a2)149 public static boolean isPortMatch(DisplayAddress a1, DisplayAddress a2) { 150 // Both displays must be of type Physical 151 if (!(a1 instanceof Physical && a2 instanceof Physical)) { 152 return false; 153 } 154 Physical p1 = (Physical) a1; 155 Physical p2 = (Physical) a2; 156 157 // If both addresses specify a model, fallback to a basic match check (which 158 // also checks the port). 159 if (p1.getModel() != null && p2.getModel() != null) { 160 return p1.equals(p2); 161 } 162 return p1.getPort() == p2.getPort(); 163 } 164 Physical(long physicalDisplayId)165 private Physical(long physicalDisplayId) { 166 mPhysicalDisplayId = physicalDisplayId; 167 } 168 Physical(int port, Long model)169 private Physical(int port, Long model) { 170 if (port < 0 || port > 255) { 171 throw new IllegalArgumentException("The port should be in the interval [0, 255]"); 172 } 173 mPhysicalDisplayId = Integer.toUnsignedLong(port) 174 | (model == null ? UNKNOWN_MODEL : (model << MODEL_SHIFT)); 175 } 176 177 public static final @NonNull Parcelable.Creator<Physical> CREATOR = 178 new Parcelable.Creator<Physical>() { 179 @Override 180 public Physical createFromParcel(Parcel in) { 181 return new Physical(in.readLong()); 182 } 183 184 @Override 185 public Physical[] newArray(int size) { 186 return new Physical[size]; 187 } 188 }; 189 } 190 191 /** 192 * Address for a network-connected display. 193 */ 194 public static final class Network extends DisplayAddress { 195 private final String mMacAddress; 196 197 @Override equals(@ullable Object other)198 public boolean equals(@Nullable Object other) { 199 return other instanceof Network && mMacAddress.equals(((Network) other).mMacAddress); 200 } 201 202 @Override toString()203 public String toString() { 204 return mMacAddress; 205 } 206 207 @Override hashCode()208 public int hashCode() { 209 return mMacAddress.hashCode(); 210 } 211 212 @Override writeToParcel(Parcel out, int flags)213 public void writeToParcel(Parcel out, int flags) { 214 out.writeString(mMacAddress); 215 } 216 Network(String macAddress)217 private Network(String macAddress) { 218 mMacAddress = macAddress; 219 } 220 221 public static final @NonNull Parcelable.Creator<Network> CREATOR = 222 new Parcelable.Creator<Network>() { 223 @Override 224 public Network createFromParcel(Parcel in) { 225 return new Network(in.readString()); 226 } 227 228 @Override 229 public Network[] newArray(int size) { 230 return new Network[size]; 231 } 232 }; 233 } 234 235 @Override describeContents()236 public int describeContents() { 237 return 0; 238 } 239 } 240