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.hardware.hdmi;
18 
19 import android.annotation.IntDef;
20 
21 import java.lang.annotation.Retention;
22 import java.lang.annotation.RetentionPolicy;
23 
24 /**
25  * Various utilities related to HDMI CEC.
26  *
27  * @hide
28  */
29 public final class HdmiUtils {
30     /**
31      * Return value of {@link #getLocalPortFromPhysicalAddress(int, int)}
32      */
33     static final int TARGET_NOT_UNDER_LOCAL_DEVICE = -1;
34     static final int TARGET_SAME_PHYSICAL_ADDRESS = 0;
35 
HdmiUtils()36     private HdmiUtils() { /* cannot be instantiated */ }
37 
38     /**
39      * Method to parse target physical address to the port number on the current device.
40      *
41      * <p>This check assumes target address is valid.
42      *
43      * @param targetPhysicalAddress is the physical address of the target device
44      * @param myPhysicalAddress is the physical address of the current device
45      * @return
46      * If the target device is under the current device, return the port number of current device
47      * that the target device is connected to. This also applies to the devices that are indirectly
48      * connected to the current device.
49      *
50      * <p>If the target device has the same physical address as the current device, return
51      * {@link #TARGET_SAME_PHYSICAL_ADDRESS}.
52      *
53      * <p>If the target device is not under the current device, return
54      * {@link #TARGET_NOT_UNDER_LOCAL_DEVICE}.
55      */
getLocalPortFromPhysicalAddress( int targetPhysicalAddress, int myPhysicalAddress)56     public static int getLocalPortFromPhysicalAddress(
57             int targetPhysicalAddress, int myPhysicalAddress) {
58         if (myPhysicalAddress == targetPhysicalAddress) {
59             return TARGET_SAME_PHYSICAL_ADDRESS;
60         }
61 
62         int mask = 0xF000;
63         int finalMask = 0xF000;
64         int maskedAddress = myPhysicalAddress;
65 
66         while (maskedAddress != 0) {
67             maskedAddress = myPhysicalAddress & mask;
68             finalMask |= mask;
69             mask >>= 4;
70         }
71 
72         int portAddress = targetPhysicalAddress & finalMask;
73         if ((portAddress & (finalMask << 4)) != myPhysicalAddress) {
74             return TARGET_NOT_UNDER_LOCAL_DEVICE;
75         }
76 
77         mask <<= 4;
78         int port = portAddress & mask;
79         while ((port >> 4) != 0) {
80             port >>= 4;
81         }
82         return port;
83     }
84 
85     /**
86      * @hide
87      */
88     @Retention(RetentionPolicy.SOURCE)
89     @IntDef({HDMI_RELATIVE_POSITION_UNKNOWN, HDMI_RELATIVE_POSITION_DIRECTLY_BELOW,
90             HDMI_RELATIVE_POSITION_BELOW, HDMI_RELATIVE_POSITION_SAME,
91             HDMI_RELATIVE_POSITION_DIRECTLY_ABOVE, HDMI_RELATIVE_POSITION_ABOVE,
92             HDMI_RELATIVE_POSITION_SIBLING, HDMI_RELATIVE_POSITION_DIFFERENT_BRANCH})
93     public @interface HdmiAddressRelativePosition {}
94     /**
95      * HDMI relative position is not determined.
96      *
97      * @hide
98      */
99     public static final int HDMI_RELATIVE_POSITION_UNKNOWN = 0;
100     /**
101      * HDMI relative position: directly blow the device.
102      *
103      * @hide
104      */
105     public static final int HDMI_RELATIVE_POSITION_DIRECTLY_BELOW = 1;
106     /**
107      * HDMI relative position: indirectly below the device.
108      *
109      * @hide
110      */
111     public static final int HDMI_RELATIVE_POSITION_BELOW = 2;
112     /**
113      * HDMI relative position: the same device.
114      *
115      * @hide
116      */
117     public static final int HDMI_RELATIVE_POSITION_SAME = 3;
118     /**
119      * HDMI relative position: directly above the device.
120      *
121      * @hide
122      */
123     public static final int HDMI_RELATIVE_POSITION_DIRECTLY_ABOVE = 4;
124     /**
125      * HDMI relative position: indirectly above the device.
126      *
127      * @hide
128      */
129     public static final int HDMI_RELATIVE_POSITION_ABOVE = 5;
130     /**
131      * HDMI relative position: directly below a same device.
132      *
133      * @hide
134      */
135     public static final int HDMI_RELATIVE_POSITION_SIBLING = 6;
136     /**
137      * HDMI relative position: different branch.
138      *
139      * @hide
140      */
141     public static final int HDMI_RELATIVE_POSITION_DIFFERENT_BRANCH = 7;
142 
143     private static final int NPOS = -1;
144 
145     /**
146      * Check if the given physical address is valid.
147      *
148      * @param address physical address
149      * @return {@code true} if the given address is valid
150      */
isValidPhysicalAddress(int address)151     public static boolean isValidPhysicalAddress(int address) {
152         if (address < 0 || address >= 0xFFFF) {
153             return false;
154         }
155         int mask = 0xF000;
156         boolean hasZero = false;
157         for (int i = 0; i < 4; i++) {
158             if ((address & mask) == 0) {
159                 hasZero = true;
160             } else if (hasZero) {
161                 // only 0s are valid after a 0.
162                 // e.g. 0x1012 is not valid.
163                 return false;
164             }
165             mask >>= 4;
166         }
167         return true;
168     }
169 
170 
171     /**
172      * Returns the relative position of two physical addresses.
173      */
174     @HdmiAddressRelativePosition
getHdmiAddressRelativePosition(int src, int dest)175     public static int getHdmiAddressRelativePosition(int src, int dest) {
176         if (src == 0xFFFF || dest == 0xFFFF) {
177             // address not assigned
178             return HDMI_RELATIVE_POSITION_UNKNOWN;
179         }
180         try {
181             int firstDiffPos = physicalAddressFirstDifferentDigitPos(src, dest);
182             if (firstDiffPos == NPOS) {
183                 return HDMI_RELATIVE_POSITION_SAME;
184             }
185             int mask = (0xF000 >> (firstDiffPos * 4));
186             int nextPos = firstDiffPos + 1;
187             if ((src & mask) == 0) {
188                 // src is above dest
189                 if (nextPos == 4) {
190                     // last digits are different
191                     return HDMI_RELATIVE_POSITION_DIRECTLY_ABOVE;
192                 }
193                 if (((0xF000 >> (nextPos * 4)) & dest) == 0) {
194                     // next digit is 0
195                     return HDMI_RELATIVE_POSITION_DIRECTLY_ABOVE;
196                 }
197                 return HDMI_RELATIVE_POSITION_ABOVE;
198             }
199 
200             if ((dest & mask) == 0) {
201                 // src is below dest
202                 if (nextPos == 4) {
203                     // last digits are different
204                     return HDMI_RELATIVE_POSITION_DIRECTLY_BELOW;
205                 }
206                 if (((0xF000 >> (nextPos * 4)) & src) == 0) {
207                     // next digit is 0
208                     return HDMI_RELATIVE_POSITION_DIRECTLY_BELOW;
209                 }
210                 return HDMI_RELATIVE_POSITION_BELOW;
211             }
212             if (nextPos == 4) {
213                 // last digits are different
214                 return HDMI_RELATIVE_POSITION_SIBLING;
215             }
216             if (((0xF000 >> (nextPos * 4)) & src) == 0 && ((0xF000 >> (nextPos * 4)) & dest) == 0) {
217                 return HDMI_RELATIVE_POSITION_SIBLING;
218             }
219             return HDMI_RELATIVE_POSITION_DIFFERENT_BRANCH;
220         } catch (IllegalArgumentException e) {
221             // invalid address
222             return HDMI_RELATIVE_POSITION_UNKNOWN;
223         }
224     }
225 
physicalAddressFirstDifferentDigitPos(int address1, int address2)226     private static int physicalAddressFirstDifferentDigitPos(int address1, int address2)
227             throws IllegalArgumentException {
228         if (!isValidPhysicalAddress(address1)) {
229             throw new IllegalArgumentException(address1 + " is not a valid address.");
230         }
231         if (!isValidPhysicalAddress(address2)) {
232             throw new IllegalArgumentException(address2 + " is not a valid address.");
233         }
234         int mask = 0xF000;
235         for (int i = 0; i < 4; i++) {
236             if ((address1 & mask) != (address2 & mask)) {
237                 return i;
238             }
239             mask = mask >> 4;
240         }
241         return NPOS;
242     }
243 }
244