Merge "[framework] Handle more possible route string from service." into main
diff --git a/apex/blobstore/OWNERS b/apex/blobstore/OWNERS
index 676cbc7..f820883 100644
--- a/apex/blobstore/OWNERS
+++ b/apex/blobstore/OWNERS
@@ -1,4 +1,4 @@
-# Bug component: 25692
+# Bug component: 1628187
 set noparent
 
 sudheersai@google.com
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 5956e2b..7a95532 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -129,6 +129,7 @@
 import com.android.internal.annotations.Immutable;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.os.SomeArgs;
+import com.android.internal.pm.RoSystemFeatures;
 import com.android.internal.util.UserIcons;
 
 import dalvik.system.VMRuntime;
@@ -818,6 +819,16 @@
                 @Override
                 public Boolean recompute(HasSystemFeatureQuery query) {
                     try {
+                        // As an optimization, check first to see if the feature was defined at
+                        // compile-time as either available or unavailable.
+                        // TODO(b/203143243): Consider hoisting this optimization out of the cache
+                        // after the trunk stable (build) flag has soaked and more features are
+                        // defined at compile-time.
+                        Boolean maybeHasSystemFeature =
+                                RoSystemFeatures.maybeHasFeature(query.name, query.version);
+                        if (maybeHasSystemFeature != null) {
+                            return maybeHasSystemFeature.booleanValue();
+                        }
                         return ActivityThread.currentActivityThread().getPackageManager().
                             hasSystemFeature(query.name, query.version);
                     } catch (RemoteException e) {
diff --git a/core/java/android/appwidget/OWNERS b/core/java/android/appwidget/OWNERS
index 1910833..0e85d5b 100644
--- a/core/java/android/appwidget/OWNERS
+++ b/core/java/android/appwidget/OWNERS
@@ -3,3 +3,5 @@
 pinyaoting@google.com
 suprabh@google.com
 sunnygoyal@google.com
+zakcohen@google.com
+shamalip@google.com
diff --git a/core/java/com/android/internal/widget/floatingtoolbar/OWNERS b/core/java/com/android/internal/widget/floatingtoolbar/OWNERS
index ed9425c..999ea0e 100644
--- a/core/java/com/android/internal/widget/floatingtoolbar/OWNERS
+++ b/core/java/com/android/internal/widget/floatingtoolbar/OWNERS
@@ -1 +1 @@
-include /core/java/android/view/selectiontoolbar/OWNERS
+include /core/java/android/permission/OWNERS
diff --git a/core/res/res/xml/sms_short_codes.xml b/core/res/res/xml/sms_short_codes.xml
index 581dee5..bb5380e 100644
--- a/core/res/res/xml/sms_short_codes.xml
+++ b/core/res/res/xml/sms_short_codes.xml
@@ -34,7 +34,7 @@
          http://smscoin.net/software/engine/WordPress/Paid+SMS-registration/ -->
 
     <!-- Arab Emirates -->
-    <shortcode country="ae" pattern="\\d{1,5}" free="1017|1355|3214|6253" />
+    <shortcode country="ae" pattern="\\d{1,5}" free="1017|1355|3214|6253|6568" />
 
     <!-- Albania: 5 digits, known short codes listed -->
     <shortcode country="al" pattern="\\d{5}" premium="15191|55[56]00" />
@@ -70,7 +70,7 @@
     <shortcode country="bh" pattern="\\d{1,5}" free="81181|85999" />
 
     <!-- Brazil: 1-5 digits (standard system default, not country specific) -->
-    <shortcode country="br" pattern="\\d{1,5}" free="6000[012]\\d|876|5500|9963|4141|8000|2652" />
+    <shortcode country="br" pattern="\\d{1,5}" free="6000[012]\\d|876|5500|9963|4141|8000|2652|26808" />
 
     <!-- Botswana: 1-5 digits (standard system default, not country specific) -->
     <shortcode country="bw" pattern="\\d{1,5}" free="16641" />
@@ -79,7 +79,7 @@
     <shortcode country="by" pattern="\\d{4}" premium="3336|4161|444[4689]|501[34]|7781" />
 
     <!-- Canada: 5-6 digits -->
-    <shortcode country="ca" pattern="\\d{5,6}" premium="60999|88188|43030" standard="244444" free="455677" />
+    <shortcode country="ca" pattern="\\d{5,6}" premium="60999|88188|43030" standard="244444" free="455677|24470" />
 
     <!-- Switzerland: 3-5 digits: http://www.swisscom.ch/fxres/kmu/thirdpartybusiness_code_of_conduct_en.pdf -->
     <shortcode country="ch" pattern="[2-9]\\d{2,4}" premium="543|83111|30118" free="98765|30075|30047" />
@@ -123,8 +123,8 @@
          http://www.tja.ee/public/documents/Elektrooniline_side/Oigusaktid/ENG/Estonian_Numbering_Plan_annex_06_09_2010.mht -->
     <shortcode country="ee" pattern="1\\d{2,4}" premium="90\\d{5}|15330|1701[0-3]" free="116\\d{3}|95034" />
 
-    <!-- Egypt: 4 digits, known codes listed -->
-    <shortcode country="eg" pattern="\\d{4}" free="1499" />
+    <!-- Egypt: 4-5 digits, known codes listed -->
+    <shortcode country="eg" pattern="\\d{4,5}" free="1499|10020" />
 
     <!-- Spain: 5-6 digits: 25xxx, 27xxx, 280xx, 35xxx, 37xxx, 795xxx, 797xxx, 995xxx, 997xxx, plus EU.
          http://www.legallink.es/?q=en/content/which-current-regulatory-status-premium-rate-services-spain -->
@@ -147,7 +147,7 @@
     <shortcode country="ge" pattern="\\d{1,5}" premium="801[234]|888[239]" free="95201|95202|95203" />
 
     <!-- Ghana: 4 digits, known premium codes listed -->
-    <shortcode country="gh" pattern="\\d{4}" free="5041|3777|2333" />
+    <shortcode country="gh" pattern="\\d{4}" free="5041|3777|2333|6061" />
 
     <!-- Greece: 5 digits (54xxx, 19yxx, x=0-9, y=0-5): http://www.cmtelecom.com/premium-sms/greece -->
     <shortcode country="gr" pattern="\\d{5}" premium="54\\d{3}|19[0-5]\\d{2}" free="116\\d{3}|12115" />
@@ -169,7 +169,7 @@
     <shortcode country="in" pattern="\\d{1,5}" free="59336|53969" />
 
     <!-- Indonesia: 1-5 digits (standard system default, not country specific) -->
-    <shortcode country="id" pattern="\\d{1,5}" free="99477|6006|46645|363|93457|99265" />
+    <shortcode country="id" pattern="\\d{1,5}" free="99477|6006|46645|363|93457|99265|77413" />
 
     <!-- Ireland: 5 digits, 5xxxx (50xxx=free, 5[12]xxx=standard), plus EU:
          http://www.comreg.ie/_fileupload/publications/ComReg1117.pdf -->
@@ -226,13 +226,13 @@
     <shortcode country="mn" pattern="\\d{1,6}" free="44444|45678|445566" />
 
     <!-- Malawi: 1-5 digits (standard system default, not country specific) -->
-    <shortcode country="mw" pattern="\\d{1,5}" free="4276" />
+    <shortcode country="mw" pattern="\\d{1,5}" free="4276|4305" />
 
     <!-- Mozambique: 1-5 digits (standard system default, not country specific) -->
     <shortcode country="mz" pattern="\\d{1,5}" free="1714" />
 
     <!-- Mexico: 4-7 digits (not confirmed), known premium codes listed -->
-    <shortcode country="mx" pattern="\\d{4,7}" premium="53035|7766" free="26259|46645|50025|50052|5050|76551|88778|9963|91101|45453|550346|3030303|81811" />
+    <shortcode country="mx" pattern="\\d{4,7}" premium="53035|7766" free="26259|46645|50025|50052|5050|76551|88778|9963|91101|45453|550346|3030303|81811|81818" />
 
     <!-- Malaysia: 5 digits: http://www.skmm.gov.my/attachment/Consumer_Regulation/Mobile_Content_Services_FAQs.pdf -->
     <shortcode country="my" pattern="\\d{5}" premium="32298|33776" free="22099|28288|66668|66966" />
@@ -324,7 +324,7 @@
     <shortcode country="tj" pattern="\\d{4}" premium="11[3-7]1|4161|4333|444[689]" />
 
     <!-- Tanzania: 1-5 digits (standard system default, not country specific) -->
-    <shortcode country="tz" pattern="\\d{1,5}" free="15046|15234|15324" />
+    <shortcode country="tz" pattern="\\d{1,5}" free="15046|15234|15324|15610" />
 
     <!-- Tunisia: 5 digits, known premium codes listed -->
     <shortcode country="tn" pattern="\\d{5}" free="85799" />
@@ -336,11 +336,11 @@
     <shortcode country="ua" pattern="\\d{4}" premium="444[3-9]|70[579]4|7540" />
 
     <!-- Uganda(UG): 4 digits (standard system default, not country specific) -->
-    <shortcode country="ug" pattern="\\d{4}" free="8000" />
+    <shortcode country="ug" pattern="\\d{4}" free="8000|8009" />
 
     <!-- USA: 5-6 digits (premium codes from https://www.premiumsmsrefunds.com/ShortCodes.htm),
          visual voicemail code for T-Mobile: 122 -->
-    <shortcode country="us" pattern="\\d{5,6}" premium="20433|21(?:344|472)|22715|23(?:333|847)|24(?:15|28)0|25209|27(?:449|606|663)|28498|305(?:00|83)|32(?:340|941)|33(?:166|786|849)|34746|35(?:182|564)|37975|38(?:135|146|254)|41(?:366|463)|42335|43(?:355|500)|44(?:578|711|811)|45814|46(?:157|173|327)|46666|47553|48(?:221|277|669)|50(?:844|920)|51(?:062|368)|52944|54(?:723|892)|55928|56483|57370|59(?:182|187|252|342)|60339|61(?:266|982)|62478|64(?:219|898)|65(?:108|500)|69(?:208|388)|70877|71851|72(?:078|087|465)|73(?:288|588|882|909|997)|74(?:034|332|815)|76426|79213|81946|83177|84(?:103|685)|85797|86(?:234|236|666)|89616|90(?:715|842|938)|91(?:362|958)|94719|95297|96(?:040|666|835|969)|97(?:142|294|688)|99(?:689|796|807)" standard="44567|244444" free="122|87902|21696|24614|28003|30356|33669|40196|41064|41270|43753|44034|46645|52413|56139|57969|61785|66975|75136|76227|81398|83952|85140|86566|86799|95737|96684|99245|611611|96831" />
+    <shortcode country="us" pattern="\\d{5,6}" premium="20433|21(?:344|472)|22715|23(?:333|847)|24(?:15|28)0|25209|27(?:449|606|663)|28498|305(?:00|83)|32(?:340|941)|33(?:166|786|849)|34746|35(?:182|564)|37975|38(?:135|146|254)|41(?:366|463)|42335|43(?:355|500)|44(?:578|711|811)|45814|46(?:157|173|327)|46666|47553|48(?:221|277|669)|50(?:844|920)|51(?:062|368)|52944|54(?:723|892)|55928|56483|57370|59(?:182|187|252|342)|60339|61(?:266|982)|62478|64(?:219|898)|65(?:108|500)|69(?:208|388)|70877|71851|72(?:078|087|465)|73(?:288|588|882|909|997)|74(?:034|332|815)|76426|79213|81946|83177|84(?:103|685)|85797|86(?:234|236|666)|89616|90(?:715|842|938)|91(?:362|958)|94719|95297|96(?:040|666|835|969)|97(?:142|294|688)|99(?:689|796|807)" standard="44567|244444" free="122|87902|21696|24614|28003|30356|33669|40196|41064|41270|43753|44034|46645|52413|56139|57969|61785|66975|75136|76227|81398|83952|85140|86566|86799|95737|96684|99245|611611|96831|10907" />
 
     <!--Uruguay : 1-6 digits (standard system default, not country specific) -->
     <shortcode country="uy" pattern="\\d{1,6}" free="55002|191289" />
diff --git a/core/tests/coretests/OWNERS b/core/tests/coretests/OWNERS
index b669e3b..6d27f31 100644
--- a/core/tests/coretests/OWNERS
+++ b/core/tests/coretests/OWNERS
@@ -4,3 +4,4 @@
 per-file ParcelTest.java = file:platform/frameworks/native:/libs/binder/OWNERS
 per-file SurfaceControlRegistryTests.java = file:/services/core/java/com/android/server/wm/OWNERS
 per-file VintfObjectTest.java = file:platform/system/libvintf:/OWNERS
+per-file WallpaperDescriptionTest,WallpaperInstanceTest = file:/core/java/android/service/wallpaper/OWNERS
diff --git a/keystore/java/android/security/OWNERS b/keystore/java/android/security/OWNERS
index 32759b2..4305286 100644
--- a/keystore/java/android/security/OWNERS
+++ b/keystore/java/android/security/OWNERS
@@ -1,2 +1,2 @@
-per-file *.java,*.aidl = eranm@google.com,pgrafov@google.com,rubinxu@google.com
+per-file *.java,*.aidl = drysdale@google.com,jbires@google.com,pgrafov@google.com,rubinxu@google.com
 per-file KeyStoreManager.java = mpgroover@google.com
diff --git a/keystore/tests/OWNERS b/keystore/tests/OWNERS
index 86c31f4..0f94ddc 100644
--- a/keystore/tests/OWNERS
+++ b/keystore/tests/OWNERS
@@ -1,4 +1,7 @@
+# Android HW Trust team
+drysdale@google.com
+jbires@google.com
+
 # Android Enterprise security team
-eranm@google.com
 pgrafov@google.com
 rubinxu@google.com
diff --git a/omapi/aidl/vts/functional/AccessControlApp/Android.bp b/omapi/aidl/vts/functional/AccessControlApp/Android.bp
index f03c3f6..57d75f5 100644
--- a/omapi/aidl/vts/functional/AccessControlApp/Android.bp
+++ b/omapi/aidl/vts/functional/AccessControlApp/Android.bp
@@ -15,6 +15,7 @@
 //
 
 package {
+    default_team: "trendy_team_fwk_nfc",
     default_applicable_licenses: ["Android-Apache-2.0"],
 }
 
diff --git a/omapi/aidl/vts/functional/omapi/Android.bp b/omapi/aidl/vts/functional/omapi/Android.bp
index c41479f..8ee55ff 100644
--- a/omapi/aidl/vts/functional/omapi/Android.bp
+++ b/omapi/aidl/vts/functional/omapi/Android.bp
@@ -15,6 +15,7 @@
 //
 
 package {
+    default_team: "trendy_team_fwk_nfc",
     default_applicable_licenses: ["Android-Apache-2.0"],
 }
 
diff --git a/ravenwood/Android.bp b/ravenwood/Android.bp
index d918201..090ec65 100644
--- a/ravenwood/Android.bp
+++ b/ravenwood/Android.bp
@@ -100,6 +100,9 @@
     srcs: [
         "runtime-helper-src/libcore-fake/**/*.java",
     ],
+    libs: [
+        "app-compat-annotations",
+    ],
     static_libs: [
         "ravenwood-runtime-common",
     ],
@@ -121,6 +124,7 @@
     ],
     static_libs: [
         "ravenwood-runtime-common",
+        "androidx.annotation_annotation",
     ],
     libs: [
         "framework-minus-apex.ravenwood",
@@ -315,6 +319,16 @@
         ":services.core.ravenwood-base{hoststubgen_services.core_apis.csv}",
         ":services.core.ravenwood-base{hoststubgen_services.core_keep_all.txt}",
         ":services.core.ravenwood-base{hoststubgen_services.core_dump.txt}",
+
+        ":framework-configinfrastructure.ravenwood-base{framework-configinfrastructure_stats.csv}",
+        ":framework-configinfrastructure.ravenwood-base{framework-configinfrastructure_apis.csv}",
+        ":framework-configinfrastructure.ravenwood-base{framework-configinfrastructure_keep_all.txt}",
+        ":framework-configinfrastructure.ravenwood-base{framework-configinfrastructure_dump.txt}",
+
+        ":framework-statsd.ravenwood-base{framework-statsd_stats.csv}",
+        ":framework-statsd.ravenwood-base{framework-statsd_apis.csv}",
+        ":framework-statsd.ravenwood-base{framework-statsd_keep_all.txt}",
+        ":framework-statsd.ravenwood-base{framework-statsd_dump.txt}",
     ],
 }
 
@@ -399,6 +413,9 @@
         // DeviceConfig
         "framework-configinfrastructure.ravenwood",
 
+        // StatsD
+        "framework-statsd.ravenwood",
+
         // Provide runtime versions of utils linked in below
         "junit",
         "truth",
diff --git a/ravenwood/Framework.bp b/ravenwood/Framework.bp
index 1bea434..d207738 100644
--- a/ravenwood/Framework.bp
+++ b/ravenwood/Framework.bp
@@ -344,3 +344,57 @@
         "framework-configinfrastructure.ravenwood.jar",
     ],
 }
+
+///////////////////////////////////
+// framework-statsd
+///////////////////////////////////
+
+java_genrule {
+    name: "framework-statsd.ravenwood-base",
+    tools: ["hoststubgen"],
+    cmd: "$(location hoststubgen) " +
+        "@$(location :ravenwood-standard-options) " +
+
+        "--debug-log $(location framework-statsd.log) " +
+        "--stats-file $(location framework-statsd_stats.csv) " +
+        "--supported-api-list-file $(location framework-statsd_apis.csv) " +
+        "--gen-keep-all-file $(location framework-statsd_keep_all.txt) " +
+        "--gen-input-dump-file $(location framework-statsd_dump.txt) " +
+
+        "--out-impl-jar $(location ravenwood.jar) " +
+        "--in-jar $(location :framework-statsd.impl{.jar}) " +
+
+        "--policy-override-file $(location :ravenwood-common-policies) " +
+        "--policy-override-file $(location :framework-statsd-ravenwood-policies) ",
+    srcs: [
+        ":framework-statsd.impl{.jar}",
+
+        ":ravenwood-common-policies",
+        ":framework-statsd-ravenwood-policies",
+        ":ravenwood-standard-options",
+    ],
+    out: [
+        "ravenwood.jar",
+
+        // Following files are created just as FYI.
+        "framework-statsd_keep_all.txt",
+        "framework-statsd_dump.txt",
+
+        "framework-statsd.log",
+        "framework-statsd_stats.csv",
+        "framework-statsd_apis.csv",
+    ],
+    visibility: ["//visibility:private"],
+}
+
+java_genrule {
+    name: "framework-statsd.ravenwood",
+    defaults: ["ravenwood-internal-only-visibility-genrule"],
+    cmd: "cp $(in) $(out)",
+    srcs: [
+        ":framework-statsd.ravenwood-base{ravenwood.jar}",
+    ],
+    out: [
+        "framework-statsd.ravenwood.jar",
+    ],
+}
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodConfigState.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodConfigState.java
index 3535cb2..870a10a 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodConfigState.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodConfigState.java
@@ -42,6 +42,10 @@
 
     private final RavenwoodConfig mConfig;
 
+    // TODO: Move the other contexts from RavenwoodConfig to here too? They're used by
+    // RavenwoodRule too, but RavenwoodRule can probably use InstrumentationRegistry?
+    RavenwoodContext mSystemServerContext;
+
     public RavenwoodConfigState(RavenwoodConfig config) {
         mConfig = config;
     }
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java
index 9a145cb..c2806da 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java
@@ -16,6 +16,8 @@
 
 package android.platform.test.ravenwood;
 
+import static android.platform.test.ravenwood.RavenwoodSystemServer.ANDROID_PACKAGE_NAME;
+
 import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_INST_RESOURCE_APK;
 import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_RESOURCE_APK;
 import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_VERBOSE_LOGGING;
@@ -267,6 +269,13 @@
         config.mInstContext = instContext;
         config.mTargetContext = targetContext;
 
+        final Supplier<Resources> systemResourcesLoader = () -> {
+            return config.mState.loadResources(null);
+        };
+
+        config.mState.mSystemServerContext =
+                new RavenwoodContext(ANDROID_PACKAGE_NAME, main, systemResourcesLoader);
+
         // Prepare other fields.
         config.mInstrumentation = new Instrumentation();
         config.mInstrumentation.basicInit(instContext, targetContext, createMockUiAutomation());
@@ -314,6 +323,9 @@
             ((RavenwoodContext) config.mTargetContext).cleanUp();
             config.mTargetContext = null;
         }
+        if (config.mState.mSystemServerContext != null) {
+            config.mState.mSystemServerContext.cleanUp();
+        }
 
         Looper.getMainLooper().quit();
         Looper.clearMainLooperForTest();
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodSystemServer.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodSystemServer.java
index 3946dd84..f198a08 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodSystemServer.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodSystemServer.java
@@ -33,6 +33,9 @@
 import java.util.Set;
 
 public class RavenwoodSystemServer {
+
+    static final String ANDROID_PACKAGE_NAME = "android";
+
     /**
      * Set of services that we know how to provide under Ravenwood. We keep this set distinct
      * from {@code com.android.server.SystemServer} to give us the ability to choose either
@@ -67,7 +70,7 @@
 
         sStartedServices = new ArraySet<>();
         sTimings = new TimingsTraceAndSlog();
-        sServiceManager = new SystemServiceManager(config.mInstContext);
+        sServiceManager = new SystemServiceManager(config.mState.mSystemServerContext);
         sServiceManager.setStartInfo(false,
                 SystemClock.elapsedRealtime(),
                 SystemClock.uptimeMillis());
diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodConfig.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodConfig.java
index 1f6e11d..37b0abc 100644
--- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodConfig.java
+++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodConfig.java
@@ -67,6 +67,7 @@
     String mTargetPackageName;
 
     int mMinSdkLevel;
+    int mTargetSdkLevel;
 
     boolean mProvideMainThread = false;
 
@@ -150,6 +151,14 @@
         }
 
         /**
+         * Configure the target SDK level of the test.
+         */
+        public Builder setTargetSdkLevel(int sdkLevel) {
+            mConfig.mTargetSdkLevel = sdkLevel;
+            return this;
+        }
+
+        /**
          * Configure a "main" thread to be available for the duration of the test, as defined
          * by {@code Looper.getMainLooper()}. Has no effect on non-Ravenwood environments.
          *
diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodSystemProperties.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodSystemProperties.java
index ced1519..9bc45be 100644
--- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodSystemProperties.java
+++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodSystemProperties.java
@@ -146,6 +146,9 @@
         if (root.startsWith("soc.")) return true;
         if (root.startsWith("system.")) return true;
 
+        // For PropertyInvalidatedCache
+        if (root.startsWith("cache_key.")) return true;
+
         switch (key) {
             case "gsm.version.baseband":
             case "no.such.thing":
@@ -170,6 +173,9 @@
 
         if (root.startsWith("debug.")) return true;
 
+        // For PropertyInvalidatedCache
+        if (root.startsWith("cache_key.")) return true;
+
         return mKeyWritable.contains(key);
     }
 
diff --git a/ravenwood/runtime-helper-src/libcore-fake/android/compat/Compatibility.java b/ravenwood/runtime-helper-src/libcore-fake/android/compat/Compatibility.java
new file mode 100644
index 0000000..c737684
--- /dev/null
+++ b/ravenwood/runtime-helper-src/libcore-fake/android/compat/Compatibility.java
@@ -0,0 +1,359 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.compat;
+
+// [Ravenwood] Copied from libcore, with "RAVENWOOD-CHANGE"
+
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+
+import android.annotation.SystemApi;
+import android.compat.annotation.ChangeId;
+
+import libcore.api.IntraCoreApi;
+import libcore.util.NonNull;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * Internal APIs for logging and gating compatibility changes.
+ *
+ * @see ChangeId
+ *
+ * @hide
+ */
+@SystemApi(client = MODULE_LIBRARIES)
+@IntraCoreApi
+public final class Compatibility {
+
+    private Compatibility() {}
+
+    /**
+     * Reports that a compatibility change is affecting the current process now.
+     *
+     * <p>Calls to this method from a non-app process are ignored. This allows code implementing
+     * APIs that are used by apps and by other code (e.g. the system server) to report changes
+     * regardless of the process it's running in. When called in a non-app process, this method is
+     * a no-op.
+     *
+     * <p>Note: for changes that are gated using {@link #isChangeEnabled(long)}, you do not need to
+     * call this API directly. The change will be reported for you in the case that
+     * {@link #isChangeEnabled(long)} returns {@code true}.
+     *
+     * @param changeId The ID of the compatibility change taking effect.
+     *
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    @IntraCoreApi
+    public static void reportUnconditionalChange(@ChangeId long changeId) {
+        sCallbacks.onChangeReported(changeId);
+    }
+
+    /**
+     * Query if a given compatibility change is enabled for the current process. This method should
+     * only be called by code running inside a process of the affected app.
+     *
+     * <p>If this method returns {@code true}, the calling code should implement the compatibility
+     * change, resulting in differing behaviour compared to earlier releases. If this method returns
+     * {@code false}, the calling code should behave as it did in earlier releases.
+     *
+     * <p>When this method returns {@code true}, it will also report the change as
+     * {@link #reportUnconditionalChange(long)} would, so there is no need to call that method
+     * directly.
+     *
+     * @param changeId The ID of the compatibility change in question.
+     * @return {@code true} if the change is enabled for the current app.
+     *
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    @IntraCoreApi
+    public static boolean isChangeEnabled(@ChangeId long changeId) {
+        return sCallbacks.isChangeEnabled(changeId);
+    }
+
+    private static final BehaviorChangeDelegate DEFAULT_CALLBACKS = new BehaviorChangeDelegate(){};
+
+    private volatile static BehaviorChangeDelegate sCallbacks = DEFAULT_CALLBACKS;
+
+    /**
+     * Sets the behavior change delegate.
+     *
+     * All changes reported via the {@link Compatibility} class will be forwarded to this class.
+     *
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    public static void setBehaviorChangeDelegate(BehaviorChangeDelegate callbacks) {
+        sCallbacks = Objects.requireNonNull(callbacks);
+    }
+
+    /**
+     * Removes a behavior change delegate previously set via {@link #setBehaviorChangeDelegate}.
+     *
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    public static void clearBehaviorChangeDelegate() {
+        sCallbacks = DEFAULT_CALLBACKS;
+    }
+
+    /**
+     * Return the behavior change delegate
+     *
+     * @hide
+     */
+    // VisibleForTesting
+    @NonNull
+    public static BehaviorChangeDelegate getBehaviorChangeDelegate() {
+        return sCallbacks;
+    }
+
+    /**
+     * For use by tests only. Causes values from {@code overrides} to be returned instead of the
+     * real value.
+     *
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    public static void setOverrides(ChangeConfig overrides) {
+        // Setting overrides twice in a row does not need to be supported because
+        // this method is only for enabling/disabling changes for the duration of
+        // a single test.
+        // In production, the app is restarted when changes get enabled or disabled,
+        // and the ChangeConfig is then set exactly once on that app process.
+        if (sCallbacks instanceof OverrideCallbacks) {
+            throw new IllegalStateException("setOverrides has already been called!");
+        }
+        sCallbacks = new OverrideCallbacks(sCallbacks, overrides);
+    }
+
+    /**
+     * For use by tests only. Removes overrides set by {@link #setOverrides}.
+     *
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    public static void clearOverrides() {
+        if (!(sCallbacks instanceof OverrideCallbacks)) {
+            throw new IllegalStateException("No overrides set");
+        }
+        sCallbacks = ((OverrideCallbacks) sCallbacks).delegate;
+    }
+
+    /**
+     * Base class for compatibility API implementations. The default implementation logs a warning
+     * to logcat.
+     *
+     * This is provided as a class rather than an interface to allow new methods to be added without
+     * breaking @SystemApi binary compatibility.
+     *
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    public interface BehaviorChangeDelegate {
+        /**
+         * Called when a change is reported via {@link Compatibility#reportUnconditionalChange}
+         *
+         * @hide
+         */
+        @SystemApi(client = MODULE_LIBRARIES)
+        default void onChangeReported(long changeId) {
+            // Do not use String.format here (b/160912695)
+
+            // RAVENWOOD-CHANGE
+            System.out.println("No Compatibility callbacks set! Reporting change " + changeId);
+        }
+
+        /**
+         * Called when a change is queried via {@link Compatibility#isChangeEnabled}
+         *
+         * @hide
+         */
+        @SystemApi(client = MODULE_LIBRARIES)
+        default boolean isChangeEnabled(long changeId) {
+            // Do not use String.format here (b/160912695)
+            // TODO(b/289900411): Rate limit this log if it's necessary in the release build.
+            // System.logW("No Compatibility callbacks set! Querying change " + changeId);
+            return true;
+        }
+    }
+
+    /**
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    @IntraCoreApi
+    public static final class ChangeConfig {
+        private final Set<Long> enabled;
+        private final Set<Long> disabled;
+
+        /**
+         * @hide
+         */
+        @SystemApi(client = MODULE_LIBRARIES)
+        @IntraCoreApi
+        public ChangeConfig(@NonNull Set<@NonNull Long> enabled, @NonNull Set<@NonNull Long> disabled) {
+            this.enabled = Objects.requireNonNull(enabled);
+            this.disabled = Objects.requireNonNull(disabled);
+            if (enabled.contains(null)) {
+                throw new NullPointerException();
+            }
+            if (disabled.contains(null)) {
+                throw new NullPointerException();
+            }
+            Set<Long> intersection = new HashSet<>(enabled);
+            intersection.retainAll(disabled);
+            if (!intersection.isEmpty()) {
+                throw new IllegalArgumentException("Cannot have changes " + intersection
+                        + " enabled and disabled!");
+            }
+        }
+
+        /**
+         * @hide
+         */
+        @SystemApi(client = MODULE_LIBRARIES)
+        @IntraCoreApi
+        public boolean isEmpty() {
+            return enabled.isEmpty() && disabled.isEmpty();
+        }
+
+        private static long[] toLongArray(Set<Long> values) {
+            long[] result = new long[values.size()];
+            int idx = 0;
+            for (Long value: values) {
+                result[idx++] = value;
+            }
+            return result;
+        }
+
+        /**
+         * @hide
+         */
+        @SystemApi(client = MODULE_LIBRARIES)
+        @IntraCoreApi
+        public @NonNull long[] getEnabledChangesArray() {
+            return toLongArray(enabled);
+        }
+
+
+        /**
+         * @hide
+         */
+        @SystemApi(client = MODULE_LIBRARIES)
+        @IntraCoreApi
+        public @NonNull long[] getDisabledChangesArray() {
+            return toLongArray(disabled);
+        }
+
+
+        /**
+         * @hide
+         */
+        @SystemApi(client = MODULE_LIBRARIES)
+        @IntraCoreApi
+        public @NonNull Set<@NonNull Long> getEnabledSet() {
+            return Collections.unmodifiableSet(enabled);
+        }
+
+
+        /**
+         * @hide
+         */
+        @SystemApi(client = MODULE_LIBRARIES)
+        @IntraCoreApi
+        public @NonNull Set<@NonNull Long> getDisabledSet() {
+            return Collections.unmodifiableSet(disabled);
+        }
+
+
+        /**
+         * @hide
+         */
+        @SystemApi(client = MODULE_LIBRARIES)
+        @IntraCoreApi
+        public boolean isForceEnabled(long changeId) {
+            return enabled.contains(changeId);
+        }
+
+
+        /**
+         * @hide
+         */
+        @SystemApi(client = MODULE_LIBRARIES)
+        @IntraCoreApi
+        public boolean isForceDisabled(long changeId) {
+            return disabled.contains(changeId);
+        }
+
+
+        /**
+         * @hide
+         */
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (!(o instanceof ChangeConfig)) {
+                return false;
+            }
+            ChangeConfig that = (ChangeConfig) o;
+            return enabled.equals(that.enabled) &&
+                    disabled.equals(that.disabled);
+        }
+
+        /**
+         * @hide
+         */
+        @Override
+        public int hashCode() {
+            return Objects.hash(enabled, disabled);
+        }
+
+
+        /**
+         * @hide
+         */
+        @Override
+        public String toString() {
+            return "ChangeConfig{enabled=" + enabled + ", disabled=" + disabled + '}';
+        }
+    }
+
+    private static class OverrideCallbacks implements BehaviorChangeDelegate {
+        private final BehaviorChangeDelegate delegate;
+        private final ChangeConfig changeConfig;
+
+        private OverrideCallbacks(BehaviorChangeDelegate delegate, ChangeConfig changeConfig) {
+            this.delegate = Objects.requireNonNull(delegate);
+            this.changeConfig = Objects.requireNonNull(changeConfig);
+        }
+        @Override
+        public boolean isChangeEnabled(long changeId) {
+           if (changeConfig.isForceEnabled(changeId)) {
+               return true;
+           }
+           if (changeConfig.isForceDisabled(changeId)) {
+               return false;
+           }
+           return delegate.isChangeEnabled(changeId);
+        }
+    }
+}
diff --git a/ravenwood/runtime-helper-src/libcore-fake/libcore/api/CorePlatformApi.java b/ravenwood/runtime-helper-src/libcore-fake/libcore/api/CorePlatformApi.java
new file mode 100644
index 0000000..00730ef
--- /dev/null
+++ b/ravenwood/runtime-helper-src/libcore-fake/libcore/api/CorePlatformApi.java
@@ -0,0 +1,69 @@
+
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package libcore.api;
+
+import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
+import static java.lang.annotation.ElementType.CONSTRUCTOR;
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.TYPE;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Indicates an API is part of a contract provided by the "core" set of
+ * libraries to select parts of the Android software stack.
+ *
+ * <p>This annotation should only appear on either (a) classes that are hidden by <pre>@hide</pre>
+ * javadoc tags or equivalent annotations, or (b) members of such classes. It is for use with
+ * metalava's {@code --show-single-annotation} option and so must be applied at the class level and
+ * applied again each member that is to be made part of the API. Members that are not part of the
+ * API do not have to be explicitly hidden.
+ *
+ * @hide
+ */
+@IntraCoreApi
+@Target({TYPE, FIELD, METHOD, CONSTRUCTOR, ANNOTATION_TYPE})
+@Retention(RetentionPolicy.SOURCE)
+public @interface CorePlatformApi {
+
+    /** Enumeration of the possible statuses of the API in the core/platform API surface. */
+    @IntraCoreApi
+    enum Status {
+
+        /**
+         * This API is considered stable, and so present in both the stable and legacy version of
+         * the API surface.
+        */
+        @IntraCoreApi
+        STABLE,
+
+        /**
+         * This API is not (yet) considered stable, and so only present in the legacy version of
+         * the API surface.
+         */
+        @IntraCoreApi
+        LEGACY_ONLY
+    }
+
+    /** The status of the API in the core/platform API surface. */
+    @IntraCoreApi
+    Status status() default Status.LEGACY_ONLY;
+}
diff --git a/ravenwood/runtime-helper-src/libcore-fake/libcore/api/Hide.java b/ravenwood/runtime-helper-src/libcore-fake/libcore/api/Hide.java
new file mode 100644
index 0000000..f87ff11d
--- /dev/null
+++ b/ravenwood/runtime-helper-src/libcore-fake/libcore/api/Hide.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package libcore.api;
+
+import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
+import static java.lang.annotation.ElementType.CONSTRUCTOR;
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.TYPE;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Indicates that an API is hidden by default, in a similar fashion to the
+ * <pre>@hide</pre> javadoc tag.
+ *
+ * <p>Note that, in order for this to work, metalava has to be invoked with
+ * the flag {@code --hide-annotation libcore.api.Hide}.
+ *
+ * <p>This annotation should be used in {@code .annotated.java} stub files which
+ * contain API inclusion information about {@code libcore/ojluni} classes, to
+ * avoid patching the source files with <pre>@hide</pre> javadoc tags. All
+ * build targets which consume these stub files should also apply the above
+ * metalava flag.
+ *
+ * @hide
+ */
+@Target({TYPE, FIELD, METHOD, CONSTRUCTOR, ANNOTATION_TYPE})
+@Retention(RetentionPolicy.SOURCE)
+public @interface Hide {
+}
diff --git a/ravenwood/runtime-helper-src/libcore-fake/libcore/api/IntraCoreApi.java b/ravenwood/runtime-helper-src/libcore-fake/libcore/api/IntraCoreApi.java
new file mode 100644
index 0000000..87cfcff2
--- /dev/null
+++ b/ravenwood/runtime-helper-src/libcore-fake/libcore/api/IntraCoreApi.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package libcore.api;
+
+import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
+import static java.lang.annotation.ElementType.CONSTRUCTOR;
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.TYPE;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Indicates an API is part of a contract within the "core" set of libraries, some of which may
+ * be mmodules.
+ *
+ * <p>This annotation should only appear on either (a) classes that are hidden by <pre>@hide</pre>
+ * javadoc tags or equivalent annotations, or (b) members of such classes. It is for use with
+ * metalava's {@code --show-single-annotation} option and so must be applied at the class level and
+ * applied again each member that is to be made part of the API. Members that are not part of the
+ * API do not have to be explicitly hidden.
+ *
+ * @hide
+ */
+@IntraCoreApi // @IntraCoreApi is itself part of the intra-core API
+@Target({TYPE, FIELD, METHOD, CONSTRUCTOR, ANNOTATION_TYPE})
+@Retention(RetentionPolicy.SOURCE)
+public @interface IntraCoreApi {
+}
diff --git a/ravenwood/runtime-helper-src/libcore-fake/libcore/util/NonNull.java b/ravenwood/runtime-helper-src/libcore-fake/libcore/util/NonNull.java
new file mode 100644
index 0000000..db3cd8ed
--- /dev/null
+++ b/ravenwood/runtime-helper-src/libcore-fake/libcore/util/NonNull.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package libcore.util;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.PARAMETER;
+import static java.lang.annotation.ElementType.TYPE_USE;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * Denotes that a type use can never be null.
+ * <p>
+ * This is a marker annotation and it has no specific attributes.
+ * @hide
+ */
+@Documented
+@Retention(SOURCE)
+@Target({FIELD, METHOD, PARAMETER, TYPE_USE})
+@libcore.api.IntraCoreApi
+public @interface NonNull {
+   /**
+    * Min Android API level (inclusive) to which this annotation is applied.
+    */
+   int from() default Integer.MIN_VALUE;
+
+   /**
+    * Max Android API level to which this annotation is applied.
+    */
+   int to() default Integer.MAX_VALUE;
+}
diff --git a/ravenwood/runtime-helper-src/libcore-fake/libcore/util/Nullable.java b/ravenwood/runtime-helper-src/libcore-fake/libcore/util/Nullable.java
new file mode 100644
index 0000000..3371978
--- /dev/null
+++ b/ravenwood/runtime-helper-src/libcore-fake/libcore/util/Nullable.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package libcore.util;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.PARAMETER;
+import static java.lang.annotation.ElementType.TYPE_USE;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * Denotes that a type use can be a null.
+ * <p>
+ * This is a marker annotation and it has no specific attributes.
+ * @hide
+ */
+@Documented
+@Retention(SOURCE)
+@Target({FIELD, METHOD, PARAMETER, TYPE_USE})
+@libcore.api.IntraCoreApi
+public @interface Nullable {
+   /**
+    * Min Android API level (inclusive) to which this annotation is applied.
+    */
+   int from() default Integer.MIN_VALUE;
+
+   /**
+    * Max Android API level to which this annotation is applied.
+    */
+   int to() default Integer.MAX_VALUE;
+}
diff --git a/ravenwood/scripts/ravenwood-stats-collector.sh b/ravenwood/scripts/ravenwood-stats-collector.sh
index 36601bd..b83216a 100755
--- a/ravenwood/scripts/ravenwood-stats-collector.sh
+++ b/ravenwood/scripts/ravenwood-stats-collector.sh
@@ -62,6 +62,8 @@
 
         dump "framework-minus-apex" hoststubgen_framework-minus-apex_stats.csv
         dump "service.core"  hoststubgen_services.core_stats.csv
+        dump "framework-configinfrastructure"  framework-configinfrastructure_stats.csv
+        dump "framework-statsd"  framework-statsd_stats.csv
     } > "$out"
 
     echo "Stats CVS created at $out"
@@ -76,6 +78,8 @@
 
         dump "framework-minus-apex"  hoststubgen_framework-minus-apex_apis.csv
         dump "service.core"  hoststubgen_services.core_apis.csv
+        dump "framework-configinfrastructure"  framework-configinfrastructure_apis.csv
+        dump "framework-statsd"  framework-statsd_apis.csv
     } > "$out"
 
     echo "API CVS created at $out"
diff --git a/ravenwood/tests/coretest/Android.bp b/ravenwood/tests/coretest/Android.bp
index 85f1baf..412744e 100644
--- a/ravenwood/tests/coretest/Android.bp
+++ b/ravenwood/tests/coretest/Android.bp
@@ -23,6 +23,7 @@
     ],
     srcs: [
         "test/**/*.java",
+        "test/**/*.kt",
     ],
     ravenizer: {
         strip_mockito: true,
diff --git a/ravenwood/tests/coretest/test/com/android/ravenwoodtest/coretest/RavenwoodStatsDTest.kt b/ravenwood/tests/coretest/test/com/android/ravenwoodtest/coretest/RavenwoodStatsDTest.kt
new file mode 100644
index 0000000..d5f5e29
--- /dev/null
+++ b/ravenwood/tests/coretest/test/com/android/ravenwoodtest/coretest/RavenwoodStatsDTest.kt
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.ravenwoodtest.coretest
+
+import com.android.internal.util.FrameworkStatsLog
+import org.junit.Test
+
+class RavenwoodStatsDTest {
+    @Test
+    fun testFrameworkStatsLog() {
+        FrameworkStatsLog.write(FrameworkStatsLog.PHONE_SIGNAL_STRENGTH_CHANGED, 123)
+    }
+}
\ No newline at end of file
diff --git a/ravenwood/texts/ravenwood-framework-policies.txt b/ravenwood/texts/ravenwood-framework-policies.txt
index 3649f0e..80126df 100644
--- a/ravenwood/texts/ravenwood-framework-policies.txt
+++ b/ravenwood/texts/ravenwood-framework-policies.txt
@@ -5,6 +5,9 @@
 rename com/.*/nano/   devicenano/
 rename android/.*/nano/   devicenano/
 
+# StatsD auto-generated
+class com.android.internal.util.FrameworkStatsLog keepclass
+
 # Exported to Mainline modules; cannot use annotations
 class com.android.internal.util.FastXmlSerializer keepclass
 class com.android.internal.util.FileRotator keepclass
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 4efe62c..f549c7b 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -2884,23 +2884,24 @@
                 addServiceToMap(mAppBindArgs, Context.INPUT_METHOD_SERVICE);
                 addServiceToMap(mAppBindArgs, Context.INPUT_SERVICE);
                 addServiceToMap(mAppBindArgs, "graphicsstats");
-                addServiceToMap(mAppBindArgs, Context.APP_OPS_SERVICE);
                 addServiceToMap(mAppBindArgs, "content");
                 addServiceToMap(mAppBindArgs, Context.JOB_SCHEDULER_SERVICE);
                 addServiceToMap(mAppBindArgs, Context.NOTIFICATION_SERVICE);
                 addServiceToMap(mAppBindArgs, Context.VIBRATOR_SERVICE);
                 addServiceToMap(mAppBindArgs, Context.ACCOUNT_SERVICE);
                 addServiceToMap(mAppBindArgs, Context.POWER_SERVICE);
-                addServiceToMap(mAppBindArgs, Context.USER_SERVICE);
                 addServiceToMap(mAppBindArgs, "mount");
                 addServiceToMap(mAppBindArgs, Context.PLATFORM_COMPAT_SERVICE);
             }
             // See b/79378449
             // Getting the window service and package service binder from servicemanager
             // is blocked for Apps. However they are necessary for apps.
+            // Removing User Service and App Ops Service from cache breaks boot for auto.
             // TODO: remove exception
+            addServiceToMap(mAppBindArgs, Context.APP_OPS_SERVICE);
             addServiceToMap(mAppBindArgs, "package");
             addServiceToMap(mAppBindArgs, Context.WINDOW_SERVICE);
+            addServiceToMap(mAppBindArgs, Context.USER_SERVICE);
         }
         return mAppBindArgs;
     }
diff --git a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
index d1576c5..bb4ae96 100644
--- a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
+++ b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
@@ -74,7 +74,6 @@
     private final Context mContext;
     private final Handler mHandler;
     private final PackageManagerInternal mPackageManagerInternal;
-    private final IntegrityFileManager mIntegrityFileManager;
 
     /** Create an instance of {@link AppIntegrityManagerServiceImpl}. */
     public static AppIntegrityManagerServiceImpl create(Context context) {
@@ -84,7 +83,6 @@
         return new AppIntegrityManagerServiceImpl(
                 context,
                 LocalServices.getService(PackageManagerInternal.class),
-                IntegrityFileManager.getInstance(),
                 handlerThread.getThreadHandler());
     }
 
@@ -92,11 +90,9 @@
     AppIntegrityManagerServiceImpl(
             Context context,
             PackageManagerInternal packageManagerInternal,
-            IntegrityFileManager integrityFileManager,
             Handler handler) {
         mContext = context;
         mPackageManagerInternal = packageManagerInternal;
-        mIntegrityFileManager = integrityFileManager;
         mHandler = handler;
 
         IntentFilter integrityVerificationFilter = new IntentFilter();
@@ -127,80 +123,40 @@
     @BinderThread
     public void updateRuleSet(
             String version, ParceledListSlice<Rule> rules, IntentSender statusReceiver) {
-        String ruleProvider = getCallerPackageNameOrThrow(Binder.getCallingUid());
-        if (DEBUG_INTEGRITY_COMPONENT) {
-            Slog.i(TAG, String.format("Calling rule provider name is: %s.", ruleProvider));
+        Intent intent = new Intent();
+        intent.putExtra(EXTRA_STATUS, STATUS_SUCCESS);
+        try {
+            statusReceiver.sendIntent(
+                mContext,
+                /* code= */ 0,
+                intent,
+                /* onFinished= */ null,
+                /* handler= */ null);
+        } catch (Exception e) {
+            Slog.e(TAG, "Error sending status feedback.", e);
         }
-
-        mHandler.post(
-                () -> {
-                    boolean success = true;
-                    try {
-                        mIntegrityFileManager.writeRules(version, ruleProvider, rules.getList());
-                    } catch (Exception e) {
-                        Slog.e(TAG, "Error writing rules.", e);
-                        success = false;
-                    }
-
-                    if (DEBUG_INTEGRITY_COMPONENT) {
-                        Slog.i(
-                                TAG,
-                                String.format(
-                                        "Successfully pushed rule set to version '%s' from '%s'",
-                                        version, ruleProvider));
-                    }
-
-                    Intent intent = new Intent();
-                    intent.putExtra(EXTRA_STATUS, success ? STATUS_SUCCESS : STATUS_FAILURE);
-                    try {
-                        statusReceiver.sendIntent(
-                                mContext,
-                                /* code= */ 0,
-                                intent,
-                                /* onFinished= */ null,
-                                /* handler= */ null);
-                    } catch (Exception e) {
-                        Slog.e(TAG, "Error sending status feedback.", e);
-                    }
-                });
     }
 
     @Override
     @BinderThread
     public String getCurrentRuleSetVersion() {
-        getCallerPackageNameOrThrow(Binder.getCallingUid());
-
-        RuleMetadata ruleMetadata = mIntegrityFileManager.readMetadata();
-        return (ruleMetadata != null && ruleMetadata.getVersion() != null)
-                ? ruleMetadata.getVersion()
-                : "";
+        return "";
     }
 
     @Override
     @BinderThread
     public String getCurrentRuleSetProvider() {
-        getCallerPackageNameOrThrow(Binder.getCallingUid());
-
-        RuleMetadata ruleMetadata = mIntegrityFileManager.readMetadata();
-        return (ruleMetadata != null && ruleMetadata.getRuleProvider() != null)
-                ? ruleMetadata.getRuleProvider()
-                : "";
+        return "";
     }
 
     @Override
     public ParceledListSlice<Rule> getCurrentRules() {
-        List<Rule> rules = Collections.emptyList();
-        try {
-            rules = mIntegrityFileManager.readRules(/* appInstallMetadata= */ null);
-        } catch (Exception e) {
-            Slog.e(TAG, "Error getting current rules", e);
-        }
-        return new ParceledListSlice<>(rules);
+        return new ParceledListSlice<>(Collections.emptyList());
     }
 
     @Override
     public List<String> getWhitelistedRuleProviders() {
-        return getAllowedRuleProviderSystemApps();
+        return Collections.emptyList();
     }
 
     private void handleIntegrityVerification(Intent intent) {
@@ -208,90 +164,4 @@
         mPackageManagerInternal.setIntegrityVerificationResult(
                 verificationId, PackageManagerInternal.INTEGRITY_VERIFICATION_ALLOW);
     }
-
-    /** We will use the SHA256 digest of a package name if it is more than 32 bytes long. */
-    private String getPackageNameNormalized(String packageName) {
-        if (packageName.length() <= 32) {
-            return packageName;
-        }
-
-        try {
-            MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
-            byte[] hashBytes = messageDigest.digest(packageName.getBytes(StandardCharsets.UTF_8));
-            return getHexDigest(hashBytes);
-        } catch (NoSuchAlgorithmException e) {
-            throw new RuntimeException("SHA-256 algorithm not found", e);
-        }
-    }
-
-    private String getCallerPackageNameOrThrow(int callingUid) {
-        String callerPackageName = getCallingRulePusherPackageName(callingUid);
-        if (callerPackageName == null) {
-            throw new SecurityException(
-                    "Only system packages specified in config_integrityRuleProviderPackages are "
-                            + "allowed to call this method.");
-        }
-        return callerPackageName;
-    }
-
-    private String getCallingRulePusherPackageName(int callingUid) {
-        // Obtain the system apps that are allowlisted in config_integrityRuleProviderPackages.
-        List<String> allowedRuleProviders = getAllowedRuleProviderSystemApps();
-        if (DEBUG_INTEGRITY_COMPONENT) {
-            Slog.i(
-                    TAG,
-                    String.format(
-                            "Rule provider system app list contains: %s", allowedRuleProviders));
-        }
-
-        // Identify the package names in the caller list.
-        List<String> callingPackageNames = getPackageListForUid(callingUid);
-
-        // Find the intersection between the allowed and calling packages. Ideally, we will have
-        // at most one package name here. But if we have more, it is fine.
-        List<String> allowedCallingPackages = new ArrayList<>();
-        for (String packageName : callingPackageNames) {
-            if (allowedRuleProviders.contains(packageName)) {
-                allowedCallingPackages.add(packageName);
-            }
-        }
-
-        return allowedCallingPackages.isEmpty() ? null : allowedCallingPackages.get(0);
-    }
-
-    private List<String> getAllowedRuleProviderSystemApps() {
-        List<String> integrityRuleProviders =
-                Arrays.asList(
-                        mContext.getResources()
-                                .getStringArray(R.array.config_integrityRuleProviderPackages));
-
-        // Filter out the rule provider packages that are not system apps.
-        List<String> systemAppRuleProviders = new ArrayList<>();
-        for (String ruleProvider : integrityRuleProviders) {
-            if (isSystemApp(ruleProvider)) {
-                systemAppRuleProviders.add(ruleProvider);
-            }
-        }
-        return systemAppRuleProviders;
-    }
-
-    private boolean isSystemApp(String packageName) {
-        try {
-            PackageInfo existingPackageInfo =
-                    mContext.getPackageManager().getPackageInfo(packageName, /* flags= */ 0);
-            return existingPackageInfo.applicationInfo != null
-                    && existingPackageInfo.applicationInfo.isSystemApp();
-        } catch (PackageManager.NameNotFoundException e) {
-            return false;
-        }
-    }
-
-    private List<String> getPackageListForUid(int uid) {
-        try {
-            return Arrays.asList(mContext.getPackageManager().getPackagesForUid(uid));
-        } catch (NullPointerException e) {
-            Slog.w(TAG, String.format("No packages were found for uid: %d", uid));
-            return List.of();
-        }
-    }
 }
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index b676fa2..cae941f 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -104,6 +104,7 @@
 import com.android.internal.notification.SystemNotificationChannels;
 import com.android.internal.os.BinderInternal;
 import com.android.internal.os.RuntimeInit;
+import com.android.internal.pm.RoSystemFeatures;
 import com.android.internal.policy.AttributeCache;
 import com.android.internal.util.ConcurrentUtils;
 import com.android.internal.util.EmergencyAffordanceManager;
@@ -1465,8 +1466,7 @@
         boolean disableCameraService = SystemProperties.getBoolean("config.disable_cameraservice",
                 false);
 
-        boolean isWatch = context.getPackageManager().hasSystemFeature(
-                PackageManager.FEATURE_WATCH);
+        boolean isWatch = RoSystemFeatures.hasFeatureWatch(context);
 
         boolean isArc = context.getPackageManager().hasSystemFeature(
                 "org.chromium.arc");
@@ -2708,7 +2708,7 @@
             t.traceEnd();
         }
 
-        if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_EMBEDDED)) {
+        if (RoSystemFeatures.hasFeatureEmbedded(context)) {
             t.traceBegin("StartIoTSystemService");
             mSystemServiceManager.startService(IOT_SERVICE_CLASS);
             t.traceEnd();
@@ -3077,9 +3077,7 @@
                 }, WEBVIEW_PREPARATION);
             }
 
-            boolean isAutomotive = mPackageManager
-                    .hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE);
-            if (isAutomotive) {
+            if (RoSystemFeatures.hasFeatureAutomotive(context)) {
                 t.traceBegin("StartCarServiceHelperService");
                 final SystemService cshs = mSystemServiceManager
                         .startService(CAR_SERVICE_HELPER_SERVICE_CLASS);
diff --git a/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java b/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java
index 9c6412b..93aa10b 100644
--- a/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java
@@ -135,7 +135,6 @@
     @Mock PlatformCompat mPlatformCompat;
     @Mock Context mMockContext;
     @Mock Resources mMockResources;
-    @Mock IntegrityFileManager mIntegrityFileManager;
     @Mock Handler mHandler;
 
     private final Context mRealContext = InstrumentationRegistry.getTargetContext();
@@ -169,7 +168,6 @@
                 new AppIntegrityManagerServiceImpl(
                         mMockContext,
                         mPackageManagerInternal,
-                        mIntegrityFileManager,
                         mHandler);
 
         mSpyPackageManager = spy(mRealContext.getPackageManager());
@@ -177,7 +175,6 @@
         when(mMockContext.getPackageManager()).thenReturn(mSpyPackageManager);
         when(mMockContext.getResources()).thenReturn(mMockResources);
         when(mMockResources.getStringArray(anyInt())).thenReturn(new String[] {});
-        when(mIntegrityFileManager.initialized()).thenReturn(true);
         // These are needed to override the Settings.Global.get result.
         when(mMockContext.getContentResolver()).thenReturn(mRealContext.getContentResolver());
         setIntegrityCheckIncludesRuleProvider(true);
@@ -191,98 +188,6 @@
     }
 
     @Test
-    public void updateRuleSet_notAuthorized() throws Exception {
-        makeUsSystemApp();
-        Rule rule =
-                new Rule(
-                        new AtomicFormula.BooleanAtomicFormula(AtomicFormula.PRE_INSTALLED, true),
-                        Rule.DENY);
-        TestUtils.assertExpectException(
-                SecurityException.class,
-                "Only system packages specified in config_integrityRuleProviderPackages are"
-                        + " allowed to call this method.",
-                () ->
-                        mService.updateRuleSet(
-                                VERSION,
-                                new ParceledListSlice<>(Arrays.asList(rule)),
-                                /* statusReceiver= */ null));
-    }
-
-    @Test
-    public void updateRuleSet_notSystemApp() throws Exception {
-        allowlistUsAsRuleProvider();
-        makeUsSystemApp(false);
-        Rule rule =
-                new Rule(
-                        new AtomicFormula.BooleanAtomicFormula(AtomicFormula.PRE_INSTALLED, true),
-                        Rule.DENY);
-        TestUtils.assertExpectException(
-                SecurityException.class,
-                "Only system packages specified in config_integrityRuleProviderPackages are"
-                        + " allowed to call this method.",
-                () ->
-                        mService.updateRuleSet(
-                                VERSION,
-                                new ParceledListSlice<>(Arrays.asList(rule)),
-                                /* statusReceiver= */ null));
-    }
-
-    @Test
-    public void updateRuleSet_authorized() throws Exception {
-        allowlistUsAsRuleProvider();
-        makeUsSystemApp();
-        Rule rule =
-                new Rule(
-                        new AtomicFormula.BooleanAtomicFormula(AtomicFormula.PRE_INSTALLED, true),
-                        Rule.DENY);
-
-        // no SecurityException
-        mService.updateRuleSet(
-                VERSION, new ParceledListSlice<>(Arrays.asList(rule)), mock(IntentSender.class));
-    }
-
-    @Test
-    public void updateRuleSet_correctMethodCall() throws Exception {
-        allowlistUsAsRuleProvider();
-        makeUsSystemApp();
-        IntentSender mockReceiver = mock(IntentSender.class);
-        List<Rule> rules =
-                Arrays.asList(
-                        new Rule(
-                                IntegrityFormula.Application.packageNameEquals(PACKAGE_NAME),
-                                Rule.DENY));
-
-        mService.updateRuleSet(VERSION, new ParceledListSlice<>(rules), mockReceiver);
-        runJobInHandler();
-
-        verify(mIntegrityFileManager).writeRules(VERSION, TEST_FRAMEWORK_PACKAGE, rules);
-        ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
-        verify(mockReceiver).sendIntent(any(), anyInt(), intentCaptor.capture(), any(), any());
-        assertEquals(STATUS_SUCCESS, intentCaptor.getValue().getIntExtra(EXTRA_STATUS, -1));
-    }
-
-    @Test
-    public void updateRuleSet_fail() throws Exception {
-        allowlistUsAsRuleProvider();
-        makeUsSystemApp();
-        doThrow(new IOException()).when(mIntegrityFileManager).writeRules(any(), any(), any());
-        IntentSender mockReceiver = mock(IntentSender.class);
-        List<Rule> rules =
-                Arrays.asList(
-                        new Rule(
-                                IntegrityFormula.Application.packageNameEquals(PACKAGE_NAME),
-                                Rule.DENY));
-
-        mService.updateRuleSet(VERSION, new ParceledListSlice<>(rules), mockReceiver);
-        runJobInHandler();
-
-        verify(mIntegrityFileManager).writeRules(VERSION, TEST_FRAMEWORK_PACKAGE, rules);
-        ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
-        verify(mockReceiver).sendIntent(any(), anyInt(), intentCaptor.capture(), any(), any());
-        assertEquals(STATUS_FAILURE, intentCaptor.getValue().getIntExtra(EXTRA_STATUS, -1));
-    }
-
-    @Test
     public void broadcastReceiverRegistration() throws Exception {
         allowlistUsAsRuleProvider();
         makeUsSystemApp();
@@ -316,71 +221,6 @@
                         1, PackageManagerInternal.INTEGRITY_VERIFICATION_ALLOW);
     }
 
-    @Test
-    public void handleBroadcast_notInitialized() throws Exception {
-        allowlistUsAsRuleProvider();
-        makeUsSystemApp();
-        when(mIntegrityFileManager.initialized()).thenReturn(false);
-        ArgumentCaptor<BroadcastReceiver> broadcastReceiverCaptor =
-                ArgumentCaptor.forClass(BroadcastReceiver.class);
-        verify(mMockContext)
-                .registerReceiver(broadcastReceiverCaptor.capture(), any(), any(), any());
-        Intent intent = makeVerificationIntent();
-
-        broadcastReceiverCaptor.getValue().onReceive(mMockContext, intent);
-        runJobInHandler();
-
-        // The evaluation will still run since we still evaluate manifest based rules.
-        verify(mPackageManagerInternal)
-                .setIntegrityVerificationResult(
-                        1, PackageManagerInternal.INTEGRITY_VERIFICATION_ALLOW);
-    }
-
-    @Test
-    public void verifierAsInstaller_skipIntegrityVerification() throws Exception {
-        allowlistUsAsRuleProvider();
-        makeUsSystemApp();
-        setIntegrityCheckIncludesRuleProvider(false);
-        ArgumentCaptor<BroadcastReceiver> broadcastReceiverCaptor =
-                ArgumentCaptor.forClass(BroadcastReceiver.class);
-        verify(mMockContext, atLeastOnce())
-                .registerReceiver(broadcastReceiverCaptor.capture(), any(), any(), any());
-        Intent intent = makeVerificationIntent(TEST_FRAMEWORK_PACKAGE);
-
-        broadcastReceiverCaptor.getValue().onReceive(mMockContext, intent);
-        runJobInHandler();
-
-        verify(mPackageManagerInternal)
-                .setIntegrityVerificationResult(
-                        1, PackageManagerInternal.INTEGRITY_VERIFICATION_ALLOW);
-    }
-
-    @Test
-    public void getCurrentRules() throws Exception {
-        allowlistUsAsRuleProvider();
-        makeUsSystemApp();
-        Rule rule = new Rule(IntegrityFormula.Application.packageNameEquals("package"), Rule.DENY);
-        when(mIntegrityFileManager.readRules(any())).thenReturn(Arrays.asList(rule));
-
-        assertThat(mService.getCurrentRules().getList()).containsExactly(rule);
-    }
-
-    @Test
-    public void getWhitelistedRuleProviders_returnsEmptyForNonSystemApps() throws Exception {
-        allowlistUsAsRuleProvider();
-        makeUsSystemApp(false);
-
-        assertThat(mService.getWhitelistedRuleProviders()).isEmpty();
-    }
-
-    @Test
-    public void getWhitelistedRuleProviders() throws Exception {
-        allowlistUsAsRuleProvider();
-        makeUsSystemApp();
-
-        assertThat(mService.getWhitelistedRuleProviders()).containsExactly(TEST_FRAMEWORK_PACKAGE);
-    }
-
     private void allowlistUsAsRuleProvider() {
         Resources mockResources = mock(Resources.class);
         when(mockResources.getStringArray(R.array.config_integrityRuleProviderPackages))
diff --git a/services/tests/servicestests/src/com/android/server/security/advancedprotection/OWNERS b/services/tests/servicestests/src/com/android/server/security/advancedprotection/OWNERS
new file mode 100644
index 0000000..9bf5e58
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/security/advancedprotection/OWNERS
@@ -0,0 +1 @@
+file:platform/frameworks/base:main:/core/java/android/security/advancedprotection/OWNERS
diff --git a/tools/fonts/fontchain_linter.py b/tools/fonts/fontchain_linter.py
index 006a0290..3bde929 100755
--- a/tools/fonts/fontchain_linter.py
+++ b/tools/fonts/fontchain_linter.py
@@ -10,6 +10,14 @@
 
 from fontTools import ttLib
 
+# TODO(nona): Remove hard coded font version and unicode versions.
+# Figure out a way of giving this information with command lines.
+EMOJI_FONT_TO_UNICODE_MAP = {
+    '2.034': 15.0,
+    '2.042': 15.1,
+    '2.047': 16.0,
+}
+
 EMOJI_VS = 0xFE0F
 
 LANG_TO_SCRIPT = {
@@ -217,9 +225,8 @@
 
 
 class FontRecord(object):
-    def __init__(self, name, psName, scripts, variant, weight, style, fallback_for, font):
+    def __init__(self, name, scripts, variant, weight, style, fallback_for, font):
         self.name = name
-        self.psName = psName
         self.scripts = scripts
         self.variant = variant
         self.weight = weight
@@ -282,13 +289,23 @@
             m = trim_re.match(font_file)
             font_file = m.group(1)
 
-            weight = int(child.get('weight'))
-            assert weight % 100 == 0, (
-                'Font weight "%d" is not a multiple of 100.' % weight)
+            # In case of variable font and it supports `wght` axis, the weight attribute can be
+            # dropped which is automatically adjusted at runtime.
+            if 'weight' in child:
+                weight = int(child.get('weight'))
+                assert weight % 100 == 0, (
+                    'Font weight "%d" is not a multiple of 100.' % weight)
+            else:
+                weight = None
 
-            style = child.get('style')
-            assert style in {'normal', 'italic'}, (
-                'Unknown style "%s"' % style)
+            # In case of variable font and it supports `ital` or `slnt` axes, the style attribute
+            # can be dropped which is automatically adjusted at runtime.
+            if 'style' in child:
+                style = child.get('style')
+                assert style in {'normal', 'italic'}, (
+                    'Unknown style "%s"' % style)
+            else:
+                style = None
 
             fallback_for = child.get('fallbackFor')
 
@@ -306,7 +323,6 @@
 
             record = FontRecord(
                 name,
-                child.get('postScriptName'),
                 frozenset(scripts),
                 variant,
                 weight,
@@ -357,6 +373,11 @@
     # regional indicator A..Z
     return 0x1F1E6 <= x <= 0x1F1FF
 
+def is_flag_sequence(seq):
+    if type(seq) == int:
+      return False
+    len(seq) == 2 and is_regional_indicator(seq[0]) and is_regional_indicator(seq[1])
+
 def is_tag(x):
     # tag block
     return 0xE0000 <= x <= 0xE007F
@@ -391,17 +412,43 @@
         if "meta" in ttf:
             assert 'Emji' not in ttf["meta"].data, 'NotoColorEmoji MUST be a compat font'
 
+def is_flag_emoji(font):
+    return 0x1F1E6 in get_best_cmap(font)
+
+def emoji_font_version_to_unicode_version(font_version):
+    version_str = '%.3f' % font_version
+    assert version_str in EMOJI_FONT_TO_UNICODE_MAP, 'Unknown emoji font verion: %s' % version_str
+    return EMOJI_FONT_TO_UNICODE_MAP[version_str]
+
 
 def check_emoji_font_coverage(emoji_fonts, all_emoji, equivalent_emoji):
     coverages = []
+    emoji_font_version = 0
+    emoji_flag_font_version = 0
     for emoji_font in emoji_fonts:
         coverages.append(get_emoji_map(emoji_font))
 
+        # Find the largest version of the installed emoji font.
+        version = open_font(emoji_font)['head'].fontRevision
+        if is_flag_emoji(emoji_font):
+          emoji_flag_font_version = max(emoji_flag_font_version, version)
+        else:
+          emoji_font_version = max(emoji_font_version, version)
+
+    emoji_flag_unicode_version = emoji_font_version_to_unicode_version(emoji_flag_font_version)
+    emoji_unicode_version = emoji_font_version_to_unicode_version(emoji_font_version)
+
     errors = []
 
     for sequence in all_emoji:
         if all([sequence not in coverage for coverage in coverages]):
-            errors.append('%s is not supported in the emoji font.' % printable(sequence))
+            sequence_version = float(_age_by_chars[sequence])
+            if is_flag_sequence(sequence):
+                if sequence_version <= emoji_flag_unicode_version:
+                    errors.append('%s is not supported in the emoji font.' % printable(sequence))
+            else:
+                if sequence_version <= emoji_unicode_version:
+                    errors.append('%s is not supported in the emoji font.' % printable(sequence))
 
     for coverage in coverages:
         for sequence in coverage:
@@ -480,6 +527,19 @@
                 repr(missing_text_chars))
 
 
+def parse_unicode_seq(chars):
+    if ' ' in chars:  # character sequence
+        sequence = [int(ch, 16) for ch in chars.split(' ')]
+        additions = [tuple(sequence)]
+    elif '..' in chars:  # character range
+        char_start, char_end = chars.split('..')
+        char_start = int(char_start, 16)
+        char_end = int(char_end, 16)
+        additions = range(char_start, char_end+1)
+    else:  # single character
+        additions = [int(chars, 16)]
+    return additions
+
 # Setting reverse to true returns a dictionary that maps the values to sets of
 # characters, useful for some binary properties. Otherwise, we get a
 # dictionary that maps characters to the property values, assuming there's only
@@ -501,16 +561,8 @@
             chars = chars.strip()
             prop = prop.strip()
 
-            if ' ' in chars:  # character sequence
-                sequence = [int(ch, 16) for ch in chars.split(' ')]
-                additions = [tuple(sequence)]
-            elif '..' in chars:  # character range
-                char_start, char_end = chars.split('..')
-                char_start = int(char_start, 16)
-                char_end = int(char_end, 16)
-                additions = range(char_start, char_end+1)
-            else:  # singe character
-                additions = [int(chars, 16)]
+            additions = parse_unicode_seq(chars)
+
             if reverse:
                 output_dict[prop].update(additions)
             else:
@@ -519,6 +571,32 @@
                     output_dict[addition] = prop
     return output_dict
 
+def parse_sequence_age(file_path):
+    VERSION_RE = re.compile(r'E([\d\.]+)')
+    output_dict = {}
+    with open(file_path) as datafile:
+        for line in datafile:
+            comment = ''
+            if '#' in line:
+                hash_pos = line.index('#')
+                comment = line[hash_pos + 1:].strip()
+                line = line[:hash_pos]
+            line = line.strip()
+            if not line:
+                continue
+
+            chars = line[:line.index(';')].strip()
+
+            m = VERSION_RE.match(comment)
+            assert m, 'Version not found: unknown format: %s' % line
+            version = m.group(1)
+
+            additions = parse_unicode_seq(chars)
+
+            for addition in additions:
+                assert addition not in output_dict
+                output_dict[addition] = version
+    return output_dict
 
 def parse_emoji_variants(file_path):
     emoji_set = set()
@@ -543,7 +621,7 @@
 
 
 def parse_ucd(ucd_path):
-    global _emoji_properties, _chars_by_age
+    global _emoji_properties, _chars_by_age, _age_by_chars
     global _text_variation_sequences, _emoji_variation_sequences
     global _emoji_sequences, _emoji_zwj_sequences
     _emoji_properties = parse_unicode_datafile(
@@ -555,6 +633,10 @@
 
     _chars_by_age = parse_unicode_datafile(
         path.join(ucd_path, 'DerivedAge.txt'), reverse=True)
+    _age_by_chars = parse_unicode_datafile(
+        path.join(ucd_path, 'DerivedAge.txt'))
+    _age_by_chars.update(parse_sequence_age(
+        path.join(ucd_path, 'emoji-sequences.txt')))
     sequences = parse_emoji_variants(
         path.join(ucd_path, 'emoji-variation-sequences.txt'))
     _text_variation_sequences, _emoji_variation_sequences = sequences
@@ -743,44 +825,12 @@
                 break
             assert_font_supports_none_of_chars(record.font, cjk_punctuation, name)
 
-def getPostScriptName(font):
-  font_file, index = font
-  font_path = path.join(_fonts_dir, font_file)
-  if index is not None:
-      # Use the first font file in the collection for resolving post script name.
-      ttf = ttLib.TTFont(font_path, fontNumber=0)
-  else:
-      ttf = ttLib.TTFont(font_path)
-
-  nameTable = ttf['name']
-  for name in nameTable.names:
-      if (name.nameID == 6 and name.platformID == 3 and name.platEncID == 1
-          and name.langID == 0x0409):
-          return str(name)
-
-def check_canonical_name():
-    for record in _all_fonts:
-        file_name, index = record.font
-
-        psName = getPostScriptName(record.font)
-        if record.psName:
-            # If fonts element has postScriptName attribute, it should match with the PostScript
-            # name in the name table.
-            assert psName == record.psName, ('postScriptName attribute %s should match with %s' % (
-                record.psName, psName))
-        else:
-            # If fonts element doesn't have postScriptName attribute, the file name should match
-            # with the PostScript name in the name table.
-            assert psName == file_name[:-4], ('file name %s should match with %s' % (
-                file_name, psName))
-
-
 def main():
     global _fonts_dir
     target_out = sys.argv[1]
     _fonts_dir = path.join(target_out, 'fonts')
 
-    fonts_xml_path = path.join(target_out, 'etc', 'fonts.xml')
+    fonts_xml_path = path.join(target_out, 'etc', 'font_fallback.xml')
 
     parse_fonts_xml(fonts_xml_path)
 
@@ -793,8 +843,6 @@
 
     check_cjk_punctuation()
 
-    check_canonical_name()
-
     check_emoji = sys.argv[2]
     if check_emoji == 'true':
         ucd_path = sys.argv[3]