Read compat config in CompatConfig class.

Use the auto generated parser, and test by feeding streams of XML.

Test: atest FrameworksServicesTests
Bug: 138222871
Change-Id: Id523d31e7b6d2def9371753ae34cba883cd62a54
Merged-In: Id523d31e7b6d2def9371753ae34cba883cd62a54
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 018945a..45584aa 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -19,6 +19,7 @@
         ":vold_aidl",
         ":gsiservice_aidl",
         ":mediaupdateservice_aidl",
+        ":platform-compat-config",
         "java/com/android/server/EventLogTags.logtags",
         "java/com/android/server/am/EventLogTags.logtags",
         "java/com/android/server/policy/EventLogTags.logtags",
diff --git a/services/core/java/com/android/server/compat/CompatChange.java b/services/core/java/com/android/server/compat/CompatChange.java
index 2a866f3..6f32bee 100644
--- a/services/core/java/com/android/server/compat/CompatChange.java
+++ b/services/core/java/com/android/server/compat/CompatChange.java
@@ -20,6 +20,8 @@
 import android.compat.annotation.EnabledAfter;
 import android.content.pm.ApplicationInfo;
 
+import com.android.server.compat.config.Change;
+
 import java.util.HashMap;
 import java.util.Map;
 
@@ -60,6 +62,16 @@
         mDisabled = disabled;
     }
 
+    /**
+     * @param change an object generated by services/core/xsd/platform-compat-config.xsd
+     */
+    public CompatChange(Change change) {
+        mChangeId = change.getId();
+        mName = change.getName();
+        mEnableAfterTargetSdk = change.getEnableAfterTargetSdk();
+        mDisabled = change.getDisabled();
+    }
+
     long getId() {
         return mChangeId;
     }
diff --git a/services/core/java/com/android/server/compat/CompatConfig.java b/services/core/java/com/android/server/compat/CompatConfig.java
index c59b065..044e417 100644
--- a/services/core/java/com/android/server/compat/CompatConfig.java
+++ b/services/core/java/com/android/server/compat/CompatConfig.java
@@ -17,14 +17,27 @@
 package com.android.server.compat;
 
 import android.content.pm.ApplicationInfo;
+import android.os.Environment;
 import android.text.TextUtils;
 import android.util.LongArray;
 import android.util.LongSparseArray;
+import android.util.Slog;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.compat.config.Change;
+import com.android.server.compat.config.XmlParser;
 
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
 import java.io.PrintWriter;
+
+import javax.xml.datatype.DatatypeConfigurationException;
 /**
  * This class maintains state relating to platform compatibility changes.
  *
@@ -33,7 +46,12 @@
  */
 public final class CompatConfig {
 
-    private static final CompatConfig sInstance = new CompatConfig();
+    private static final String TAG = "CompatConfig";
+    private static final String CONFIG_FILE_SUFFIX = "platform_compat_config.xml";
+
+    private static final CompatConfig sInstance = new CompatConfig().initConfigFromLib(
+            Environment.buildPath(
+                    Environment.getRootDirectory(), "etc", "sysconfig"));
 
     @GuardedBy("mChanges")
     private final LongSparseArray<CompatChange> mChanges = new LongSparseArray<>();
@@ -188,4 +206,29 @@
         }
     }
 
+    CompatConfig initConfigFromLib(File libraryDir) {
+        if (!libraryDir.exists() || !libraryDir.isDirectory()) {
+            Slog.e(TAG, "No directory " + libraryDir + ", skipping");
+            return this;
+        }
+        for (File f : libraryDir.listFiles()) {
+            //TODO(b/138222363): Handle duplicate ids across config files.
+            if (f.getPath().endsWith(CONFIG_FILE_SUFFIX)) {
+                readConfig(f);
+            }
+        }
+        return this;
+    }
+
+    private void readConfig(File configFile) {
+        try (InputStream in = new BufferedInputStream(new FileInputStream(configFile))) {
+            for (Change change : XmlParser.read(in).getCompatChange()) {
+                Slog.w(TAG, "Adding: " + change.toString());
+                addChange(new CompatChange(change));
+            }
+        } catch (IOException | DatatypeConfigurationException | XmlPullParserException e) {
+            Slog.e(TAG, "Encountered an error while reading/parsing compat config file", e);
+        }
+    }
+
 }
diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp
index 153820d..214806e 100644
--- a/services/tests/servicestests/Android.bp
+++ b/services/tests/servicestests/Android.bp
@@ -38,6 +38,7 @@
         "ub-uiautomator",
         "platformprotosnano",
         "servicestests-utils",
+        "xml-writer-device-lib",
     ],
 
     aidl: {
diff --git a/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java b/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
index d008ca6..f3c5e99 100644
--- a/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
@@ -22,9 +22,17 @@
 
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.compat.annotation.Change;
+import com.android.compat.annotation.XmlWriter;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.UUID;
+
 @RunWith(AndroidJUnit4.class)
 public class CompatConfigTest {
 
@@ -35,6 +43,27 @@
         return ai;
     }
 
+    private File createTempDir() {
+        String base = System.getProperty("java.io.tmpdir");
+        File dir = new File(base, UUID.randomUUID().toString());
+        assertThat(dir.mkdirs()).isTrue();
+        return dir;
+    }
+
+    private void writeChangesToFile(Change[] changes, File f) {
+        XmlWriter writer = new XmlWriter();
+        for (Change change: changes) {
+            writer.addChange(change);
+        }
+        try {
+            f.createNewFile();
+            writer.write(new FileOutputStream(f));
+        } catch (IOException e) {
+            throw new RuntimeException(
+                    "Encountered an error while writing compat config file", e);
+        }
+    }
+
     @Test
     public void testUnknownChangeEnabled() {
         CompatConfig pc = new CompatConfig();
@@ -170,4 +199,45 @@
         sysApp.flags |= ApplicationInfo.FLAG_SYSTEM;
         assertThat(pc.isChangeEnabled(1234L, sysApp)).isTrue();
     }
+
+    @Test
+    public void testReadConfig() {
+        Change[] changes = {new Change(1234L, "MY_CHANGE1", false, 2), new Change(1235L,
+                "MY_CHANGE2", true, null), new Change(1236L, "MY_CHANGE3", false, null)};
+
+        File dir = createTempDir();
+        writeChangesToFile(changes, new File(dir.getPath() + "/platform_compat_config.xml"));
+
+        CompatConfig pc = new CompatConfig();
+        pc.initConfigFromLib(dir);
+
+        assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.some.package", 1))).isFalse();
+        assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.some.package", 3))).isTrue();
+        assertThat(pc.isChangeEnabled(1235L, makeAppInfo("com.some.package", 5))).isFalse();
+        assertThat(pc.isChangeEnabled(1236L, makeAppInfo("com.some.package", 1))).isTrue();
+    }
+
+    @Test
+    public void testReadConfigMultipleFiles() {
+        Change[] changes1 = {new Change(1234L, "MY_CHANGE1", false, 2)};
+        Change[] changes2 = {new Change(1235L, "MY_CHANGE2", true, null), new Change(1236L,
+                "MY_CHANGE3", false, null)};
+
+        File dir = createTempDir();
+        writeChangesToFile(changes1,
+                new File(dir.getPath() + "/libcore_platform_compat_config.xml"));
+        writeChangesToFile(changes2,
+                new File(dir.getPath() + "/frameworks_platform_compat_config.xml"));
+
+
+        CompatConfig pc = new CompatConfig();
+        pc.initConfigFromLib(dir);
+
+        assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.some.package", 1))).isFalse();
+        assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.some.package", 3))).isTrue();
+        assertThat(pc.isChangeEnabled(1235L, makeAppInfo("com.some.package", 5))).isFalse();
+        assertThat(pc.isChangeEnabled(1236L, makeAppInfo("com.some.package", 1))).isTrue();
+    }
 }
+
+