Add method to compute the relationship between two paths.

This is a helper function used to implement the initial set of Westworld
atoms.

Test: atest HdmiUtilsTest
Bug: 159796477

Change-Id: Ic21fa1a9b7bd93e0ae7e125601e532468fae7928
diff --git a/services/core/java/com/android/server/hdmi/Constants.java b/services/core/java/com/android/server/hdmi/Constants.java
index 713f800..425fbc5 100644
--- a/services/core/java/com/android/server/hdmi/Constants.java
+++ b/services/core/java/com/android/server/hdmi/Constants.java
@@ -329,6 +329,30 @@
     static final int INVALID_PHYSICAL_ADDRESS = HdmiDeviceInfo.PATH_INVALID;
     static final int PATH_INTERNAL = HdmiDeviceInfo.PATH_INTERNAL;
 
+    // The relationship from one path (physical address) to another.
+    @IntDef({
+            PATH_RELATIONSHIP_UNKNOWN,
+            PATH_RELATIONSHIP_DIFFERENT_BRANCH,
+            PATH_RELATIONSHIP_ANCESTOR,
+            PATH_RELATIONSHIP_DESCENDANT,
+            PATH_RELATIONSHIP_SIBLING,
+            PATH_RELATIONSHIP_SAME
+    })
+    @interface PathRelationship {}
+
+    // One or both of the paths is invalid
+    static final int PATH_RELATIONSHIP_UNKNOWN = 0;
+    // None of the relationships below holds
+    static final int PATH_RELATIONSHIP_DIFFERENT_BRANCH = 1;
+    // A path is either the TV, or between the TV and another path
+    static final int PATH_RELATIONSHIP_ANCESTOR = 2;
+    // A path is located somewhere below another path
+    static final int PATH_RELATIONSHIP_DESCENDANT = 3;
+    // A path has the same parent as another path
+    static final int PATH_RELATIONSHIP_SIBLING = 4;
+    // A path is equal to another path
+    static final int PATH_RELATIONSHIP_SAME = 5;
+
     // Strategy for device polling.
     // Should use "OR(|) operation of POLL_STRATEGY_XXX and POLL_ITERATION_XXX.
     static final int POLL_STRATEGY_MASK = 0x3; // first and second bit.
diff --git a/services/core/java/com/android/server/hdmi/HdmiUtils.java b/services/core/java/com/android/server/hdmi/HdmiUtils.java
index cd65db6..a6951ef 100644
--- a/services/core/java/com/android/server/hdmi/HdmiUtils.java
+++ b/services/core/java/com/android/server/hdmi/HdmiUtils.java
@@ -24,10 +24,11 @@
 
 import com.android.internal.util.HexDump;
 import com.android.internal.util.IndentingPrintWriter;
-
 import com.android.server.hdmi.Constants.AbortReason;
 import com.android.server.hdmi.Constants.AudioCodec;
 import com.android.server.hdmi.Constants.FeatureOpcode;
+import com.android.server.hdmi.Constants.PathRelationship;
+
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 
@@ -311,26 +312,43 @@
      * @return true if the new path in the active routing path
      */
     static boolean isInActiveRoutingPath(int activePath, int newPath) {
-        // Check each nibble of the currently active path and the new path till the position
-        // where the active nibble is not zero. For (activePath, newPath),
-        // (1.1.0.0, 1.0.0.0) -> true, new path is a parent
-        // (1.2.1.0, 1.2.1.2) -> true, new path is a descendant
-        // (1.1.0.0, 1.2.0.0) -> false, new path is a sibling
-        // (1.0.0.0, 2.0.0.0) -> false, in a completely different path
-        for (int i = 12; i >= 0; i -= 4) {
-            int nibbleActive = (activePath >> i) & 0xF;
-            if (nibbleActive == 0) {
-                break;
-            }
-            int nibbleNew = (newPath >> i) & 0xF;
-            if (nibbleNew == 0) {
-                break;
-            }
-            if (nibbleActive != nibbleNew) {
-                return false;
+        @PathRelationship int pathRelationship = pathRelationship(newPath, activePath);
+        return (pathRelationship == Constants.PATH_RELATIONSHIP_ANCESTOR
+                || pathRelationship == Constants.PATH_RELATIONSHIP_DESCENDANT
+                || pathRelationship == Constants.PATH_RELATIONSHIP_SAME);
+    }
+
+    /**
+     * Computes the relationship from the first path to the second path.
+     */
+    static @PathRelationship int pathRelationship(int firstPath, int secondPath) {
+        if (firstPath == Constants.INVALID_PHYSICAL_ADDRESS
+                || secondPath == Constants.INVALID_PHYSICAL_ADDRESS) {
+            return Constants.PATH_RELATIONSHIP_UNKNOWN;
+        }
+        // Loop forwards through both paths, looking for the first nibble where the paths differ.
+        // Checking this nibble and the next one distinguishes between most possible relationships.
+        for (int nibbleIndex = 0; nibbleIndex <= 3; nibbleIndex++) {
+            int shift = 12 - nibbleIndex * 4;
+            int firstPathNibble = (firstPath >> shift) & 0xF;
+            int secondPathNibble = (secondPath >> shift) & 0xF;
+            // Found the first nibble where the paths differ.
+            if (firstPathNibble != secondPathNibble) {
+                int firstPathNextNibble = (firstPath >> (shift - 4)) & 0xF;
+                int secondPathNextNibble = (secondPath >> (shift - 4)) & 0xF;
+                if (firstPathNibble == 0) {
+                    return Constants.PATH_RELATIONSHIP_ANCESTOR;
+                } else if (secondPathNibble == 0) {
+                    return Constants.PATH_RELATIONSHIP_DESCENDANT;
+                } else if (nibbleIndex == 3
+                        || (firstPathNextNibble == 0 && secondPathNextNibble == 0)) {
+                    return Constants.PATH_RELATIONSHIP_SIBLING;
+                } else {
+                    return Constants.PATH_RELATIONSHIP_DIFFERENT_BRANCH;
+                }
             }
         }
-        return true;
+        return Constants.PATH_RELATIONSHIP_SAME;
     }
 
     /**
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiUtilsTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiUtilsTest.java
index f72dbf6..d8f5c4c 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiUtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiUtilsTest.java
@@ -17,6 +17,9 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
 import android.platform.test.annotations.Presubmit;
 import android.util.Slog;
 
@@ -145,4 +148,91 @@
 
         assertThat(config).isEqualTo(expectedConfig);
     }
+
+    @Test
+    public void isAffectingActiveRoutingPath() {
+        // New path alters the parent
+        assertTrue(HdmiUtils.isAffectingActiveRoutingPath(0x1100, 0x2000));
+        // New path is a sibling
+        assertTrue(HdmiUtils.isAffectingActiveRoutingPath(0x1100, 0x1200));
+        // New path is the descendant of a sibling
+        assertFalse(HdmiUtils.isAffectingActiveRoutingPath(0x1100, 0x1210));
+        // In a completely different path
+        assertFalse(HdmiUtils.isAffectingActiveRoutingPath(0x1000, 0x3200));
+    }
+
+    @Test
+    public void isInActiveRoutingPath() {
+        // New path is a parent
+        assertTrue(HdmiUtils.isInActiveRoutingPath(0x1100, 0x1000));
+        // New path is a descendant
+        assertTrue(HdmiUtils.isInActiveRoutingPath(0x1210, 0x1212));
+        // New path is a sibling
+        assertFalse(HdmiUtils.isInActiveRoutingPath(0x1100, 0x1200));
+        // In a completely different path
+        assertFalse(HdmiUtils.isInActiveRoutingPath(0x1000, 0x2000));
+    }
+
+    @Test
+    public void pathRelationship_unknown() {
+        assertThat(HdmiUtils.pathRelationship(0x1234, Constants.INVALID_PHYSICAL_ADDRESS))
+                .isEqualTo(Constants.PATH_RELATIONSHIP_UNKNOWN);
+        assertThat(HdmiUtils.pathRelationship(Constants.INVALID_PHYSICAL_ADDRESS, 0x1234))
+                .isEqualTo(Constants.PATH_RELATIONSHIP_UNKNOWN);
+        assertThat(HdmiUtils.pathRelationship(Constants.INVALID_PHYSICAL_ADDRESS,
+                Constants.INVALID_PHYSICAL_ADDRESS))
+                .isEqualTo(Constants.PATH_RELATIONSHIP_UNKNOWN);
+    }
+
+    @Test
+    public void pathRelationship_differentBranch() {
+        assertThat(HdmiUtils.pathRelationship(0x1200, 0x2000))
+                .isEqualTo(Constants.PATH_RELATIONSHIP_DIFFERENT_BRANCH);
+        assertThat(HdmiUtils.pathRelationship(0x1234, 0x1224))
+                .isEqualTo(Constants.PATH_RELATIONSHIP_DIFFERENT_BRANCH);
+        assertThat(HdmiUtils.pathRelationship(0x1234, 0x1134))
+                .isEqualTo(Constants.PATH_RELATIONSHIP_DIFFERENT_BRANCH);
+        assertThat(HdmiUtils.pathRelationship(0x1234, 0x2234))
+                .isEqualTo(Constants.PATH_RELATIONSHIP_DIFFERENT_BRANCH);
+    }
+
+    @Test
+    public void pathRelationship_ancestor() {
+        assertThat(HdmiUtils.pathRelationship(0x0000, 0x1230))
+                .isEqualTo(Constants.PATH_RELATIONSHIP_ANCESTOR);
+        assertThat(HdmiUtils.pathRelationship(0x1000, 0x1230))
+                .isEqualTo(Constants.PATH_RELATIONSHIP_ANCESTOR);
+        assertThat(HdmiUtils.pathRelationship(0x1200, 0x1230))
+                .isEqualTo(Constants.PATH_RELATIONSHIP_ANCESTOR);
+    }
+
+    @Test
+    public void pathRelationship_descendant() {
+        assertThat(HdmiUtils.pathRelationship(0x1230, 0x0000))
+                .isEqualTo(Constants.PATH_RELATIONSHIP_DESCENDANT);
+        assertThat(HdmiUtils.pathRelationship(0x1230, 0x1000))
+                .isEqualTo(Constants.PATH_RELATIONSHIP_DESCENDANT);
+        assertThat(HdmiUtils.pathRelationship(0x1230, 0x1200))
+                .isEqualTo(Constants.PATH_RELATIONSHIP_DESCENDANT);
+    }
+
+    @Test
+    public void pathRelationship_sibling() {
+        assertThat(HdmiUtils.pathRelationship(0x1000, 0x2000))
+                .isEqualTo(Constants.PATH_RELATIONSHIP_SIBLING);
+        assertThat(HdmiUtils.pathRelationship(0x1200, 0x1100))
+                .isEqualTo(Constants.PATH_RELATIONSHIP_SIBLING);
+        assertThat(HdmiUtils.pathRelationship(0x1230, 0x1220))
+                .isEqualTo(Constants.PATH_RELATIONSHIP_SIBLING);
+        assertThat(HdmiUtils.pathRelationship(0x1234, 0x1233))
+                .isEqualTo(Constants.PATH_RELATIONSHIP_SIBLING);
+    }
+
+    @Test
+    public void pathRelationship_same() {
+        assertThat(HdmiUtils.pathRelationship(0x0000, 0x0000))
+                .isEqualTo(Constants.PATH_RELATIONSHIP_SAME);
+        assertThat(HdmiUtils.pathRelationship(0x1234, 0x1234))
+                .isEqualTo(Constants.PATH_RELATIONSHIP_SAME);
+    }
 }