Merge "Pull sdcard mounted atom."
diff --git a/api/current.txt b/api/current.txt
index 15c4fdf..af8f53f 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -13064,6 +13064,10 @@
     method public String buildUnionSubQuery(String, String[], java.util.Set<java.lang.String>, int, String, String, String, String);
     method @Deprecated public String buildUnionSubQuery(String, String[], java.util.Set<java.lang.String>, int, String, String, String[], String, String);
     method public int delete(@NonNull android.database.sqlite.SQLiteDatabase, @Nullable String, @Nullable String[]);
+    method public android.database.sqlite.SQLiteDatabase.CursorFactory getCursorFactory();
+    method public boolean getDistinct();
+    method public java.util.Map<java.lang.String,java.lang.String> getProjectionMap();
+    method public boolean getStrict();
     method public String getTables();
     method public android.database.Cursor query(android.database.sqlite.SQLiteDatabase, String[], String, String[], String, String, String);
     method public android.database.Cursor query(android.database.sqlite.SQLiteDatabase, String[], String, String[], String, String, String, String);
@@ -23408,7 +23412,7 @@
     method @NonNull public android.media.AudioPresentation.Builder setProgramId(int);
   }
 
-  public class AudioRecord implements android.media.AudioRecordingMonitor android.media.AudioRouting {
+  public class AudioRecord implements android.media.AudioRecordingMonitor android.media.AudioRouting android.media.MicrophoneDirection {
     ctor public AudioRecord(int, int, int, int, int) throws java.lang.IllegalArgumentException;
     method public void addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler);
     method @Deprecated public void addOnRoutingChangedListener(android.media.AudioRecord.OnRoutingChangedListener, android.os.Handler);
@@ -23443,6 +23447,8 @@
     method public void release();
     method public void removeOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener);
     method @Deprecated public void removeOnRoutingChangedListener(android.media.AudioRecord.OnRoutingChangedListener);
+    method public boolean setMicrophoneDirection(int);
+    method public boolean setMicrophoneFieldDimension(@FloatRange(from=-1.0, to=1.0) float);
     method public int setNotificationMarkerPosition(int);
     method public int setPositionNotificationPeriod(int);
     method public boolean setPreferredDevice(android.media.AudioDeviceInfo);
@@ -26092,6 +26098,15 @@
     field public static final android.media.MediaTimestamp TIMESTAMP_UNKNOWN;
   }
 
+  public interface MicrophoneDirection {
+    method public boolean setMicrophoneDirection(int);
+    method public boolean setMicrophoneFieldDimension(@FloatRange(from=-1.0, to=1.0) float);
+    field public static final int MIC_DIRECTION_BACK = 2; // 0x2
+    field public static final int MIC_DIRECTION_EXTERNAL = 3; // 0x3
+    field public static final int MIC_DIRECTION_FRONT = 1; // 0x1
+    field public static final int MIC_DIRECTION_UNSPECIFIED = 0; // 0x0
+  }
+
   public final class MicrophoneInfo {
     method @NonNull public String getAddress();
     method public java.util.List<android.util.Pair<java.lang.Integer,java.lang.Integer>> getChannelMapping();
@@ -41588,6 +41603,7 @@
     field public static final android.os.Parcelable.Creator<android.service.notification.Adjustment> CREATOR;
     field public static final String KEY_CONTEXTUAL_ACTIONS = "key_contextual_actions";
     field public static final String KEY_IMPORTANCE = "key_importance";
+    field public static final String KEY_SNOOZE_CRITERIA = "key_snooze_criteria";
     field public static final String KEY_TEXT_REPLIES = "key_text_replies";
     field public static final String KEY_USER_SENTIMENT = "key_user_sentiment";
   }
@@ -41645,12 +41661,14 @@
     method public void onActionInvoked(@NonNull String, @NonNull android.app.Notification.Action, int);
     method public final android.os.IBinder onBind(android.content.Intent);
     method public void onNotificationDirectReplied(@NonNull String);
-    method public android.service.notification.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification);
+    method public abstract android.service.notification.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification);
     method public android.service.notification.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification, android.app.NotificationChannel);
     method public void onNotificationExpansionChanged(@NonNull String, boolean, boolean);
     method public void onNotificationRemoved(android.service.notification.StatusBarNotification, android.service.notification.NotificationListenerService.RankingMap, android.service.notification.NotificationStats, int);
+    method public abstract void onNotificationSnoozedUntilContext(android.service.notification.StatusBarNotification, String);
     method public void onNotificationsSeen(java.util.List<java.lang.String>);
     method public void onSuggestedReplySent(@NonNull String, @NonNull CharSequence, int);
+    method public final void unsnoozeNotification(String);
     field public static final String SERVICE_INTERFACE = "android.service.notification.NotificationAssistantService";
     field public static final int SOURCE_FROM_APP = 0; // 0x0
     field public static final int SOURCE_FROM_ASSISTANT = 1; // 0x1
diff --git a/api/system-current.txt b/api/system-current.txt
index fb2b9e1..33b1586 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -3475,7 +3475,7 @@
     field public static final int PLAYER_TYPE_UNKNOWN = -1; // 0xffffffff
   }
 
-  public class AudioRecord implements android.media.AudioRecordingMonitor android.media.AudioRouting {
+  public class AudioRecord implements android.media.AudioRecordingMonitor android.media.AudioRouting android.media.MicrophoneDirection {
     ctor public AudioRecord(android.media.AudioAttributes, android.media.AudioFormat, int, int) throws java.lang.IllegalArgumentException;
   }
 
@@ -6522,6 +6522,15 @@
 
 package android.service.notification {
 
+  public final class Adjustment implements android.os.Parcelable {
+    ctor protected Adjustment(android.os.Parcel);
+    field public static final String KEY_PEOPLE = "key_people";
+  }
+
+  public final class NotificationStats implements android.os.Parcelable {
+    ctor protected NotificationStats(android.os.Parcel);
+  }
+
   public final class SnoozeCriterion implements android.os.Parcelable {
     ctor public SnoozeCriterion(String, CharSequence, CharSequence);
     ctor protected SnoozeCriterion(android.os.Parcel);
diff --git a/api/test-current.txt b/api/test-current.txt
index 2cb01ea..fecb07a 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -802,6 +802,7 @@
 
   public class LocationManager {
     method public String[] getBackgroundThrottlingWhitelist();
+    method public String[] getIgnoreSettingsWhitelist();
     method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(android.location.LocationRequest, android.location.LocationListener, android.os.Looper);
     method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(android.location.LocationRequest, android.app.PendingIntent);
     method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void setLocationEnabledForUser(boolean, android.os.UserHandle);
diff --git a/cmds/statsd/src/FieldValue.cpp b/cmds/statsd/src/FieldValue.cpp
index 80ed807..13f5c8a 100644
--- a/cmds/statsd/src/FieldValue.cpp
+++ b/cmds/statsd/src/FieldValue.cpp
@@ -19,6 +19,7 @@
 #include "FieldValue.h"
 #include "HashableDimensionKey.h"
 #include "math.h"
+#include "statslog.h"
 
 namespace android {
 namespace os {
@@ -122,6 +123,24 @@
     return false;
 }
 
+int32_t getUidIfExists(const FieldValue& value) {
+    bool isUid = false;
+    // the field is uid field if the field is the uid field in attribution node or marked as
+    // is_uid in atoms.proto
+    if (isAttributionUidField(value)) {
+        isUid = true;
+    } else {
+        auto it = android::util::AtomsInfo::kAtomsWithUidField.find(value.mField.getTag());
+        if (it != android::util::AtomsInfo::kAtomsWithUidField.end()) {
+            int uidField = it->second;  // uidField is the field number in proto
+            isUid = value.mField.getDepth() == 0 && value.mField.getPosAtDepth(0) == uidField &&
+                    value.mValue.getType() == INT;
+        }
+    }
+
+    return isUid ? value.mValue.int_value : -1;
+}
+
 bool isAttributionUidField(const Field& field, const Value& value) {
     int f = field.getField() & 0xff007f;
     if (f == 0x10001 && value.getType() == INT) {
diff --git a/cmds/statsd/src/FieldValue.h b/cmds/statsd/src/FieldValue.h
index a5d00ac..6729e05 100644
--- a/cmds/statsd/src/FieldValue.h
+++ b/cmds/statsd/src/FieldValue.h
@@ -386,6 +386,9 @@
 
 bool isAttributionUidField(const FieldValue& value);
 
+/* returns uid if the field is uid field, or -1 if the field is not a uid field */
+int getUidIfExists(const FieldValue& value);
+
 void translateFieldMatcher(const FieldMatcher& matcher, std::vector<Matcher>* output);
 
 bool isAttributionUidField(const Field& field, const Value& value);
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index fb603b9..c542b62 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -159,7 +159,7 @@
            }
 
       }))  {
-    mUidMap = new UidMap();
+    mUidMap = UidMap::getInstance();
     mPullerManager = new StatsPullerManager();
     StatsPuller::SetUidMap(mUidMap);
     mConfigManager = new ConfigManager();
diff --git a/cmds/statsd/src/anomaly/AlarmTracker.cpp b/cmds/statsd/src/anomaly/AlarmTracker.cpp
index 8d73699..019a9f7 100644
--- a/cmds/statsd/src/anomaly/AlarmTracker.cpp
+++ b/cmds/statsd/src/anomaly/AlarmTracker.cpp
@@ -78,8 +78,8 @@
     }
     if (!mSubscriptions.empty()) {
         VLOG("AlarmTracker triggers the subscribers.");
-        triggerSubscribers(mAlarmConfig.id(), DEFAULT_METRIC_DIMENSION_KEY, mConfigKey,
-                           mSubscriptions);
+        triggerSubscribers(mAlarmConfig.id(), 0 /*metricId N/A*/, DEFAULT_METRIC_DIMENSION_KEY,
+                           0 /* metricValue N/A */, mConfigKey, mSubscriptions);
     }
     firedAlarms.erase(mInternalAlarm);
     mAlarmSec = findNextAlarmSec((timestampNs-1) / NS_PER_SEC + 1); // round up
diff --git a/cmds/statsd/src/anomaly/AnomalyTracker.cpp b/cmds/statsd/src/anomaly/AnomalyTracker.cpp
index ee111cd..d1dcb5df 100644
--- a/cmds/statsd/src/anomaly/AnomalyTracker.cpp
+++ b/cmds/statsd/src/anomaly/AnomalyTracker.cpp
@@ -207,7 +207,8 @@
            getSumOverPastBuckets(key) + currentBucketValue > mAlert.trigger_if_sum_gt();
 }
 
-void AnomalyTracker::declareAnomaly(const int64_t& timestampNs, const MetricDimensionKey& key) {
+void AnomalyTracker::declareAnomaly(const int64_t& timestampNs, int64_t metricId,
+                                    const MetricDimensionKey& key, int64_t metricValue) {
     // TODO(b/110563466): Why receive timestamp? RefractoryPeriod should always be based on
     // real time right now.
     if (isInRefractoryPeriod(timestampNs, key)) {
@@ -225,7 +226,7 @@
     if (!mSubscriptions.empty()) {
         ALOGI("An anomaly (%lld) %s has occurred! Informing subscribers.",
                 mAlert.id(), key.toString().c_str());
-        informSubscribers(key);
+        informSubscribers(key, metricId, metricValue);
     } else {
         ALOGI("An anomaly has occurred! (But no subscriber for that alert.)");
     }
@@ -238,11 +239,11 @@
 }
 
 void AnomalyTracker::detectAndDeclareAnomaly(const int64_t& timestampNs,
-                                             const int64_t& currBucketNum,
+                                             const int64_t& currBucketNum, int64_t metricId,
                                              const MetricDimensionKey& key,
                                              const int64_t& currentBucketValue) {
     if (detectAnomaly(currBucketNum, key, currentBucketValue)) {
-        declareAnomaly(timestampNs, key);
+        declareAnomaly(timestampNs, metricId, key, currentBucketValue);
     }
 }
 
@@ -255,8 +256,9 @@
     return false;
 }
 
-void AnomalyTracker::informSubscribers(const MetricDimensionKey& key) {
-    triggerSubscribers(mAlert.id(), key, mConfigKey, mSubscriptions);
+void AnomalyTracker::informSubscribers(const MetricDimensionKey& key, int64_t metric_id,
+                                       int64_t metricValue) {
+    triggerSubscribers(mAlert.id(), metric_id, key, metricValue, mConfigKey, mSubscriptions);
 }
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/anomaly/AnomalyTracker.h b/cmds/statsd/src/anomaly/AnomalyTracker.h
index 927e2df..e941473 100644
--- a/cmds/statsd/src/anomaly/AnomalyTracker.h
+++ b/cmds/statsd/src/anomaly/AnomalyTracker.h
@@ -67,14 +67,16 @@
                        const int64_t& currentBucketValue);
 
     // Informs incidentd about the detected alert.
-    void declareAnomaly(const int64_t& timestampNs, const MetricDimensionKey& key);
+    void declareAnomaly(const int64_t& timestampNs, int64_t metricId, const MetricDimensionKey& key,
+                        int64_t metricValue);
 
     // Detects if, based on past buckets plus the new currentBucketValue (which generally
     // represents the partially-filled current bucket), an anomaly has happened, and if so,
     // declares an anomaly and informs relevant subscribers.
     // Also advances to currBucketNum-1.
     void detectAndDeclareAnomaly(const int64_t& timestampNs, const int64_t& currBucketNum,
-                                 const MetricDimensionKey& key, const int64_t& currentBucketValue);
+                                 int64_t metricId, const MetricDimensionKey& key,
+                                 const int64_t& currentBucketValue);
 
     // Init the AlarmMonitor which is shared across anomaly trackers.
     virtual void setAlarmMonitor(const sp<AlarmMonitor>& alarmMonitor) {
@@ -176,7 +178,7 @@
     virtual void resetStorage();
 
     // Informs the subscribers (incidentd, perfetto, broadcasts, etc) that an anomaly has occurred.
-    void informSubscribers(const MetricDimensionKey& key);
+    void informSubscribers(const MetricDimensionKey& key, int64_t metricId, int64_t metricValue);
 
     FRIEND_TEST(AnomalyTrackerTest, TestConsecutiveBuckets);
     FRIEND_TEST(AnomalyTrackerTest, TestSparseBuckets);
diff --git a/cmds/statsd/src/anomaly/DurationAnomalyTracker.cpp b/cmds/statsd/src/anomaly/DurationAnomalyTracker.cpp
index 3acfd17..2b56810 100644
--- a/cmds/statsd/src/anomaly/DurationAnomalyTracker.cpp
+++ b/cmds/statsd/src/anomaly/DurationAnomalyTracker.cpp
@@ -65,7 +65,9 @@
 
     // If the alarm is set in the past but hasn't fired yet (due to lag), catch it now.
     if (itr->second != nullptr && timestampNs >= (int64_t)NS_PER_SEC * itr->second->timestampSec) {
-        declareAnomaly(timestampNs, dimensionKey);
+        declareAnomaly(timestampNs, mAlert.metric_id(), dimensionKey,
+                       mAlert.trigger_if_sum_gt() + (timestampNs / NS_PER_SEC) -
+                               itr->second->timestampSec);
     }
     if (mAlarmMonitor != nullptr) {
         mAlarmMonitor->remove(itr->second);
@@ -100,7 +102,9 @@
 
     // Now declare each of these alarms to have fired.
     for (const auto& kv : matchedAlarms) {
-        declareAnomaly(timestampNs, kv.first);
+        declareAnomaly(
+                timestampNs, mAlert.metric_id(), kv.first,
+                mAlert.trigger_if_sum_gt() + (timestampNs / NS_PER_SEC) - kv.second->timestampSec);
         mAlarms.erase(kv.first);
         firedAlarms.erase(kv.second);  // No one else can also own it, so we're done with it.
     }
diff --git a/cmds/statsd/src/anomaly/subscriber_util.cpp b/cmds/statsd/src/anomaly/subscriber_util.cpp
index 6b46b8b..548a686 100644
--- a/cmds/statsd/src/anomaly/subscriber_util.cpp
+++ b/cmds/statsd/src/anomaly/subscriber_util.cpp
@@ -30,9 +30,8 @@
 namespace os {
 namespace statsd {
 
-void triggerSubscribers(const int64_t rule_id,
-                        const MetricDimensionKey& dimensionKey,
-                        const ConfigKey& configKey,
+void triggerSubscribers(int64_t ruleId, int64_t metricId, const MetricDimensionKey& dimensionKey,
+                        int64_t metricValue, const ConfigKey& configKey,
                         const std::vector<Subscription>& subscriptions) {
     VLOG("informSubscribers called.");
     if (subscriptions.empty()) {
@@ -50,13 +49,14 @@
         }
         switch (subscription.subscriber_information_case()) {
             case Subscription::SubscriberInformationCase::kIncidentdDetails:
-                if (!GenerateIncidentReport(subscription.incidentd_details(), rule_id, configKey)) {
+                if (!GenerateIncidentReport(subscription.incidentd_details(), ruleId, metricId,
+                                            dimensionKey, metricValue, configKey)) {
                     ALOGW("Failed to generate incident report.");
                 }
                 break;
             case Subscription::SubscriberInformationCase::kPerfettoDetails:
                 if (!CollectPerfettoTraceAndUploadToDropbox(subscription.perfetto_details(),
-                                                            subscription.id(), rule_id, configKey)) {
+                                                            subscription.id(), ruleId, configKey)) {
                     ALOGW("Failed to generate perfetto traces.");
                 }
                 break;
@@ -66,7 +66,7 @@
                 break;
             case Subscription::SubscriberInformationCase::kPerfprofdDetails:
                 if (!CollectPerfprofdTraceAndUploadToDropbox(subscription.perfprofd_details(),
-                                                             rule_id, configKey)) {
+                                                             ruleId, configKey)) {
                     ALOGW("Failed to generate perfprofd traces.");
                 }
                 break;
@@ -76,7 +76,6 @@
     }
 }
 
-
 }  // namespace statsd
 }  // namespace os
 }  // namespace android
diff --git a/cmds/statsd/src/anomaly/subscriber_util.h b/cmds/statsd/src/anomaly/subscriber_util.h
index dba8981..1df3c89 100644
--- a/cmds/statsd/src/anomaly/subscriber_util.h
+++ b/cmds/statsd/src/anomaly/subscriber_util.h
@@ -24,10 +24,9 @@
 namespace os {
 namespace statsd {
 
-void triggerSubscribers(const int64_t rule_id,
-                        const MetricDimensionKey& dimensionKey,
-                        const ConfigKey& configKey,
-                        const std::vector<Subscription>& subscriptions);
+void triggerSubscribers(const int64_t ruleId, const int64_t metricId,
+                        const MetricDimensionKey& dimensionKey, int64_t metricValue,
+                        const ConfigKey& configKey, const std::vector<Subscription>& subscriptions);
 
 }  // namespace statsd
 }  // namespace os
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.cpp b/cmds/statsd/src/metrics/CountMetricProducer.cpp
index 5707de5..e84f88d 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/CountMetricProducer.cpp
@@ -303,7 +303,7 @@
         if (prev != mCurrentFullCounters->end()) {
             countWholeBucket += prev->second;
         }
-        tracker->detectAndDeclareAnomaly(eventTimeNs, mCurrentBucketNum, eventKey,
+        tracker->detectAndDeclareAnomaly(eventTimeNs, mCurrentBucketNum, mMetricId, eventKey,
                                          countWholeBucket);
     }
 
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
index 837d532..6301793 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
@@ -485,8 +485,8 @@
                 gaugeVal = value.long_value;
             }
             for (auto& tracker : mAnomalyTrackers) {
-                tracker->detectAndDeclareAnomaly(eventTimeNs, mCurrentBucketNum, eventKey,
-                                                 gaugeVal);
+                tracker->detectAndDeclareAnomaly(eventTimeNs, mCurrentBucketNum, mMetricId,
+                                                 eventKey, gaugeVal);
             }
         }
     }
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
index bc7c872..3cf378d 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
@@ -697,8 +697,8 @@
             wholeBucketVal += prev->second;
         }
         for (auto& tracker : mAnomalyTrackers) {
-            tracker->detectAndDeclareAnomaly(
-                eventTimeNs, mCurrentBucketNum, eventKey, wholeBucketVal);
+            tracker->detectAndDeclareAnomaly(eventTimeNs, mCurrentBucketNum, mMetricId, eventKey,
+                                             wholeBucketVal);
         }
     }
 }
diff --git a/cmds/statsd/src/metrics/duration_helper/DurationTracker.h b/cmds/statsd/src/metrics/duration_helper/DurationTracker.h
index ccb1d43..081e61e 100644
--- a/cmds/statsd/src/metrics/duration_helper/DurationTracker.h
+++ b/cmds/statsd/src/metrics/duration_helper/DurationTracker.h
@@ -155,8 +155,8 @@
                                  const int64_t& currentBucketValue) {
         for (auto& anomalyTracker : mAnomalyTrackers) {
             if (anomalyTracker != nullptr) {
-                anomalyTracker->detectAndDeclareAnomaly(timestamp, currBucketNum, mEventKey,
-                                                        currentBucketValue);
+                anomalyTracker->detectAndDeclareAnomaly(timestamp, currBucketNum, mTrackerId,
+                                                        mEventKey, currentBucketValue);
             }
         }
     }
diff --git a/cmds/statsd/src/packages/UidMap.cpp b/cmds/statsd/src/packages/UidMap.cpp
index 5cf012638..d4b57dd 100644
--- a/cmds/statsd/src/packages/UidMap.cpp
+++ b/cmds/statsd/src/packages/UidMap.cpp
@@ -73,6 +73,11 @@
 
 UidMap::~UidMap() {}
 
+sp<UidMap> UidMap::getInstance() {
+    static sp<UidMap> sInstance = new UidMap();
+    return sInstance;
+}
+
 bool UidMap::hasApp(int uid, const string& packageName) const {
     lock_guard<mutex> lock(mMutex);
 
@@ -336,6 +341,61 @@
     return mBytesUsed;
 }
 
+void UidMap::writeUidMapSnapshot(int64_t timestamp, bool includeVersionStrings,
+                                 bool includeInstaller, const std::set<int32_t>& interestingUids,
+                                 std::set<string>* str_set, ProtoOutputStream* proto) {
+    lock_guard<mutex> lock(mMutex);
+
+    writeUidMapSnapshotLocked(timestamp, includeVersionStrings, includeInstaller, interestingUids,
+                              str_set, proto);
+}
+
+void UidMap::writeUidMapSnapshotLocked(int64_t timestamp, bool includeVersionStrings,
+                                       bool includeInstaller,
+                                       const std::set<int32_t>& interestingUids,
+                                       std::set<string>* str_set, ProtoOutputStream* proto) {
+    proto->write(FIELD_TYPE_INT64 | FIELD_ID_SNAPSHOT_TIMESTAMP, (long long)timestamp);
+    for (const auto& kv : mMap) {
+        if (!interestingUids.empty() &&
+            interestingUids.find(kv.first.first) == interestingUids.end()) {
+            continue;
+        }
+        uint64_t token = proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED |
+                                      FIELD_ID_SNAPSHOT_PACKAGE_INFO);
+        if (str_set != nullptr) {
+            str_set->insert(kv.first.second);
+            proto->write(FIELD_TYPE_UINT64 | FIELD_ID_SNAPSHOT_PACKAGE_NAME_HASH,
+                         (long long)Hash64(kv.first.second));
+            if (includeVersionStrings) {
+                str_set->insert(kv.second.versionString);
+                proto->write(FIELD_TYPE_UINT64 | FIELD_ID_SNAPSHOT_PACKAGE_VERSION_STRING_HASH,
+                             (long long)Hash64(kv.second.versionString));
+            }
+            if (includeInstaller) {
+                str_set->insert(kv.second.installer);
+                proto->write(FIELD_TYPE_UINT64 | FIELD_ID_SNAPSHOT_PACKAGE_INSTALLER_HASH,
+                             (long long)Hash64(kv.second.installer));
+            }
+        } else {
+            proto->write(FIELD_TYPE_STRING | FIELD_ID_SNAPSHOT_PACKAGE_NAME, kv.first.second);
+            if (includeVersionStrings) {
+                proto->write(FIELD_TYPE_STRING | FIELD_ID_SNAPSHOT_PACKAGE_VERSION_STRING,
+                             kv.second.versionString);
+            }
+            if (includeInstaller) {
+                proto->write(FIELD_TYPE_STRING | FIELD_ID_SNAPSHOT_PACKAGE_INSTALLER,
+                             kv.second.installer);
+            }
+        }
+
+        proto->write(FIELD_TYPE_INT64 | FIELD_ID_SNAPSHOT_PACKAGE_VERSION,
+                     (long long)kv.second.versionCode);
+        proto->write(FIELD_TYPE_INT32 | FIELD_ID_SNAPSHOT_PACKAGE_UID, kv.first.first);
+        proto->write(FIELD_TYPE_BOOL | FIELD_ID_SNAPSHOT_PACKAGE_DELETED, kv.second.deleted);
+        proto->end(token);
+    }
+}
+
 void UidMap::appendUidMap(const int64_t& timestamp, const ConfigKey& key, std::set<string>* str_set,
                           bool includeVersionStrings, bool includeInstaller,
                           ProtoOutputStream* proto) {
@@ -381,43 +441,9 @@
     // Write snapshot from current uid map state.
     uint64_t snapshotsToken =
             proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_SNAPSHOTS);
-    proto->write(FIELD_TYPE_INT64 | FIELD_ID_SNAPSHOT_TIMESTAMP, (long long)timestamp);
-    for (const auto& kv : mMap) {
-        uint64_t token = proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED |
-                                      FIELD_ID_SNAPSHOT_PACKAGE_INFO);
-
-        if (str_set != nullptr) {
-            str_set->insert(kv.first.second);
-            proto->write(FIELD_TYPE_UINT64 | FIELD_ID_SNAPSHOT_PACKAGE_NAME_HASH,
-                         (long long)Hash64(kv.first.second));
-            if (includeVersionStrings) {
-                str_set->insert(kv.second.versionString);
-                proto->write(FIELD_TYPE_UINT64 | FIELD_ID_SNAPSHOT_PACKAGE_VERSION_STRING_HASH,
-                             (long long)Hash64(kv.second.versionString));
-            }
-            if (includeInstaller) {
-                str_set->insert(kv.second.installer);
-                proto->write(FIELD_TYPE_UINT64 | FIELD_ID_SNAPSHOT_PACKAGE_INSTALLER_HASH,
-                             (long long)Hash64(kv.second.installer));
-            }
-        } else {
-            proto->write(FIELD_TYPE_STRING | FIELD_ID_SNAPSHOT_PACKAGE_NAME, kv.first.second);
-            if (includeVersionStrings) {
-                proto->write(FIELD_TYPE_STRING | FIELD_ID_SNAPSHOT_PACKAGE_VERSION_STRING,
-                             kv.second.versionString);
-            }
-            if (includeInstaller) {
-                proto->write(FIELD_TYPE_STRING | FIELD_ID_SNAPSHOT_PACKAGE_INSTALLER,
-                             kv.second.installer);
-            }
-        }
-
-        proto->write(FIELD_TYPE_INT64 | FIELD_ID_SNAPSHOT_PACKAGE_VERSION,
-                     (long long)kv.second.versionCode);
-        proto->write(FIELD_TYPE_INT32 | FIELD_ID_SNAPSHOT_PACKAGE_UID, kv.first.first);
-        proto->write(FIELD_TYPE_BOOL | FIELD_ID_SNAPSHOT_PACKAGE_DELETED, kv.second.deleted);
-        proto->end(token);
-    }
+    writeUidMapSnapshotLocked(timestamp, includeVersionStrings, includeInstaller,
+                              std::set<int32_t>() /*empty uid set means including every uid*/,
+                              str_set, proto);
     proto->end(snapshotsToken);
 
     int64_t prevMin = getMinimumTimestampNs();
diff --git a/cmds/statsd/src/packages/UidMap.h b/cmds/statsd/src/packages/UidMap.h
index 75ff507..a7c5fb2 100644
--- a/cmds/statsd/src/packages/UidMap.h
+++ b/cmds/statsd/src/packages/UidMap.h
@@ -91,6 +91,8 @@
     UidMap();
     ~UidMap();
     static const std::map<std::string, uint32_t> sAidToUidMapping;
+
+    static sp<UidMap> getInstance();
     /*
      * All three inputs must be the same size, and the jth element in each array refers to the same
      * tuple, ie. uid[j] corresponds to packageName[j] with versionCode[j].
@@ -152,12 +154,25 @@
 
     std::set<int32_t> getAppUid(const string& package) const;
 
+    // Write current PackageInfoSnapshot to ProtoOutputStream.
+    // interestingUids: If not empty, only write the package info for these uids. If empty, write
+    //                  package info for all uids.
+    // str_set: if not null, add new string to the set and write str_hash to proto
+    //          if null, write string to proto.
+    void writeUidMapSnapshot(int64_t timestamp, bool includeVersionStrings, bool includeInstaller,
+                             const std::set<int32_t>& interestingUids, std::set<string>* str_set,
+                             ProtoOutputStream* proto);
+
 private:
     std::set<string> getAppNamesFromUidLocked(const int32_t& uid, bool returnNormalized) const;
     string normalizeAppName(const string& appName) const;
 
     void getListenerListCopyLocked(std::vector<wp<PackageInfoListener>>* output);
 
+    void writeUidMapSnapshotLocked(int64_t timestamp, bool includeVersionStrings,
+                                   bool includeInstaller, const std::set<int32_t>& interestingUids,
+                                   std::set<string>* str_set, ProtoOutputStream* proto);
+
     mutable mutex mMutex;
     mutable mutex mIsolatedMutex;
 
diff --git a/cmds/statsd/src/stats_log.proto b/cmds/statsd/src/stats_log.proto
index 09b8fed..623d8bc 100644
--- a/cmds/statsd/src/stats_log.proto
+++ b/cmds/statsd/src/stats_log.proto
@@ -457,3 +457,17 @@
     }
     repeated LogLossStats detected_log_loss = 16;
 }
+
+message AlertTriggerDetails {
+    message MetricValue {
+        optional int64 metric_id = 1;
+        optional DimensionsValue dimension_in_what = 2;
+        optional DimensionsValue dimension_in_condition = 3;
+        optional int64 value = 4;
+    }
+    oneof value {
+        MetricValue trigger_metric = 1;
+        EventMetricData trigger_event = 2;
+    }
+    optional UidMapping.PackageInfoSnapshot package_info = 3;
+}
diff --git a/cmds/statsd/src/subscriber/IncidentdReporter.cpp b/cmds/statsd/src/subscriber/IncidentdReporter.cpp
index 42cac0c..0ed2d75 100644
--- a/cmds/statsd/src/subscriber/IncidentdReporter.cpp
+++ b/cmds/statsd/src/subscriber/IncidentdReporter.cpp
@@ -16,7 +16,10 @@
 #define DEBUG false
 #include "Log.h"
 
+#include "FieldValue.h"
 #include "IncidentdReporter.h"
+#include "packages/UidMap.h"
+#include "stats_log_util.h"
 
 #include <android/os/IIncidentManager.h>
 #include <android/os/IncidentReportArgs.h>
@@ -43,8 +46,18 @@
 const int FIELD_ID_CONFIG_KEY_UID = 1;
 const int FIELD_ID_CONFIG_KEY_ID = 2;
 
+const int FIELD_ID_TRIGGER_DETAILS = 4;
+const int FIELD_ID_TRIGGER_DETAILS_TRIGGER_METRIC = 1;
+const int FIELD_ID_METRIC_VALUE_METRIC_ID = 1;
+const int FIELD_ID_METRIC_VALUE_DIMENSION_IN_WHAT = 2;
+const int FIELD_ID_METRIC_VALUE_DIMENSION_IN_CONDITION = 3;
+const int FIELD_ID_METRIC_VALUE_VALUE = 4;
+
+const int FIELD_ID_PACKAGE_INFO = 3;
+
 namespace {
-void getProtoData(const int64_t& rule_id, const ConfigKey& configKey, vector<uint8_t>* protoData) {
+void getProtoData(const int64_t& rule_id, int64_t metricId, const MetricDimensionKey& dimensionKey,
+                  int64_t metricValue, const ConfigKey& configKey, vector<uint8_t>* protoData) {
     ProtoOutputStream headerProto;
     headerProto.write(FIELD_TYPE_INT64 | FIELD_ID_ALERT_ID, (long long)rule_id);
     uint64_t token =
@@ -53,6 +66,58 @@
     headerProto.write(FIELD_TYPE_INT64 | FIELD_ID_CONFIG_KEY_ID, (long long)configKey.GetId());
     headerProto.end(token);
 
+    token = headerProto.start(FIELD_TYPE_MESSAGE | FIELD_ID_TRIGGER_DETAILS);
+
+    // MetricValue trigger_metric = 1;
+    uint64_t metricToken =
+            headerProto.start(FIELD_TYPE_MESSAGE | FIELD_ID_TRIGGER_DETAILS_TRIGGER_METRIC);
+    // message MetricValue {
+    // optional int64 metric_id = 1;
+    headerProto.write(FIELD_TYPE_INT64 | FIELD_ID_METRIC_VALUE_METRIC_ID, (long long)metricId);
+    // optional DimensionsValue dimension_in_what = 2;
+    uint64_t dimToken =
+            headerProto.start(FIELD_TYPE_MESSAGE | FIELD_ID_METRIC_VALUE_DIMENSION_IN_WHAT);
+    writeDimensionToProto(dimensionKey.getDimensionKeyInWhat(), nullptr, &headerProto);
+    headerProto.end(dimToken);
+
+    // optional DimensionsValue dimension_in_condition = 3;
+    dimToken = headerProto.start(FIELD_TYPE_MESSAGE | FIELD_ID_METRIC_VALUE_DIMENSION_IN_CONDITION);
+    writeDimensionToProto(dimensionKey.getDimensionKeyInCondition(), nullptr, &headerProto);
+    headerProto.end(dimToken);
+
+    // optional int64 value = 4;
+    headerProto.write(FIELD_TYPE_INT64 | FIELD_ID_METRIC_VALUE_VALUE, (long long)metricValue);
+
+    // }
+    headerProto.end(metricToken);
+
+    // write relevant uid package info
+    std::set<int32_t> uids;
+
+    for (const auto& dim : dimensionKey.getDimensionKeyInWhat().getValues()) {
+        int uid = getUidIfExists(dim);
+        // any uid <= 2000 are predefined AID_*
+        if (uid > 2000) {
+            uids.insert(uid);
+        }
+    }
+
+    for (const auto& dim : dimensionKey.getDimensionKeyInCondition().getValues()) {
+        int uid = getUidIfExists(dim);
+        if (uid > 2000) {
+            uids.insert(uid);
+        }
+    }
+
+    if (!uids.empty()) {
+        uint64_t token = headerProto.start(FIELD_TYPE_MESSAGE | FIELD_ID_PACKAGE_INFO);
+        UidMap::getInstance()->writeUidMapSnapshot(getElapsedRealtimeNs(), true, true, uids,
+                                                   nullptr /*string set*/, &headerProto);
+        headerProto.end(token);
+    }
+
+    headerProto.end(token);
+
     protoData->resize(headerProto.size());
     size_t pos = 0;
     auto iter = headerProto.data();
@@ -65,7 +130,8 @@
 }
 }  // namespace
 
-bool GenerateIncidentReport(const IncidentdDetails& config, const int64_t& rule_id,
+bool GenerateIncidentReport(const IncidentdDetails& config, int64_t rule_id, int64_t metricId,
+                            const MetricDimensionKey& dimensionKey, int64_t metricValue,
                             const ConfigKey& configKey) {
     if (config.section_size() == 0) {
         VLOG("The alert %lld contains zero section in config(%d,%lld)", (unsigned long long)rule_id,
@@ -76,7 +142,7 @@
     IncidentReportArgs incidentReport;
 
     vector<uint8_t> protoData;
-    getProtoData(rule_id, configKey, &protoData);
+    getProtoData(rule_id, metricId, dimensionKey, metricValue, configKey, &protoData);
     incidentReport.addHeader(protoData);
 
     for (int i = 0; i < config.section_size(); i++) {
diff --git a/cmds/statsd/src/subscriber/IncidentdReporter.h b/cmds/statsd/src/subscriber/IncidentdReporter.h
index 1b83fe2..e78a4d9 100644
--- a/cmds/statsd/src/subscriber/IncidentdReporter.h
+++ b/cmds/statsd/src/subscriber/IncidentdReporter.h
@@ -16,6 +16,7 @@
 
 #pragma once
 
+#include "HashableDimensionKey.h"
 #include "config/ConfigKey.h"
 #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"  // Alert, IncidentdDetails
 
@@ -26,7 +27,8 @@
 /**
  * Calls incidentd to trigger an incident report and put in dropbox for uploading.
  */
-bool GenerateIncidentReport(const IncidentdDetails& config, const int64_t& rule_id,
+bool GenerateIncidentReport(const IncidentdDetails& config, int64_t rule_id, int64_t metricId,
+                            const MetricDimensionKey& dimensionKey, int64_t metricValue,
                             const ConfigKey& configKey);
 
 }  // namespace statsd
diff --git a/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp b/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp
index 960fbda..c10703c 100644
--- a/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp
+++ b/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp
@@ -90,7 +90,8 @@
                                const std::shared_ptr<DimToValMap>& bucket,
                                const int64_t& eventTimestamp) {
     for (const auto& kv : *bucket) {
-        tracker.detectAndDeclareAnomaly(eventTimestamp, bucketNum, kv.first, kv.second);
+        tracker.detectAndDeclareAnomaly(eventTimestamp, bucketNum, 0 /*metric_id*/, kv.first,
+                                        kv.second);
     }
 }
 
diff --git a/core/java/android/app/UiModeManager.java b/core/java/android/app/UiModeManager.java
index a554882..46316e1 100644
--- a/core/java/android/app/UiModeManager.java
+++ b/core/java/android/app/UiModeManager.java
@@ -229,7 +229,11 @@
      * <strong>Note:</strong> On API 22 and below, changes to the night mode
      * are only effective when the {@link Configuration#UI_MODE_TYPE_CAR car}
      * or {@link Configuration#UI_MODE_TYPE_DESK desk} mode is enabled on a
-     * device. Starting in API 23, changes to night mode are always effective.
+     * device. On API 23 through API 28, changes to night mode are always effective.
+     * <p>
+     * Starting in API 29, when the device is in car mode and this method is called, night mode
+     * will change, but the new setting is not persisted and the previously persisted setting
+     * will be restored when the device exits car mode.
      * <p>
      * Changes to night mode take effect globally and will result in a configuration change
      * (and potentially an Activity lifecycle event) being applied to all running apps.
diff --git a/core/java/android/database/sqlite/SQLiteQueryBuilder.java b/core/java/android/database/sqlite/SQLiteQueryBuilder.java
index 5722e7b..ea489c4 100644
--- a/core/java/android/database/sqlite/SQLiteQueryBuilder.java
+++ b/core/java/android/database/sqlite/SQLiteQueryBuilder.java
@@ -77,6 +77,14 @@
     }
 
     /**
+     * Get if the query is marked as DISTINCT, as last configured by
+     * {@link #setDistinct(boolean)}.
+     */
+    public boolean getDistinct() {
+        return mDistinct;
+    }
+
+    /**
      * Returns the list of tables being queried
      *
      * @return the list of tables being queried
@@ -167,6 +175,14 @@
     }
 
     /**
+     * Gets the projection map for the query, as last configured by
+     * {@link #setProjectionMap(Map)}.
+     */
+    public Map<String, String> getProjectionMap() {
+        return mProjectionMap;
+    }
+
+    /**
      * Sets a projection greylist of columns that will be allowed through, even
      * when {@link #setStrict(boolean)} is enabled. This provides a way for
      * abusive custom columns like {@code COUNT(*)} to continue working.
@@ -178,6 +194,16 @@
     }
 
     /**
+     * Gets the projection greylist for the query, as last configured by
+     * {@link #setProjectionGreylist(List)}.
+     *
+     * @hide
+     */
+    public List<Pattern> getProjectionGreylist() {
+        return mProjectionGreylist;
+    }
+
+    /**
      * Sets the cursor factory to be used for the query.  You can use
      * one factory for all queries on a database but it is normally
      * easier to specify the factory when doing this query.
@@ -189,6 +215,14 @@
     }
 
     /**
+     * Sets the cursor factory to be used for the query, as last configured by
+     * {@link #setCursorFactory(android.database.sqlite.SQLiteDatabase.CursorFactory)}.
+     */
+    public SQLiteDatabase.CursorFactory getCursorFactory() {
+        return mFactory;
+    }
+
+    /**
      * When set, the selection is verified against malicious arguments.
      * When using this class to create a statement using
      * {@link #buildQueryString(boolean, String, String[], String, String, String, String, String)},
@@ -214,6 +248,14 @@
     }
 
     /**
+     * Get if the query is marked as strict, as last configured by
+     * {@link #setStrict(boolean)}.
+     */
+    public boolean getStrict() {
+        return mStrict;
+    }
+
+    /**
      * Build an SQL query string from the given clauses.
      *
      * @param distinct true if you want each row to be unique, false otherwise.
diff --git a/core/java/android/provider/BaseColumns.java b/core/java/android/provider/BaseColumns.java
index f594c19..00c9e72 100644
--- a/core/java/android/provider/BaseColumns.java
+++ b/core/java/android/provider/BaseColumns.java
@@ -16,17 +16,18 @@
 
 package android.provider;
 
-public interface BaseColumns
-{
+import android.database.Cursor;
+
+public interface BaseColumns {
     /**
      * The unique ID for a row.
-     * <P>Type: INTEGER (long)</P>
      */
+    @Column(Cursor.FIELD_TYPE_INTEGER)
     public static final String _ID = "_id";
 
     /**
      * The count of rows in a directory.
-     * <P>Type: INTEGER</P>
      */
+    // @Column(Cursor.FIELD_TYPE_INTEGER)
     public static final String _COUNT = "_count";
 }
diff --git a/core/java/android/provider/Column.java b/core/java/android/provider/Column.java
new file mode 100644
index 0000000..1364fb8
--- /dev/null
+++ b/core/java/android/provider/Column.java
@@ -0,0 +1,50 @@
+/*
+ * 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.provider;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.database.Cursor;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * Denotes that a field is a {@link ContentProvider} column. It can be used as a
+ * key for {@link ContentValues} when inserting or updating data, or as a
+ * projection when querying.
+ *
+ * @hide
+ */
+@Documented
+@Retention(RUNTIME)
+@Target({FIELD})
+public @interface Column {
+    /**
+     * The {@link Cursor#getType(int)} of the data stored in this column.
+     */
+    int value();
+
+    /**
+     * This column is read-only and cannot be defined during insert or updates.
+     */
+    boolean readOnly() default false;
+}
diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java
index 0b38420..1b10bef 100644
--- a/core/java/android/provider/MediaStore.java
+++ b/core/java/android/provider/MediaStore.java
@@ -857,8 +857,6 @@
          * this path. Instead of trying to open this path directly, apps should
          * use {@link ContentResolver#openFileDescriptor(Uri, String)} to gain
          * access.
-         * <p>
-         * Type: TEXT
          *
          * @deprecated Apps may not have filesystem permissions to directly
          *             access this path. Instead of trying to open this path
@@ -869,6 +867,7 @@
          *             {@link android.os.Build.VERSION_CODES#Q} or higher.
          */
         @Deprecated
+        @Column(Cursor.FIELD_TYPE_STRING)
         public static final String DATA = "_data";
 
         /**
@@ -883,44 +882,44 @@
          * If you require the hash of a specific item, you can call
          * {@link ContentResolver#canonicalize(Uri)}, which will block until the
          * hash is calculated.
-         * <p>
-         * Type: BLOB
+         *
          * @removed
          */
         @Deprecated
+        @Column(value = Cursor.FIELD_TYPE_BLOB, readOnly = true)
         public static final String HASH = "_hash";
 
         /**
          * The size of the file in bytes
-         * <P>Type: INTEGER (long)</P>
          */
+        @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
         public static final String SIZE = "_size";
 
         /**
          * The display name of the file
-         * <P>Type: TEXT</P>
          */
+        @Column(Cursor.FIELD_TYPE_STRING)
         public static final String DISPLAY_NAME = "_display_name";
 
         /**
          * The title of the content
-         * <P>Type: TEXT</P>
          */
+        @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
         public static final String TITLE = "title";
 
         /**
          * The time the file was added to the media provider
          * Units are seconds since 1970.
-         * <P>Type: INTEGER (long)</P>
          */
+        @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
         public static final String DATE_ADDED = "date_added";
 
         /**
          * The time the file was last modified
          * Units are seconds since 1970.
          * NOTE: This is for internal use by the media scanner.  Do not modify this field.
-         * <P>Type: INTEGER (long)</P>
          */
+        @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
         public static final String DATE_MODIFIED = "date_modified";
 
         /**
@@ -938,9 +937,8 @@
          * {@code format} of {@code audio/ogg} would be ignored.
          * <p>
          * This is a read-only column that is automatically computed.
-         * <p>
-         * Type: TEXT
          */
+        @Column(Cursor.FIELD_TYPE_STRING)
         public static final String MIME_TYPE = "mime_type";
 
         /**
@@ -948,35 +946,34 @@
          * Used to pass the new file's object handle through the media scanner
          * from MTP to the media provider
          * For internal use only by MTP, media scanner and media provider.
-         * <P>Type: INTEGER</P>
          * @hide
          */
+        @Deprecated
+        // @Column(Cursor.FIELD_TYPE_INTEGER)
         public static final String MEDIA_SCANNER_NEW_OBJECT_ID = "media_scanner_new_object_id";
 
         /**
          * Non-zero if the media file is drm-protected
-         * <P>Type: INTEGER (boolean)</P>
          * @hide
          */
         @UnsupportedAppUsage
+        @Deprecated
+        @Column(Cursor.FIELD_TYPE_INTEGER)
         public static final String IS_DRM = "is_drm";
 
         /**
          * Flag indicating if a media item is pending, and still being inserted
          * by its owner.
-         * <p>
-         * Type: BOOLEAN
          *
          * @see MediaColumns#IS_PENDING
          * @see MediaStore#setIncludePending(Uri)
          * @see MediaStore#createPending(Context, PendingParams)
          */
+        @Column(Cursor.FIELD_TYPE_INTEGER)
         public static final String IS_PENDING = "is_pending";
 
         /**
          * Flag indicating if a media item is trashed.
-         * <p>
-         * Type: BOOLEAN
          *
          * @see MediaColumns#IS_TRASHED
          * @see MediaStore#setIncludeTrashed(Uri)
@@ -985,57 +982,54 @@
          * @removed
          */
         @Deprecated
+        @Column(Cursor.FIELD_TYPE_INTEGER)
         public static final String IS_TRASHED = "is_trashed";
 
         /**
          * The time the file should be considered expired. Units are seconds
          * since 1970. Typically only meaningful in the context of
          * {@link #IS_PENDING} or {@link #IS_TRASHED}.
-         * <p>
-         * Type: INTEGER
          * @removed
          */
         @Deprecated
+        @Column(Cursor.FIELD_TYPE_INTEGER)
         public static final String DATE_EXPIRES = "date_expires";
 
         /**
          * The width of the image/video in pixels.
          */
+        @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
         public static final String WIDTH = "width";
 
         /**
          * The height of the image/video in pixels.
          */
+        @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
         public static final String HEIGHT = "height";
 
         /**
          * Package name that contributed this media. The value may be
          * {@code NULL} if ownership cannot be reliably determined.
-         * <p>
-         * This is a read-only column that is automatically computed.
-         * <p>
-         * Type: TEXT
          */
+        @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
         public static final String OWNER_PACKAGE_NAME = "owner_package_name";
 
         /**
          * The primary directory name this media exists under. The value may be
          * {@code NULL} if the media doesn't have a primary directory name.
-         * <p>
-         * Type: TEXT
          *
          * @see PendingParams#setPrimaryDirectory(String)
          */
+        @Column(Cursor.FIELD_TYPE_STRING)
         public static final String PRIMARY_DIRECTORY = "primary_directory";
 
         /**
          * The secondary directory name this media exists under. The value may
          * be {@code NULL} if the media doesn't have a secondary directory name.
-         * <p>
-         * Type: TEXT
          *
          * @see PendingParams#setSecondaryDirectory(String)
          */
+        @Column(Cursor.FIELD_TYPE_STRING)
         public static final String SECONDARY_DIRECTORY = "secondary_directory";
 
         /**
@@ -1046,11 +1040,8 @@
          * <p>
          * Each "document ID" is created once for each new resource. Different
          * renditions of that resource are expected to have different IDs.
-         * <p>
-         * This is a read-only column that is automatically computed.
-         * <p>
-         * Type: TEXT
          */
+        @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
         public static final String DOCUMENT_ID = "document_id";
 
         /**
@@ -1061,11 +1052,8 @@
          * <p>
          * This "instance ID" changes with each save operation of a specific
          * "document ID".
-         * <p>
-         * This is a read-only column that is automatically computed.
-         * <p>
-         * Type: TEXT
          */
+        @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
         public static final String INSTANCE_ID = "instance_id";
 
         /**
@@ -1077,11 +1065,8 @@
          * For example, when you save a PSD document as a JPEG, then convert the
          * JPEG to GIF format, the "original document ID" of both the JPEG and
          * GIF files is the "document ID" of the original PSD file.
-         * <p>
-         * This is a read-only column that is automatically computed.
-         * <p>
-         * Type: TEXT
          */
+        @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
         public static final String ORIGINAL_DOCUMENT_ID = "original_document_id";
     }
 
@@ -1172,43 +1157,46 @@
         public interface FileColumns extends MediaColumns {
             /**
              * The MTP storage ID of the file
-             * <P>Type: INTEGER</P>
              * @hide
              */
             @UnsupportedAppUsage
+            @Deprecated
+            // @Column(Cursor.FIELD_TYPE_INTEGER)
             public static final String STORAGE_ID = "storage_id";
 
             /**
              * The MTP format code of the file
-             * <P>Type: INTEGER</P>
              * @hide
              */
             @UnsupportedAppUsage
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String FORMAT = "format";
 
             /**
              * The index of the parent directory of the file
-             * <P>Type: INTEGER</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String PARENT = "parent";
 
             /**
              * The MIME type of the file
              * <P>Type: TEXT</P>
              */
+            @Column(Cursor.FIELD_TYPE_STRING)
             public static final String MIME_TYPE = "mime_type";
 
             /**
              * The title of the content
              * <P>Type: TEXT</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
             public static final String TITLE = "title";
 
             /**
              * The media type (audio, video, image or playlist)
              * of the file, or 0 for not a media file
-             * <P>Type: TEXT</P>
              */
+            @Column(Cursor.FIELD_TYPE_INTEGER)
             public static final String MEDIA_TYPE = "media_type";
 
             /**
@@ -1241,6 +1229,7 @@
              * Column indicating if the file is part of Downloads collection.
              * @hide
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String IS_DOWNLOAD = "is_download";
         }
     }
@@ -1260,23 +1249,20 @@
     public interface DownloadColumns extends MediaColumns {
         /**
          * Uri indicating where the file has been downloaded from.
-         * <p>
-         * Type: TEXT
          */
+        @Column(Cursor.FIELD_TYPE_STRING)
         String DOWNLOAD_URI = "download_uri";
 
         /**
          * Uri indicating HTTP referer of {@link #DOWNLOAD_URI}.
-         * <p>
-         * Type: TEXT
          */
+        @Column(Cursor.FIELD_TYPE_STRING)
         String REFERER_URI = "referer_uri";
 
         /**
          * The description of the download.
-         * <p>
-         * Type: Text
          */
+        @Column(Cursor.FIELD_TYPE_STRING)
         String DESCRIPTION = "description";
     }
 
@@ -1442,29 +1428,28 @@
         public interface ImageColumns extends MediaColumns {
             /**
              * The description of the image
-             * <P>Type: TEXT</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
             public static final String DESCRIPTION = "description";
 
             /**
              * The picasa id of the image
-             * <P>Type: TEXT</P>
              *
              * @deprecated this value was only relevant for images hosted on
              *             Picasa, which are no longer supported.
              */
             @Deprecated
+            @Column(Cursor.FIELD_TYPE_STRING)
             public static final String PICASA_ID = "picasa_id";
 
             /**
              * Whether the video should be published as public or private
-             * <P>Type: INTEGER</P>
              */
+            @Column(Cursor.FIELD_TYPE_INTEGER)
             public static final String IS_PRIVATE = "isprivate";
 
             /**
              * The latitude where the image was captured.
-             * <P>Type: DOUBLE</P>
              *
              * @deprecated location details are no longer indexed for privacy
              *             reasons, and this value is now always {@code null}.
@@ -1472,11 +1457,11 @@
              *             {@link ExifInterface#getLatLong(float[])}.
              */
             @Deprecated
+            @Column(value = Cursor.FIELD_TYPE_FLOAT, readOnly = true)
             public static final String LATITUDE = "latitude";
 
             /**
              * The longitude where the image was captured.
-             * <P>Type: DOUBLE</P>
              *
              * @deprecated location details are no longer indexed for privacy
              *             reasons, and this value is now always {@code null}.
@@ -1484,40 +1469,40 @@
              *             {@link ExifInterface#getLatLong(float[])}.
              */
             @Deprecated
+            @Column(value = Cursor.FIELD_TYPE_FLOAT, readOnly = true)
             public static final String LONGITUDE = "longitude";
 
             /**
              * The date & time that the image was taken in units
              * of milliseconds since jan 1, 1970.
-             * <P>Type: INTEGER</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String DATE_TAKEN = "datetaken";
 
             /**
              * The orientation for the image expressed as degrees.
              * Only degrees 0, 90, 180, 270 will work.
-             * <P>Type: INTEGER</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String ORIENTATION = "orientation";
 
             /**
              * The mini thumb id.
-             * <P>Type: INTEGER</P>
              *
              * @deprecated all thumbnails should be obtained via
              *             {@link MediaStore.Images.Thumbnails#getThumbnail}, as this
              *             value is no longer supported.
              */
             @Deprecated
+            @Column(Cursor.FIELD_TYPE_INTEGER)
             public static final String MINI_THUMB_MAGIC = "mini_thumb_magic";
 
             /**
              * The primary bucket ID of this media item. This can be useful to
              * present the user a first-level clustering of related media items.
              * This is a read-only column that is automatically computed.
-             * <p>
-             * Type: INTEGER
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String BUCKET_ID = "bucket_id";
 
             /**
@@ -1525,9 +1510,8 @@
              * useful to present the user a first-level clustering of related
              * media items. This is a read-only column that is automatically
              * computed.
-             * <p>
-             * Type: TEXT
              */
+            @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
             public static final String BUCKET_DISPLAY_NAME = "bucket_display_name";
 
             /**
@@ -1541,9 +1525,8 @@
              * {@code IMG1024.BURST001.JPG} and {@code IMG1024.BURST002.JPG}
              * will have the same {@link #GROUP_ID} because the first portion of
              * their filenames is identical.
-             * <p>
-             * Type: INTEGER
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String GROUP_ID = "group_id";
         }
 
@@ -1848,8 +1831,6 @@
              * apps should use
              * {@link ContentResolver#openFileDescriptor(Uri, String)} to gain
              * access.
-             * <p>
-             * Type: TEXT
              *
              * @deprecated Apps may not have filesystem permissions to directly
              *             access this path. Instead of trying to open this path
@@ -1860,39 +1841,45 @@
              *             {@link android.os.Build.VERSION_CODES#Q} or higher.
              */
             @Deprecated
+            @Column(Cursor.FIELD_TYPE_STRING)
             public static final String DATA = "_data";
 
             /**
              * The original image for the thumbnal
-             * <P>Type: INTEGER (ID from Images table)</P>
              */
+            @Column(Cursor.FIELD_TYPE_INTEGER)
             public static final String IMAGE_ID = "image_id";
 
             /**
              * The kind of the thumbnail
-             * <P>Type: INTEGER (One of the values below)</P>
              */
+            @Column(Cursor.FIELD_TYPE_INTEGER)
             public static final String KIND = "kind";
 
             public static final int MINI_KIND = ThumbnailConstants.MINI_KIND;
             public static final int FULL_SCREEN_KIND = ThumbnailConstants.FULL_SCREEN_KIND;
             public static final int MICRO_KIND = ThumbnailConstants.MICRO_KIND;
+
             /**
              * The blob raw data of thumbnail
-             * <P>Type: DATA STREAM</P>
+             *
+             * @deprecated this column never existed internally, and could never
+             *             have returned valid data.
              */
+            @Deprecated
+            @Column(Cursor.FIELD_TYPE_BLOB)
             public static final String THUMB_DATA = "thumb_data";
 
             /**
              * The width of the thumbnal
-             * <P>Type: INTEGER (long)</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String WIDTH = "width";
 
             /**
              * The height of the thumbnail
-             * <P>Type: INTEGER (long)</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String HEIGHT = "height";
         }
     }
@@ -1909,79 +1896,80 @@
             /**
              * A non human readable key calculated from the TITLE, used for
              * searching, sorting and grouping
-             * <P>Type: TEXT</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
             public static final String TITLE_KEY = "title_key";
 
             /**
              * The duration of the audio file, in ms
-             * <P>Type: INTEGER (long)</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String DURATION = "duration";
 
             /**
              * The position, in ms, playback was at when playback for this file
              * was last stopped.
-             * <P>Type: INTEGER (long)</P>
              */
+            @Column(Cursor.FIELD_TYPE_INTEGER)
             public static final String BOOKMARK = "bookmark";
 
             /**
              * The id of the artist who created the audio file, if any
-             * <P>Type: INTEGER (long)</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String ARTIST_ID = "artist_id";
 
             /**
              * The artist who created the audio file, if any
-             * <P>Type: TEXT</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
             public static final String ARTIST = "artist";
 
             /**
              * The artist credited for the album that contains the audio file
-             * <P>Type: TEXT</P>
              * @hide
              */
+            @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
             public static final String ALBUM_ARTIST = "album_artist";
 
             /**
              * Whether the song is part of a compilation
-             * <P>Type: TEXT</P>
              * @hide
              */
+            @Deprecated
+            // @Column(Cursor.FIELD_TYPE_STRING)
             public static final String COMPILATION = "compilation";
 
             /**
              * A non human readable key calculated from the ARTIST, used for
              * searching, sorting and grouping
-             * <P>Type: TEXT</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
             public static final String ARTIST_KEY = "artist_key";
 
             /**
              * The composer of the audio file, if any
-             * <P>Type: TEXT</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
             public static final String COMPOSER = "composer";
 
             /**
              * The id of the album the audio file is from, if any
-             * <P>Type: INTEGER (long)</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String ALBUM_ID = "album_id";
 
             /**
              * The album the audio file is from, if any
-             * <P>Type: TEXT</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
             public static final String ALBUM = "album";
 
             /**
              * A non human readable key calculated from the ALBUM, used for
              * searching, sorting and grouping
-             * <P>Type: TEXT</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
             public static final String ALBUM_KEY = "album_key";
 
             /**
@@ -1990,63 +1978,63 @@
              * disc number. For multi-disc sets, this number will
              * be 1xxx for tracks on the first disc, 2xxx for tracks
              * on the second disc, etc.
-             * <P>Type: INTEGER</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String TRACK = "track";
 
             /**
              * The year the audio file was recorded, if any
-             * <P>Type: INTEGER</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String YEAR = "year";
 
             /**
              * Non-zero if the audio file is music
-             * <P>Type: INTEGER (boolean)</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String IS_MUSIC = "is_music";
 
             /**
              * Non-zero if the audio file is a podcast
-             * <P>Type: INTEGER (boolean)</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String IS_PODCAST = "is_podcast";
 
             /**
              * Non-zero if the audio file may be a ringtone
-             * <P>Type: INTEGER (boolean)</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String IS_RINGTONE = "is_ringtone";
 
             /**
              * Non-zero if the audio file may be an alarm
-             * <P>Type: INTEGER (boolean)</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String IS_ALARM = "is_alarm";
 
             /**
              * Non-zero if the audio file may be a notification sound
-             * <P>Type: INTEGER (boolean)</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String IS_NOTIFICATION = "is_notification";
 
             /**
              * Non-zero if the audio file is an audiobook
-             * <P>Type: INTEGER (boolean)</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String IS_AUDIOBOOK = "is_audiobook";
 
             /**
              * The genre of the audio file, if any
-             * <P>Type: TEXT</P>
              * Does not exist in the database - only used by the media scanner for inserts.
              * @hide
              */
+            @Deprecated
+            // @Column(Cursor.FIELD_TYPE_STRING)
             public static final String GENRE = "genre";
 
             /**
              * The resource URI of a localized title, if any
-             * <P>Type: TEXT</P>
              * Conforms to this pattern:
              *   Scheme: {@link ContentResolver.SCHEME_ANDROID_RESOURCE}
              *   Authority: Package Name of ringtone title provider
@@ -2054,6 +2042,7 @@
              *   Second Path Segment: Resource ID of title
              * @hide
              */
+            @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
             public static final String TITLE_RESOURCE_URI = "title_resource_uri";
         }
 
@@ -2142,6 +2131,7 @@
              * @deprecated Apps may not have filesystem permissions to directly
              *             access this path.
              */
+            @Deprecated
             public static @Nullable Uri getContentUriForPath(@NonNull String path) {
                 return getContentUri(getVolumeName(new File(path)));
             }
@@ -2202,8 +2192,8 @@
         public interface GenresColumns {
             /**
              * The name of the genre
-             * <P>Type: TEXT</P>
              */
+            @Column(Cursor.FIELD_TYPE_STRING)
             public static final String NAME = "name";
         }
 
@@ -2287,14 +2277,14 @@
 
                 /**
                  * The ID of the audio file
-                 * <P>Type: INTEGER (long)</P>
                  */
+                @Column(Cursor.FIELD_TYPE_INTEGER)
                 public static final String AUDIO_ID = "audio_id";
 
                 /**
                  * The ID of the genre
-                 * <P>Type: INTEGER (long)</P>
                  */
+                @Column(Cursor.FIELD_TYPE_INTEGER)
                 public static final String GENRE_ID = "genre_id";
             }
         }
@@ -2305,8 +2295,8 @@
         public interface PlaylistsColumns {
             /**
              * The name of the playlist
-             * <P>Type: TEXT</P>
              */
+            @Column(Cursor.FIELD_TYPE_STRING)
             public static final String NAME = "name";
 
             /**
@@ -2317,8 +2307,6 @@
              * apps should use
              * {@link ContentResolver#openFileDescriptor(Uri, String)} to gain
              * access.
-             * <p>
-             * Type: TEXT
              *
              * @deprecated Apps may not have filesystem permissions to directly
              *             access this path. Instead of trying to open this path
@@ -2329,21 +2317,22 @@
              *             {@link android.os.Build.VERSION_CODES#Q} or higher.
              */
             @Deprecated
+            @Column(Cursor.FIELD_TYPE_STRING)
             public static final String DATA = "_data";
 
             /**
              * The time the file was added to the media provider
              * Units are seconds since 1970.
-             * <P>Type: INTEGER (long)</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String DATE_ADDED = "date_added";
 
             /**
              * The time the file was last modified
              * Units are seconds since 1970.
              * NOTE: This is for internal use by the media scanner.  Do not modify this field.
-             * <P>Type: INTEGER (long)</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String DATE_MODIFIED = "date_modified";
         }
 
@@ -2426,6 +2415,7 @@
                 /**
                  * The ID within the playlist.
                  */
+                @Column(Cursor.FIELD_TYPE_INTEGER)
                 public static final String _ID = "_id";
 
                 /**
@@ -2436,20 +2426,20 @@
 
                 /**
                  * The ID of the audio file
-                 * <P>Type: INTEGER (long)</P>
                  */
+                @Column(Cursor.FIELD_TYPE_INTEGER)
                 public static final String AUDIO_ID = "audio_id";
 
                 /**
                  * The ID of the playlist
-                 * <P>Type: INTEGER (long)</P>
                  */
+                @Column(Cursor.FIELD_TYPE_INTEGER)
                 public static final String PLAYLIST_ID = "playlist_id";
 
                 /**
                  * The order of the songs in the playlist
-                 * <P>Type: INTEGER (long)></P>
                  */
+                @Column(Cursor.FIELD_TYPE_INTEGER)
                 public static final String PLAY_ORDER = "play_order";
 
                 /**
@@ -2465,25 +2455,27 @@
         public interface ArtistColumns {
             /**
              * The artist who created the audio file, if any
-             * <P>Type: TEXT</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
             public static final String ARTIST = "artist";
 
             /**
              * A non human readable key calculated from the ARTIST, used for
              * searching, sorting and grouping
-             * <P>Type: TEXT</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
             public static final String ARTIST_KEY = "artist_key";
 
             /**
              * The number of albums in the database for this artist
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String NUMBER_OF_ALBUMS = "number_of_albums";
 
             /**
              * The number of albums in the database for this artist
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String NUMBER_OF_TRACKS = "number_of_tracks";
         }
 
@@ -2551,34 +2543,34 @@
 
             /**
              * The id for the album
-             * <P>Type: INTEGER</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String ALBUM_ID = "album_id";
 
             /**
              * The album on which the audio file appears, if any
-             * <P>Type: TEXT</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
             public static final String ALBUM = "album";
 
             /**
              * The artist whose songs appear on this album
-             * <P>Type: TEXT</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
             public static final String ARTIST = "artist";
 
             /**
              * The number of songs on this album
-             * <P>Type: INTEGER</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String NUMBER_OF_SONGS = "numsongs";
 
             /**
              * This column is available when getting album info via artist,
              * and indicates the number of songs on the album by the given
              * artist.
-             * <P>Type: INTEGER</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String NUMBER_OF_SONGS_FOR_ARTIST = "numsongs_by_artist";
 
             /**
@@ -2586,8 +2578,8 @@
              * on this album were released. This will often
              * be the same as {@link #LAST_YEAR}, but for compilation albums
              * they might differ.
-             * <P>Type: INTEGER</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String FIRST_YEAR = "minyear";
 
             /**
@@ -2595,20 +2587,19 @@
              * on this album were released. This will often
              * be the same as {@link #FIRST_YEAR}, but for compilation albums
              * they might differ.
-             * <P>Type: INTEGER</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String LAST_YEAR = "maxyear";
 
             /**
              * A non human readable key calculated from the ALBUM, used for
              * searching, sorting and grouping
-             * <P>Type: TEXT</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
             public static final String ALBUM_KEY = "album_key";
 
             /**
              * Cached album art.
-             * <P>Type: TEXT</P>
              *
              * @deprecated Apps may not have filesystem permissions to directly
              *             access this path. Instead of trying to open this path
@@ -2619,6 +2610,7 @@
              *             {@link android.os.Build.VERSION_CODES#Q} or higher.
              */
             @Deprecated
+            @Column(Cursor.FIELD_TYPE_STRING)
             public static final String ALBUM_ART = "album_art";
         }
 
@@ -2676,6 +2668,43 @@
             // Not instantiable.
             private Radio() { }
         }
+
+        /**
+         * This class provides utility methods to obtain thumbnails for various
+         * {@link Audio} items.
+         *
+         * @deprecated Callers should migrate to using
+         *             {@link ContentResolver#loadThumbnail}, since it offers
+         *             richer control over requested thumbnail sizes and
+         *             cancellation behavior.
+         * @hide
+         */
+        @Deprecated
+        public static class Thumbnails implements BaseColumns {
+            /**
+             * Path to the thumbnail file on disk.
+             * <p>
+             * Note that apps may not have filesystem permissions to directly
+             * access this path. Instead of trying to open this path directly,
+             * apps should use
+             * {@link ContentResolver#openFileDescriptor(Uri, String)} to gain
+             * access.
+             *
+             * @deprecated Apps may not have filesystem permissions to directly
+             *             access this path. Instead of trying to open this path
+             *             directly, apps should use
+             *             {@link ContentResolver#loadThumbnail}
+             *             to gain access. This value will always be
+             *             {@code NULL} for apps targeting
+             *             {@link android.os.Build.VERSION_CODES#Q} or higher.
+             */
+            @Deprecated
+            @Column(Cursor.FIELD_TYPE_STRING)
+            public static final String DATA = "_data";
+
+            @Column(Cursor.FIELD_TYPE_INTEGER)
+            public static final String ALBUM_ID = "album_id";
+        }
     }
 
     public static final class Video {
@@ -2693,61 +2722,60 @@
 
             /**
              * The duration of the video file, in ms
-             * <P>Type: INTEGER (long)</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String DURATION = "duration";
 
             /**
              * The artist who created the video file, if any
-             * <P>Type: TEXT</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
             public static final String ARTIST = "artist";
 
             /**
              * The album the video file is from, if any
-             * <P>Type: TEXT</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
             public static final String ALBUM = "album";
 
             /**
              * The resolution of the video file, formatted as "XxY"
-             * <P>Type: TEXT</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
             public static final String RESOLUTION = "resolution";
 
             /**
              * The description of the video recording
-             * <P>Type: TEXT</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
             public static final String DESCRIPTION = "description";
 
             /**
              * Whether the video should be published as public or private
-             * <P>Type: INTEGER</P>
              */
+            @Column(Cursor.FIELD_TYPE_INTEGER)
             public static final String IS_PRIVATE = "isprivate";
 
             /**
              * The user-added tags associated with a video
-             * <P>Type: TEXT</P>
              */
+            @Column(Cursor.FIELD_TYPE_STRING)
             public static final String TAGS = "tags";
 
             /**
              * The YouTube category of the video
-             * <P>Type: TEXT</P>
              */
+            @Column(Cursor.FIELD_TYPE_STRING)
             public static final String CATEGORY = "category";
 
             /**
              * The language of the video
-             * <P>Type: TEXT</P>
              */
+            @Column(Cursor.FIELD_TYPE_STRING)
             public static final String LANGUAGE = "language";
 
             /**
              * The latitude where the video was captured.
-             * <P>Type: DOUBLE</P>
              *
              * @deprecated location details are no longer indexed for privacy
              *             reasons, and this value is now always {@code null}.
@@ -2755,11 +2783,11 @@
              *             {@link ExifInterface#getLatLong(float[])}.
              */
             @Deprecated
+            @Column(value = Cursor.FIELD_TYPE_FLOAT, readOnly = true)
             public static final String LATITUDE = "latitude";
 
             /**
              * The longitude where the video was captured.
-             * <P>Type: DOUBLE</P>
              *
              * @deprecated location details are no longer indexed for privacy
              *             reasons, and this value is now always {@code null}.
@@ -2767,33 +2795,33 @@
              *             {@link ExifInterface#getLatLong(float[])}.
              */
             @Deprecated
+            @Column(value = Cursor.FIELD_TYPE_FLOAT, readOnly = true)
             public static final String LONGITUDE = "longitude";
 
             /**
              * The date & time that the video was taken in units
              * of milliseconds since jan 1, 1970.
-             * <P>Type: INTEGER</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String DATE_TAKEN = "datetaken";
 
             /**
              * The mini thumb id.
-             * <P>Type: INTEGER</P>
              *
              * @deprecated all thumbnails should be obtained via
              *             {@link MediaStore.Images.Thumbnails#getThumbnail}, as this
              *             value is no longer supported.
              */
             @Deprecated
+            @Column(Cursor.FIELD_TYPE_INTEGER)
             public static final String MINI_THUMB_MAGIC = "mini_thumb_magic";
 
             /**
              * The primary bucket ID of this media item. This can be useful to
              * present the user a first-level clustering of related media items.
              * This is a read-only column that is automatically computed.
-             * <p>
-             * Type: INTEGER
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String BUCKET_ID = "bucket_id";
 
             /**
@@ -2801,9 +2829,8 @@
              * useful to present the user a first-level clustering of related
              * media items. This is a read-only column that is automatically
              * computed.
-             * <p>
-             * Type: TEXT
              */
+            @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
             public static final String BUCKET_DISPLAY_NAME = "bucket_display_name";
 
             /**
@@ -2817,9 +2844,8 @@
              * {@code IMG1024.BURST001.JPG} and {@code IMG1024.BURST002.JPG}
              * will have the same {@link #GROUP_ID} because the first portion of
              * their filenames is identical.
-             * <p>
-             * Type: INTEGER
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String GROUP_ID = "group_id";
 
             /**
@@ -2827,29 +2853,29 @@
              * video should start playing at the next time it is opened. If the value is null or
              * out of the range 0..DURATION-1 then the video should start playing from the
              * beginning.
-             * <P>Type: INTEGER</P>
              */
+            @Column(Cursor.FIELD_TYPE_INTEGER)
             public static final String BOOKMARK = "bookmark";
 
             /**
              * The standard of color aspects
-             * <P>Type: INTEGER</P>
              * @hide
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String COLOR_STANDARD = "color_standard";
 
             /**
              * The transfer of color aspects
-             * <P>Type: INTEGER</P>
              * @hide
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String COLOR_TRANSFER = "color_transfer";
 
             /**
              * The range of color aspects
-             * <P>Type: INTEGER</P>
              * @hide
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String COLOR_RANGE = "color_range";
         }
 
@@ -3016,8 +3042,6 @@
 
             /**
              * Path to the thumbnail file on disk.
-             * <p>
-             * Type: TEXT
              *
              * @deprecated Apps may not have filesystem permissions to directly
              *             access this path. Instead of trying to open this path
@@ -3028,18 +3052,19 @@
              *             {@link android.os.Build.VERSION_CODES#Q} or higher.
              */
             @Deprecated
+            @Column(Cursor.FIELD_TYPE_STRING)
             public static final String DATA = "_data";
 
             /**
              * The original image for the thumbnal
-             * <P>Type: INTEGER (ID from Video table)</P>
              */
+            @Column(Cursor.FIELD_TYPE_INTEGER)
             public static final String VIDEO_ID = "video_id";
 
             /**
              * The kind of the thumbnail
-             * <P>Type: INTEGER (One of the values below)</P>
              */
+            @Column(Cursor.FIELD_TYPE_INTEGER)
             public static final String KIND = "kind";
 
             public static final int MINI_KIND = ThumbnailConstants.MINI_KIND;
@@ -3048,14 +3073,14 @@
 
             /**
              * The width of the thumbnal
-             * <P>Type: INTEGER (long)</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String WIDTH = "width";
 
             /**
              * The height of the thumbnail
-             * <P>Type: INTEGER (long)</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String HEIGHT = "height";
         }
     }
diff --git a/core/java/android/service/notification/Adjustment.java b/core/java/android/service/notification/Adjustment.java
index 93189b3..2961426 100644
--- a/core/java/android/service/notification/Adjustment.java
+++ b/core/java/android/service/notification/Adjustment.java
@@ -15,6 +15,7 @@
  */
 package android.service.notification;
 
+import android.annotation.SystemApi;
 import android.app.Notification;
 import android.os.Bundle;
 import android.os.Parcel;
@@ -36,13 +37,13 @@
      * See {@link android.app.Notification.Builder#addPerson(String)}.
      * @hide
      */
+    @SystemApi
     public static final String KEY_PEOPLE = "key_people";
     /**
      * Parcelable {@code ArrayList} of {@link SnoozeCriterion}. These criteria may be visible to
      * users. If a user chooses to snooze a notification until one of these criterion, the
      * assistant will be notified via
      * {@link NotificationAssistantService#onNotificationSnoozedUntilContext}.
-     * @hide
      */
     public static final String KEY_SNOOZE_CRITERIA = "key_snooze_criteria";
     /**
@@ -111,7 +112,11 @@
         mUser = user;
     }
 
-    private Adjustment(Parcel in) {
+    /**
+     * @hide
+     */
+    @SystemApi
+    protected Adjustment(Parcel in) {
         if (in.readInt() == 1) {
             mPackage = in.readString();
         } else {
diff --git a/core/java/android/service/notification/NotificationAssistantService.java b/core/java/android/service/notification/NotificationAssistantService.java
index e93b158..a697248 100644
--- a/core/java/android/service/notification/NotificationAssistantService.java
+++ b/core/java/android/service/notification/NotificationAssistantService.java
@@ -21,6 +21,7 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.SdkConstant;
+import android.annotation.SystemApi;
 import android.app.Notification;
 import android.app.NotificationChannel;
 import android.app.admin.DevicePolicyManager;
@@ -103,7 +104,6 @@
      *
      * @param sbn the notification to snooze
      * @param snoozeCriterionId the {@link SnoozeCriterion#getId()} representing a device context.
-     * @hide
      */
     abstract public void onNotificationSnoozedUntilContext(StatusBarNotification sbn,
             String snoozeCriterionId);
@@ -111,12 +111,13 @@
     /**
      * A notification was posted by an app. Called before post.
      *
+     * <p>Note: this method is only called if you don't override
+     * {@link #onNotificationEnqueued(StatusBarNotification, NotificationChannel)}.</p>
+     *
      * @param sbn the new notification
      * @return an adjustment or null to take no action, within 100ms.
      */
-    public Adjustment onNotificationEnqueued(StatusBarNotification sbn) {
-        return null;
-    }
+    abstract public Adjustment onNotificationEnqueued(StatusBarNotification sbn);
 
     /**
      * A notification was posted by an app. Called before post.
@@ -240,12 +241,11 @@
     /**
      * Inform the notification manager about un-snoozing a specific notification.
      * <p>
-     * This should only be used for notifications snoozed by this listener using
-     * {@link #snoozeNotification(String, String)}. Once un-snoozed, you will get a
+     * This should only be used for notifications snoozed because of a contextual snooze suggestion
+     * you provided via {@link Adjustment#KEY_SNOOZE_CRITERIA}. Once un-snoozed, you will get a
      * {@link #onNotificationPosted(StatusBarNotification, RankingMap)} callback for the
      * notification.
      * @param key The key of the notification to snooze
-     * @hide
      */
     public final void unsnoozeNotification(String key) {
         if (!isBound()) return;
diff --git a/core/java/android/service/notification/NotificationStats.java b/core/java/android/service/notification/NotificationStats.java
index 814b477..ebfabbf 100644
--- a/core/java/android/service/notification/NotificationStats.java
+++ b/core/java/android/service/notification/NotificationStats.java
@@ -16,6 +16,7 @@
 package android.service.notification;
 
 import android.annotation.IntDef;
+import android.annotation.SystemApi;
 import android.app.RemoteInput;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -98,7 +99,11 @@
     public NotificationStats() {
     }
 
-    private NotificationStats(Parcel in) {
+    /**
+     * @hide
+     */
+    @SystemApi
+    protected NotificationStats(Parcel in) {
         mSeen = in.readByte() != 0;
         mExpanded = in.readByte() != 0;
         mDirectReplied = in.readByte() != 0;
diff --git a/core/java/com/android/internal/os/BackgroundThread.java b/core/java/com/android/internal/os/BackgroundThread.java
index eada142..22c832e 100644
--- a/core/java/com/android/internal/os/BackgroundThread.java
+++ b/core/java/com/android/internal/os/BackgroundThread.java
@@ -17,10 +17,13 @@
 package com.android.internal.os;
 
 import android.os.Handler;
+import android.os.HandlerExecutor;
 import android.os.HandlerThread;
 import android.os.Looper;
 import android.os.Trace;
 
+import java.util.concurrent.Executor;
+
 /**
  * Shared singleton background thread for each process.
  */
@@ -29,6 +32,7 @@
     private static final long SLOW_DELIVERY_THRESHOLD_MS = 30_000;
     private static BackgroundThread sInstance;
     private static Handler sHandler;
+    private static HandlerExecutor sHandlerExecutor;
 
     private BackgroundThread() {
         super("android.bg", android.os.Process.THREAD_PRIORITY_BACKGROUND);
@@ -43,6 +47,7 @@
             looper.setSlowLogThresholdMs(
                     SLOW_DISPATCH_THRESHOLD_MS, SLOW_DELIVERY_THRESHOLD_MS);
             sHandler = new Handler(sInstance.getLooper());
+            sHandlerExecutor = new HandlerExecutor(sHandler);
         }
     }
 
@@ -59,4 +64,11 @@
             return sHandler;
         }
     }
+
+    public static Executor getExecutor() {
+        synchronized (BackgroundThread.class) {
+            ensureThreadLocked();
+            return sHandlerExecutor;
+        }
+    }
 }
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index a2ed7d3..bd998999 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -1918,7 +1918,6 @@
     for (jint i = 0; i < nb; i++) {
         deviceTypesVector.push_back((audio_devices_t) typesPtr[i]);
     }
-    env->ReleaseIntArrayElements(deviceTypes, typesPtr, 0);
 
     // check each address is a string and add device type/address to list for device affinity
     Vector<AudioDeviceTypeAddr> deviceVector;
@@ -1932,6 +1931,7 @@
         AudioDeviceTypeAddr dev = AudioDeviceTypeAddr(typesPtr[i], address);
         deviceVector.add(dev);
     }
+    env->ReleaseIntArrayElements(deviceTypes, typesPtr, 0);
 
     status_t status = AudioSystem::setUidDeviceAffinities((uid_t) uid, deviceVector);
     return (jint) nativeToJavaStatus(status);
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 5cecf66a..b36c85d 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -1384,8 +1384,8 @@
       RuntimeAbort(env, __LINE__, "Bad gids array");
     }
 
-    for (int gid_index = gids_num; --gids_num >= 0;) {
-      if (native_gid_proxy[gid_index] == AID_WAKELOCK) {
+    for (int gids_index = 0; gids_index < gids_num; ++gids_index) {
+      if (native_gid_proxy[gids_index] == AID_WAKELOCK) {
         gid_wakelock_found = true;
         break;
       }
diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h
index 742813e..875b90b 100644
--- a/libs/androidfw/include/androidfw/ResourceTypes.h
+++ b/libs/androidfw/include/androidfw/ResourceTypes.h
@@ -1647,6 +1647,10 @@
     // The overlay must reside of the product partition or must have existed on the product
     // partition before an upgrade to overlay these resources.
     POLICY_PRODUCT_PARTITION = 0x00000008,
+
+    // The overlay must be signed with the same signature as the actor of the target resource,
+    // which can be separate or the same as the target package with the resource.
+    POLICY_SIGNATURE = 0x00000010,
   };
   uint32_t policy_flags;
 
diff --git a/libs/incident/proto/android/os/header.proto b/libs/incident/proto/android/os/header.proto
index a84dc48..d463f87 100644
--- a/libs/incident/proto/android/os/header.proto
+++ b/libs/incident/proto/android/os/header.proto
@@ -37,4 +37,9 @@
       optional int64 id = 2; // The unique id of the statsd config.
     }
     optional StatsdConfigKey config_key = 3;
+
+    // Details about the trigger. com.android.os.AlertTriggerDetails
+    // Only use bytes type here to avoid indirect dependency on atoms.proto
+    // And this header passes through incidentd without incidentd parsing it.
+    optional bytes trigger_details = 4;
 }
diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl
index 1aeefb8..57a0a72 100644
--- a/location/java/android/location/ILocationManager.aidl
+++ b/location/java/android/location/ILocationManager.aidl
@@ -114,6 +114,7 @@
     // for reporting callback completion
     void locationCallbackFinished(ILocationListener listener);
 
-    // used by gts tests to verify throttling whitelist
+    // used by gts tests to verify whitelists
     String[] getBackgroundThrottlingWhitelist();
+    String[] getIgnoreSettingsWhitelist();
 }
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index c027fd4..586ee2a 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -414,6 +414,18 @@
     }
 
     /**
+     * @hide
+     */
+    @TestApi
+    public String[] getIgnoreSettingsWhitelist() {
+        try {
+            return mService.getIgnoreSettingsWhitelist();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * @hide - hide this constructor because it has a parameter
      * of type ILocationManager, which is a system private class. The
      * right way to create an instance of this class is using the
diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java
index 92afe7e..24a3a9b 100644
--- a/media/java/android/media/AudioRecord.java
+++ b/media/java/android/media/AudioRecord.java
@@ -17,6 +17,7 @@
 package android.media;
 
 import android.annotation.CallbackExecutor;
+import android.annotation.FloatRange;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -1713,12 +1714,11 @@
     /**
      * Specifies the logical microphone (for processing).
      *
-     * @param direction Direction constant (MicrophoneDirection.MIC_DIRECTION_*)
-     * @return retval OK if the call is successful, an error code otherwise.
-     * @hide
+     * @param direction Direction constant.
+     * @return true if sucessful.
      */
-    public int setMicrophoneDirection(int direction) {
-        return native_set_microphone_direction(direction);
+    public boolean setMicrophoneDirection(int direction) {
+        return native_set_microphone_direction(direction) == 0;
     }
 
     /**
@@ -1727,11 +1727,10 @@
      *
      * @param zoom the desired field dimension of microphone capture. Range is from -1 (wide angle),
      * though 0 (no zoom) to 1 (maximum zoom).
-     * @return retval OK if the call is successful, an error code otherwise.
-     * @hide
+     * @return true if sucessful.
      */
-    public int setMicrophoneFieldDimension(float zoom) {
-        return native_set_microphone_field_dimension(zoom);
+    public boolean setMicrophoneFieldDimension(@FloatRange(from = -1.0, to = 1.0) float zoom) {
+        return native_set_microphone_field_dimension(zoom) == 0;
     }
 
     //---------------------------------------------------------
diff --git a/media/java/android/media/MicrophoneDirection.java b/media/java/android/media/MicrophoneDirection.java
index 99201c0..489e268 100644
--- a/media/java/android/media/MicrophoneDirection.java
+++ b/media/java/android/media/MicrophoneDirection.java
@@ -16,38 +16,46 @@
 
 package android.media;
 
-/**
- * @hide
- */
+import android.annotation.FloatRange;
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
 public interface MicrophoneDirection {
     /**
-     * @hide
+     * Don't do any directionality processing of the activated microphone(s).
      */
     int MIC_DIRECTION_UNSPECIFIED = 0;
-
     /**
-     * @hide
+     * Optimize capture for audio coming from the screen-side of the device.
      */
     int MIC_DIRECTION_FRONT = 1;
-
     /**
-     * @hide
+     * Optimize capture for audio coming from the side of the device opposite the screen.
      */
     int MIC_DIRECTION_BACK = 2;
-
     /**
-     * @hide
+     * Optimize capture for audio coming from an off-device microphone.
      */
     int MIC_DIRECTION_EXTERNAL = 3;
 
+    /** @hide */
+    @IntDef({
+            MIC_DIRECTION_UNSPECIFIED,
+            MIC_DIRECTION_FRONT,
+            MIC_DIRECTION_BACK,
+            MIC_DIRECTION_EXTERNAL
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Directionmode{};
     /**
      * Specifies the logical microphone (for processing).
      *
-     * @param direction Direction constant (MicrophoneDirection.MIC_DIRECTION_*)
-     * @return retval OK if the call is successful, an error code otherwise.
-     * @hide
+     * @param direction Direction constant.
+     * @return true if sucessful.
      */
-    int setMicrophoneDirection(int direction);
+    boolean setMicrophoneDirection(@Directionmode int direction);
 
     /**
      * Specifies the zoom factor (i.e. the field dimension) for the selected microphone
@@ -55,8 +63,7 @@
      *
      * @param zoom the desired field dimension of microphone capture. Range is from -1 (wide angle),
      * though 0 (no zoom) to 1 (maximum zoom).
-     * @return retval OK if the call is successful, an error code otherwise.
-     * @hide
+     * @return true if sucessful.
      */
-    int setMicrophoneFieldDimension(float zoom);
+    boolean setMicrophoneFieldDimension(@FloatRange(from = -1.0, to = 1.0) float zoom);
 }
diff --git a/packages/ExtServices/src/android/ext/services/notification/Assistant.java b/packages/ExtServices/src/android/ext/services/notification/Assistant.java
index c5e598d..31a3ff4 100644
--- a/packages/ExtServices/src/android/ext/services/notification/Assistant.java
+++ b/packages/ExtServices/src/android/ext/services/notification/Assistant.java
@@ -221,6 +221,12 @@
     }
 
     @Override
+    public Adjustment onNotificationEnqueued(StatusBarNotification sbn) {
+        // we use the version with channel, so this is never called.
+        return null;
+    }
+
+    @Override
     public Adjustment onNotificationEnqueued(StatusBarNotification sbn,
             NotificationChannel channel) {
         if (DEBUG) Log.i(TAG, "ENQUEUED " + sbn.getKey() + " on " + channel.getId());
diff --git a/services/core/java/com/android/server/ExtconUEventObserver.java b/services/core/java/com/android/server/ExtconUEventObserver.java
index eb59152..775e4c8 100644
--- a/services/core/java/com/android/server/ExtconUEventObserver.java
+++ b/services/core/java/com/android/server/ExtconUEventObserver.java
@@ -68,7 +68,7 @@
      * Subclasses of ExtconUEventObserver should override this method to handle UEvents.
      *
      * @param extconInfo that matches the {@code DEVPATH} of {@code event}
-     * @param event the event
+     * @param event      the event
      */
     protected abstract void onUEvent(ExtconInfo extconInfo, UEvent event);
 
@@ -91,6 +91,9 @@
 
         /** Returns a new list of all external connections whose name matches {@code regex}. */
         public static List<ExtconInfo> getExtconInfos(@Nullable String regex) {
+            if (!extconExists()) {
+                return new ArrayList<>(0);  // Always return a new list.
+            }
             Pattern p = regex == null ? null : Pattern.compile(regex);
             File file = new File("/sys/class/extcon");
             File[] files = file.listFiles();
diff --git a/services/core/java/com/android/server/FgThread.java b/services/core/java/com/android/server/FgThread.java
index fe30057..5d0e308 100644
--- a/services/core/java/com/android/server/FgThread.java
+++ b/services/core/java/com/android/server/FgThread.java
@@ -17,9 +17,12 @@
 package com.android.server;
 
 import android.os.Handler;
+import android.os.HandlerExecutor;
 import android.os.Looper;
 import android.os.Trace;
 
+import java.util.concurrent.Executor;
+
 /**
  * Shared singleton foreground thread for the system.  This is a thread for regular
  * foreground service operations, which shouldn't be blocked by anything running in
@@ -34,6 +37,7 @@
 
     private static FgThread sInstance;
     private static Handler sHandler;
+    private static HandlerExecutor sHandlerExecutor;
 
     private FgThread() {
         super("android.fg", android.os.Process.THREAD_PRIORITY_DEFAULT, true /*allowIo*/);
@@ -48,6 +52,7 @@
             looper.setSlowLogThresholdMs(
                     SLOW_DISPATCH_THRESHOLD_MS, SLOW_DELIVERY_THRESHOLD_MS);
             sHandler = new Handler(sInstance.getLooper());
+            sHandlerExecutor = new HandlerExecutor(sHandler);
         }
     }
 
@@ -64,4 +69,11 @@
             return sHandler;
         }
     }
+
+    public static Executor getExecutor() {
+        synchronized (FgThread.class) {
+            ensureThreadLocked();
+            return sHandlerExecutor;
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/IoThread.java b/services/core/java/com/android/server/IoThread.java
index bfe825a..21fd29c 100644
--- a/services/core/java/com/android/server/IoThread.java
+++ b/services/core/java/com/android/server/IoThread.java
@@ -17,8 +17,11 @@
 package com.android.server;
 
 import android.os.Handler;
+import android.os.HandlerExecutor;
 import android.os.Trace;
 
+import java.util.concurrent.Executor;
+
 /**
  * Shared singleton I/O thread for the system.  This is a thread for non-background
  * service operations that can potential block briefly on network IO operations
@@ -27,6 +30,7 @@
 public final class IoThread extends ServiceThread {
     private static IoThread sInstance;
     private static Handler sHandler;
+    private static HandlerExecutor sHandlerExecutor;
 
     private IoThread() {
         super("android.io", android.os.Process.THREAD_PRIORITY_DEFAULT, true /*allowIo*/);
@@ -38,6 +42,7 @@
             sInstance.start();
             sInstance.getLooper().setTraceTag(Trace.TRACE_TAG_SYSTEM_SERVER);
             sHandler = new Handler(sInstance.getLooper());
+            sHandlerExecutor = new HandlerExecutor(sHandler);
         }
     }
 
@@ -54,4 +59,11 @@
             return sHandler;
         }
     }
+
+    public static Executor getExecutor() {
+        synchronized (IoThread.class) {
+            ensureThreadLocked();
+            return sHandlerExecutor;
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index 9d92ea2b..5989a46 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -2136,6 +2136,13 @@
         }
     }
 
+    @Override
+    public String[] getIgnoreSettingsWhitelist() {
+        synchronized (mLock) {
+            return mIgnoreSettingsPackageWhitelist.toArray(new String[0]);
+        }
+    }
+
     @GuardedBy("mLock")
     private boolean isThrottlingExemptLocked(CallerIdentity callerIdentity) {
         if (callerIdentity.mUid == Process.SYSTEM_UID) {
@@ -2794,8 +2801,7 @@
         } catch (RemoteException e) {
             // if the remote process registering the listener is already dead, just swallow the
             // exception and return
-            Log.w(TAG, "Could not link " + linkedListener.mListenerName + " death callback.",
-                    e);
+            Log.w(TAG, "Could not link " + linkedListener.mListenerName + " death callback.", e);
             return false;
         }
     }
@@ -2808,8 +2814,7 @@
         } catch (NoSuchElementException e) {
             // if the death callback isn't connected (it should be...), log error,
             // swallow the exception and return
-            Log.w(TAG, "Could not unlink " + linkedListener.mListenerName + " death callback.",
-                    e);
+            Log.w(TAG, "Could not unlink " + linkedListener.mListenerName + " death callback.", e);
             return false;
         }
     }
diff --git a/services/core/java/com/android/server/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java
index 6b57fcd..710a0ba3 100644
--- a/services/core/java/com/android/server/UiModeManagerService.java
+++ b/services/core/java/com/android/server/UiModeManagerService.java
@@ -30,11 +30,13 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.os.BatteryManager;
 import android.os.Binder;
+import android.os.Build;
 import android.os.Handler;
 import android.os.PowerManager;
 import android.os.PowerManager.ServiceType;
@@ -354,8 +356,12 @@
             try {
                 synchronized (mLock) {
                     if (mNightMode != mode) {
-                        Settings.Secure.putIntForUser(getContext().getContentResolver(),
-                                Settings.Secure.UI_NIGHT_MODE, mode, user);
+                        // Only persist setting if not transient night mode or not in car mode
+                        if (!shouldTransientNightWhenInCarMode() || !mCarModeEnabled) {
+                            Settings.Secure.putIntForUser(getContext().getContentResolver(),
+                                    Settings.Secure.UI_NIGHT_MODE, mode, user);
+                        }
+
                         mNightMode = mode;
                         updateLocked(0, 0);
                     }
@@ -438,9 +444,39 @@
         }
     }
 
+    // Night mode settings in car mode are only persisted below Q.
+    // When targeting Q, changes are not saved and night mode will be re-read
+    // from settings when exiting car mode.
+    private boolean shouldTransientNightWhenInCarMode() {
+        int uid = Binder.getCallingUid();
+        PackageManager packageManager = getContext().getPackageManager();
+        String[] packagesForUid = packageManager.getPackagesForUid(uid);
+        if (packagesForUid == null || packagesForUid.length == 0) {
+            return false;
+        }
+
+        try {
+            ApplicationInfo appInfo = packageManager.getApplicationInfoAsUser(
+                    packagesForUid[0], 0, uid);
+
+            return appInfo.targetSdkVersion >= Build.VERSION_CODES.Q;
+        } catch (PackageManager.NameNotFoundException ignored) {
+        }
+
+        return false;
+    }
+
     void setCarModeLocked(boolean enabled, int flags) {
         if (mCarModeEnabled != enabled) {
             mCarModeEnabled = enabled;
+
+            // When transient night mode and exiting car mode, restore night mode from settings
+            if (shouldTransientNightWhenInCarMode() && !mCarModeEnabled) {
+                Context context = getContext();
+                updateNightModeFromSettings(context,
+                        context.getResources(),
+                        UserHandle.getCallingUserId());
+            }
         }
         mCarModeEnableFlags = flags;
     }
@@ -498,7 +534,9 @@
             uiMode |= mNightMode << 4;
         }
 
-        if (mPowerSave) {
+        // Override night mode in power save mode if not transient night mode or not in car mode
+        boolean shouldOverrideNight = !mCarModeEnabled || !shouldTransientNightWhenInCarMode();
+        if (mPowerSave && shouldOverrideNight) {
             uiMode &= ~Configuration.UI_MODE_NIGHT_NO;
             uiMode |= Configuration.UI_MODE_NIGHT_YES;
         }
diff --git a/services/core/java/com/android/server/appbinding/finders/CarrierMessagingClientServiceFinder.java b/services/core/java/com/android/server/appbinding/finders/CarrierMessagingClientServiceFinder.java
index 753d3b0..3663518 100644
--- a/services/core/java/com/android/server/appbinding/finders/CarrierMessagingClientServiceFinder.java
+++ b/services/core/java/com/android/server/appbinding/finders/CarrierMessagingClientServiceFinder.java
@@ -99,7 +99,7 @@
     @Override
     public void startMonitoring() {
         mRoleManager.addOnRoleHoldersChangedListenerAsUser(
-                mContext.getMainExecutor(), mRoleHolderChangedListener, UserHandle.ALL);
+                BackgroundThread.getExecutor(), mRoleHolderChangedListener, UserHandle.ALL);
     }
 
     @Override
@@ -120,9 +120,7 @@
 
     private final OnRoleHoldersChangedListener mRoleHolderChangedListener = (role, user) -> {
         if (RoleManager.ROLE_SMS.equals(role)) {
-            BackgroundThread.getHandler().post(() -> {
-                mListener.accept(CarrierMessagingClientServiceFinder.this, user.getIdentifier());
-            });
+            mListener.accept(CarrierMessagingClientServiceFinder.this, user.getIdentifier());
         }
     };
 }
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index 6df60d6..9af57da 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -280,9 +280,9 @@
                     AudioSystem.FOR_COMMUNICATION, mForcedUseForComm, eventSource);
             sendIILMsgNoDelay(MSG_IIL_SET_FORCE_USE, SENDMSG_QUEUE,
                     AudioSystem.FOR_RECORD, mForcedUseForComm, eventSource);
-            // Un-mute ringtone stream volume
-            mAudioService.setUpdateRingerModeServiceInt();
         }
+        // Un-mute ringtone stream volume
+        mAudioService.postUpdateRingerModeServiceInt();
     }
 
     /*package*/ AudioRoutesInfo startWatchingRoutes(IAudioRoutesObserver observer) {
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index fbabc82..1a62d4f 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -248,6 +248,7 @@
     private static final int MSG_NOTIFY_VOL_EVENT = 22;
     private static final int MSG_DISPATCH_AUDIO_SERVER_STATE = 23;
     private static final int MSG_ENABLE_SURROUND_FORMATS = 24;
+    private static final int MSG_UPDATE_RINGER_MODE = 25;
     // start of messages handled under wakelock
     //   these messages can only be queued, i.e. sent with queueMsgUnderWakeLock(),
     //   and not with sendMsg(..., ..., SENDMSG_QUEUE, ...)
@@ -2720,7 +2721,11 @@
         }
     }
 
-    /*package*/ void setUpdateRingerModeServiceInt() {
+    /*package*/ void postUpdateRingerModeServiceInt() {
+        sendMsg(mAudioHandler, MSG_UPDATE_RINGER_MODE, SENDMSG_QUEUE, 0, 0, null, 0);
+    }
+
+    private void onUpdateRingerModeServiceInt() {
         setRingerModeInt(getRingerModeInternal(), false);
     }
 
@@ -4959,6 +4964,10 @@
                 case MSG_ENABLE_SURROUND_FORMATS:
                     onEnableSurroundFormats((ArrayList<Integer>) msg.obj);
                     break;
+
+                case MSG_UPDATE_RINGER_MODE:
+                    onUpdateRingerModeServiceInt();
+                    break;
             }
         }
     }
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index e71b156..243b6fe 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -373,6 +373,7 @@
     private final NtpTimeHelper mNtpTimeHelper;
     private final GnssBatchingProvider mGnssBatchingProvider;
     private final GnssGeofenceProvider mGnssGeofenceProvider;
+    // Available only on GNSS HAL 2.0 implementations and later.
     private GnssVisibilityControl mGnssVisibilityControl;
 
     // Handler for processing events
@@ -463,8 +464,8 @@
         }
     };
 
-    // TODO(b/119326010): replace OnSubscriptionsChangedListener with
-    // ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED broadcast reseiver.
+    // TODO: replace OnSubscriptionsChangedListener with ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED
+    //       broadcast receiver.
     private final OnSubscriptionsChangedListener mOnSubscriptionsChangedListener =
             new OnSubscriptionsChangedListener() {
                 @Override
@@ -676,8 +677,7 @@
         mNtpTimeHelper.onNetworkAvailable();
         if (mDownloadXtraDataPending == STATE_PENDING_NETWORK) {
             if (mSupportsXtra) {
-                // Download only if supported, (prevents an unneccesary on-boot
-                // download)
+                // Download only if supported, (prevents an unnecessary on-boot download)
                 xtraDownloadRequest();
             }
         }
@@ -764,7 +764,7 @@
 
     /** Returns true if the location request is too frequent. */
     private boolean isRequestLocationRateLimited() {
-        // TODO(b/73198123): implement exponential backoff.
+        // TODO: implement exponential backoff.
         return false;
     }
 
diff --git a/services/core/java/com/android/server/location/GnssVisibilityControl.java b/services/core/java/com/android/server/location/GnssVisibilityControl.java
index c3f25bf..20f872a 100644
--- a/services/core/java/com/android/server/location/GnssVisibilityControl.java
+++ b/services/core/java/com/android/server/location/GnssVisibilityControl.java
@@ -24,10 +24,13 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.PackageManager;
+import android.database.ContentObserver;
+import android.location.LocationManager;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.PowerManager;
 import android.os.UserHandle;
+import android.provider.Settings;
 import android.text.TextUtils;
 import android.util.Log;
 import android.util.StatsLog;
@@ -70,7 +73,7 @@
     private final Handler mHandler;
     private final Context mContext;
 
-    private boolean mIsMasterLocationSettingsEnabled = true;
+    private boolean mIsDeviceLocationSettingsEnabled;
 
     // Number of non-framework location access proxy apps is expected to be small (< 5).
     private static final int HASH_MAP_INITIAL_CAPACITY_PROXY_APP_TO_LOCATION_PERMISSIONS = 7;
@@ -88,13 +91,9 @@
         mAppOps = mContext.getSystemService(AppOpsManager.class);
         mPackageManager = mContext.getPackageManager();
 
-        // Set to empty proxy app list initially until the configuration properties are loaded.
-        updateNfwLocationAccessProxyAppsInGnssHal();
-
-        // Listen for proxy app package installation, removal events.
-        listenForProxyAppsPackageUpdates();
-
-        // TODO(b/122855984): Handle global location settings on/off.
+        // Complete initialization as the first event to run in mHandler thread. After that,
+        // all object state read/update events run in the mHandler thread.
+        runOnHandler(this::handleInitialize);
     }
 
     void updateProxyApps(List<String> nfwLocationAccessProxyApps) {
@@ -105,10 +104,6 @@
         runOnHandler(() -> handleUpdateProxyApps(nfwLocationAccessProxyApps));
     }
 
-    void masterLocationSettingsUpdated(boolean enabled) {
-        runOnHandler(() -> handleMasterLocationSettingsUpdated(enabled));
-    }
-
     void reportNfwNotification(String proxyAppPackageName, byte protocolStack,
             String otherProtocolStackName, byte requestor, String requestorId, byte responseType,
             boolean inEmergencyMode, boolean isCachedLocation) {
@@ -117,7 +112,19 @@
                         requestor, requestorId, responseType, inEmergencyMode, isCachedLocation)));
     }
 
+    private void handleInitialize() {
+        disableNfwLocationAccess(); // Disable until config properties are loaded.
+        listenForProxyAppsPackageUpdates();
+        listenForDeviceLocationSettingsUpdate();
+        mIsDeviceLocationSettingsEnabled = getDeviceLocationSettings();
+    }
+
+    private boolean getDeviceLocationSettings() {
+        return mContext.getSystemService(LocationManager.class).isLocationEnabled();
+    }
+
     private void listenForProxyAppsPackageUpdates() {
+        // Listen for proxy apps package installation, removal events.
         IntentFilter intentFilter = new IntentFilter();
         intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
         intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
@@ -143,11 +150,22 @@
         }, UserHandle.ALL, intentFilter, null, mHandler);
     }
 
+    private void listenForDeviceLocationSettingsUpdate() {
+        mContext.getContentResolver().registerContentObserver(
+                Settings.Secure.getUriFor(Settings.Secure.LOCATION_MODE),
+                true,
+                new ContentObserver(mHandler) {
+                    @Override
+                    public void onChange(boolean selfChange) {
+                        handleDeviceLocationSettingsUpdated();
+                    }
+                }, UserHandle.USER_ALL);
+    }
+
     private void handleProxyAppPackageUpdate(String pkgName, String action) {
         final Boolean locationPermission = mProxyAppToLocationPermissions.get(pkgName);
-        // pkgName is not one of the proxy apps in our list.
         if (locationPermission == null) {
-            return;
+            return; // ignore, pkgName is not one of the proxy apps in our list.
         }
 
         Log.i(TAG, "Proxy app " + pkgName + " package changed: " + action);
@@ -197,15 +215,33 @@
                 return true;
             }
         }
-
         return false;
     }
 
-    private void handleMasterLocationSettingsUpdated(boolean enabled) {
-        mIsMasterLocationSettingsEnabled = enabled;
-        Log.i(TAG, "Master location settings switch changed to "
-                + (enabled ? "enabled" : "disabled"));
-        updateNfwLocationAccessProxyAppsInGnssHal();
+    private void handleDeviceLocationSettingsUpdated() {
+        final boolean enabled = getDeviceLocationSettings();
+        Log.i(TAG, "Device location settings enabled: " + enabled);
+
+        if (mIsDeviceLocationSettingsEnabled == enabled) {
+            return;
+        }
+
+        mIsDeviceLocationSettingsEnabled = enabled;
+        if (!mIsDeviceLocationSettingsEnabled) {
+            disableNfwLocationAccess();
+            return;
+        }
+
+        // When device location settings was disabled, we already set the proxy app list
+        // to empty in GNSS HAL. Update only if the proxy app list is not empty.
+        String[] locationPermissionEnabledProxyApps = getLocationPermissionEnabledProxyApps();
+        if (locationPermissionEnabledProxyApps.length != 0) {
+            setNfwLocationAccessProxyAppsInGnssHal(locationPermissionEnabledProxyApps);
+        }
+    }
+
+    private void disableNfwLocationAccess() {
+        setNfwLocationAccessProxyAppsInGnssHal(NO_LOCATION_ENABLED_PROXY_APPS);
     }
 
     // Represents NfwNotification structure in IGnssVisibilityControlCallback.hal
@@ -316,8 +352,7 @@
             return mPackageManager.getApplicationInfo(pkgName, 0).uid;
         } catch (PackageManager.NameNotFoundException e) {
             if (DEBUG) {
-                Log.d(TAG, "Non-framework location access proxy app "
-                        + pkgName + " is not found.");
+                Log.d(TAG, "Non-framework location access proxy app " + pkgName + " is not found.");
             }
             return null;
         }
@@ -329,8 +364,14 @@
     }
 
     private void updateNfwLocationAccessProxyAppsInGnssHal() {
-        final String[] locationPermissionEnabledProxyApps = shouldDisableNfwLocationAccess()
-                ? NO_LOCATION_ENABLED_PROXY_APPS : getLocationPermissionEnabledProxyApps();
+        if (!mIsDeviceLocationSettingsEnabled) {
+            return; // Keep non-framework location access disabled.
+        }
+        setNfwLocationAccessProxyAppsInGnssHal(getLocationPermissionEnabledProxyApps());
+    }
+
+    private void setNfwLocationAccessProxyAppsInGnssHal(
+            String[] locationPermissionEnabledProxyApps) {
         final String proxyAppsStr = Arrays.toString(locationPermissionEnabledProxyApps);
         Log.i(TAG, "Updating non-framework location access proxy apps in the GNSS HAL to: "
                 + proxyAppsStr);
@@ -341,12 +382,8 @@
         }
     }
 
-    private boolean shouldDisableNfwLocationAccess() {
-        return !mIsMasterLocationSettingsEnabled;
-    }
-
     private String[] getLocationPermissionEnabledProxyApps() {
-        // Get a count of proxy apps with location permission enabled to array creation size.
+        // Get a count of proxy apps with location permission enabled for array creation size.
         int countLocationPermissionEnabledProxyApps = 0;
         for (Boolean hasLocationPermissionEnabled : mProxyAppToLocationPermissions.values()) {
             if (hasLocationPermissionEnabled) {
diff --git a/services/core/java/com/android/server/pm/OWNERS b/services/core/java/com/android/server/pm/OWNERS
index 33b8641..640b155 100644
--- a/services/core/java/com/android/server/pm/OWNERS
+++ b/services/core/java/com/android/server/pm/OWNERS
@@ -19,6 +19,9 @@
 per-file CompilerStats.java = agampe@google.com
 per-file CompilerStats.java = calin@google.com
 per-file CompilerStats.java = ngeoffray@google.com
+per-file DynamicCodeLoggingService.java = agampe@google.com
+per-file DynamicCodeLoggingService.java = calin@google.com
+per-file DynamicCodeLoggingService.java = ngeoffray@google.com
 per-file InstructionSets.java = agampe@google.com
 per-file InstructionSets.java = calin@google.com
 per-file InstructionSets.java = ngeoffray@google.com
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 168c9ad..899bf7c 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -5090,11 +5090,7 @@
 
     @Override
     public void startWindowTrace(){
-        try {
-            mWindowTracing.startTrace(null /* printwriter */);
-        } catch (IOException e) {
-            throw new RuntimeException(e);
-        }
+        mWindowTracing.startTrace(null /* printwriter */);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/wm/WindowTraceBuffer.java b/services/core/java/com/android/server/wm/WindowTraceBuffer.java
index e4461ea..2ce6e6c 100644
--- a/services/core/java/com/android/server/wm/WindowTraceBuffer.java
+++ b/services/core/java/com/android/server/wm/WindowTraceBuffer.java
@@ -20,7 +20,6 @@
 import static com.android.server.wm.WindowManagerTraceFileProto.MAGIC_NUMBER_H;
 import static com.android.server.wm.WindowManagerTraceFileProto.MAGIC_NUMBER_L;
 
-import android.os.Trace;
 import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -36,24 +35,30 @@
 /**
  * Buffer used for window tracing.
  */
-abstract class WindowTraceBuffer {
+class WindowTraceBuffer {
     private static final long MAGIC_NUMBER_VALUE = ((long) MAGIC_NUMBER_H << 32) | MAGIC_NUMBER_L;
 
-    final Object mBufferLock = new Object();
-    final Queue<ProtoOutputStream> mBuffer = new ArrayDeque<>();
-    final File mTraceFile;
-    int mBufferSize;
-    private final int mBufferCapacity;
+    private final Object mBufferLock = new Object();
 
-    WindowTraceBuffer(int size, File traceFile) throws IOException {
-        mBufferCapacity = size;
-        mTraceFile = traceFile;
+    private final Queue<ProtoOutputStream> mBuffer = new ArrayDeque<>();
+    private int mBufferUsedSize;
+    private int mBufferCapacity;
 
-        initTraceFile();
+    WindowTraceBuffer(int bufferCapacity) {
+        mBufferCapacity = bufferCapacity;
+        resetBuffer();
     }
 
     int getAvailableSpace() {
-        return mBufferCapacity - mBufferSize;
+        return mBufferCapacity - mBufferUsedSize;
+    }
+
+    int size() {
+        return mBuffer.size();
+    }
+
+    void setCapacity(int capacity) {
+        mBufferCapacity = capacity;
     }
 
     /**
@@ -70,42 +75,37 @@
                     + mBufferCapacity + " Object size: " + protoLength);
         }
         synchronized (mBufferLock) {
-            boolean canAdd = canAdd(protoLength);
-            if (canAdd) {
-                mBuffer.add(proto);
-                mBufferSize += protoLength;
-            }
+            discardOldest(protoLength);
+            mBuffer.add(proto);
+            mBufferUsedSize += protoLength;
             mBufferLock.notify();
         }
     }
 
-    /**
-     * Stops the buffer execution and flush all buffer content to the disk.
-     *
-     * @throws IOException if the buffer cannot write its contents to the {@link #mTraceFile}
-     */
-    void dump() throws IOException, InterruptedException {
-        try {
-            Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "writeTraceToFile");
-            writeTraceToFile();
-        } finally {
-            Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
-        }
-    }
-
-    @VisibleForTesting
     boolean contains(byte[] other) {
         return mBuffer.stream()
                 .anyMatch(p -> Arrays.equals(p.getBytes(), other));
     }
 
-    private void initTraceFile() throws IOException {
-        mTraceFile.delete();
-        try (OutputStream os = new FileOutputStream(mTraceFile)) {
-            mTraceFile.setReadable(true, false);
-            ProtoOutputStream proto = new ProtoOutputStream(os);
-            proto.write(MAGIC_NUMBER, MAGIC_NUMBER_VALUE);
-            proto.flush();
+    /**
+     * Writes the trace buffer to disk.
+     */
+    void writeTraceToFile(File traceFile) throws IOException {
+        synchronized (mBufferLock) {
+            traceFile.delete();
+            traceFile.setReadable(true, false);
+            try (OutputStream os = new FileOutputStream(traceFile)) {
+                ProtoOutputStream proto = new ProtoOutputStream();
+                proto.write(MAGIC_NUMBER, MAGIC_NUMBER_VALUE);
+                os.write(proto.getBytes());
+                while (!mBuffer.isEmpty()) {
+                    proto = mBuffer.poll();
+                    mBufferUsedSize -= proto.getRawSize();
+                    byte[] protoBytes = proto.getBytes();
+                    os.write(protoBytes);
+                }
+                os.flush();
+            }
         }
     }
 
@@ -114,59 +114,48 @@
      * smaller than the overall buffer size.
      *
      * @param protoLength byte array representation of the Proto object to add
-     * @return {@code true} if the element can be added to the buffer or not
      */
-    abstract boolean canAdd(int protoLength);
+    private void discardOldest(int protoLength) {
+        long availableSpace = getAvailableSpace();
+
+        while (availableSpace < protoLength) {
+
+            ProtoOutputStream item = mBuffer.poll();
+            if (item == null) {
+                throw new IllegalStateException("No element to discard from buffer");
+            }
+            mBufferUsedSize -= item.getRawSize();
+            availableSpace = getAvailableSpace();
+        }
+    }
 
     /**
-     * Flush all buffer content to the disk.
-     *
-     * @throws IOException if the buffer cannot write its contents to the {@link #mTraceFile}
+     * Removes all elements form the buffer
      */
-    abstract void writeTraceToFile() throws IOException, InterruptedException;
-
-    /**
-     * Builder for a {@code WindowTraceBuffer} which creates a {@link WindowTraceRingBuffer} for
-     * continuous mode or a {@link WindowTraceQueueBuffer} otherwise
-     */
-    static class Builder {
-        private boolean mContinuous;
-        private File mTraceFile;
-        private int mBufferCapacity;
-
-        Builder setContinuousMode(boolean continuous) {
-            mContinuous = continuous;
-            return this;
+    void resetBuffer() {
+        synchronized (mBufferLock) {
+            mBuffer.clear();
+            mBufferUsedSize = 0;
         }
+    }
 
-        Builder setTraceFile(File traceFile) {
-            mTraceFile = traceFile;
-            return this;
-        }
+    @VisibleForTesting
+    int getBufferSize() {
+        return mBufferUsedSize;
+    }
 
-        Builder setBufferCapacity(int size) {
-            mBufferCapacity = size;
-            return this;
-        }
-
-        File getFile() {
-            return mTraceFile;
-        }
-
-        WindowTraceBuffer build() throws IOException {
-            if (mBufferCapacity <= 0) {
-                throw new IllegalStateException("Buffer capacity must be greater than 0.");
-            }
-
-            if (mTraceFile == null) {
-                throw new IllegalArgumentException("A valid trace file must be specified.");
-            }
-
-            if (mContinuous) {
-                return new WindowTraceRingBuffer(mBufferCapacity, mTraceFile);
-            } else {
-                return new WindowTraceQueueBuffer(mBufferCapacity, mTraceFile);
-            }
+    String getStatus() {
+        synchronized (mBufferLock) {
+            return "Buffer size: "
+                    + mBufferCapacity
+                    + " bytes"
+                    + "\n"
+                    + "Buffer usage: "
+                    + mBufferUsedSize
+                    + " bytes"
+                    + "\n"
+                    + "Elements in the buffer: "
+                    + mBuffer.size();
         }
     }
 }
diff --git a/services/core/java/com/android/server/wm/WindowTraceQueueBuffer.java b/services/core/java/com/android/server/wm/WindowTraceQueueBuffer.java
deleted file mode 100644
index 5888b7a..0000000
--- a/services/core/java/com/android/server/wm/WindowTraceQueueBuffer.java
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * 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 com.android.server.wm;
-
-import static android.os.Build.IS_USER;
-
-import android.util.Log;
-import android.util.proto.ProtoOutputStream;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-
-/**
- * A buffer structure backed by a {@link java.util.concurrent.BlockingQueue} to store the first
- * {@code #size size} bytes of window trace elements.
- * Once the buffer is full it will no longer accepts new elements.
- */
-class WindowTraceQueueBuffer extends WindowTraceBuffer {
-    private static final String TAG = "WindowTracing";
-
-    private Thread mConsumerThread;
-    private boolean mCancel;
-
-    @VisibleForTesting
-    WindowTraceQueueBuffer(int size, File traceFile, boolean startConsumerThread)
-            throws IOException {
-        super(size, traceFile);
-        if (startConsumerThread) {
-            initializeConsumerThread();
-        }
-    }
-
-    WindowTraceQueueBuffer(int size, File traceFile) throws IOException {
-        this(size, traceFile, !IS_USER);
-    }
-
-    private void initializeConsumerThread() {
-        mCancel = false;
-        mConsumerThread = new Thread(() -> {
-            try {
-                loop();
-            } catch (InterruptedException e) {
-                Log.i(TAG, "Interrupting trace consumer thread");
-            } catch (IOException e) {
-                Log.e(TAG, "Failed to execute trace consumer thread", e);
-            }
-        }, "window_tracing");
-        mConsumerThread.start();
-    }
-
-    private void loop() throws IOException, InterruptedException {
-        while (!mCancel) {
-            ProtoOutputStream proto;
-            synchronized (mBufferLock) {
-                mBufferLock.wait();
-                proto = mBuffer.poll();
-                if (proto != null) {
-                    mBufferSize -= proto.getRawSize();
-                }
-            }
-            if (proto != null) {
-                try (OutputStream os = new FileOutputStream(mTraceFile, true)) {
-                    byte[] protoBytes = proto.getBytes();
-                    os.write(protoBytes);
-                }
-            }
-        }
-    }
-
-    @Override
-    boolean canAdd(int protoLength) {
-        long availableSpace = getAvailableSpace();
-        return availableSpace >= protoLength;
-    }
-
-    @Override
-    void writeTraceToFile() throws InterruptedException {
-        synchronized (mBufferLock) {
-            mCancel = true;
-            mBufferLock.notify();
-        }
-        if (mConsumerThread != null) {
-            mConsumerThread.join();
-            mConsumerThread = null;
-        }
-    }
-}
diff --git a/services/core/java/com/android/server/wm/WindowTraceRingBuffer.java b/services/core/java/com/android/server/wm/WindowTraceRingBuffer.java
deleted file mode 100644
index 77d30be..0000000
--- a/services/core/java/com/android/server/wm/WindowTraceRingBuffer.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * 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 com.android.server.wm;
-
-import android.util.proto.ProtoOutputStream;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-
-/**
- * A ring buffer to store the {@code #size size} bytes of window trace data.
- * The buffer operates on a trace entry level, that is, if the new trace data is larger than the
- * available buffer space, the buffer will discard as many full trace entries as necessary to fit
- * the new trace.
- */
-class WindowTraceRingBuffer extends WindowTraceBuffer {
-    WindowTraceRingBuffer(int size, File traceFile) throws IOException {
-        super(size, traceFile);
-    }
-
-    @Override
-    boolean canAdd(int protoLength) {
-        long availableSpace = getAvailableSpace();
-
-        while (availableSpace < protoLength) {
-            discardOldest();
-            availableSpace = getAvailableSpace();
-        }
-
-        return true;
-    }
-
-    @Override
-    void writeTraceToFile() throws IOException {
-        synchronized (mBufferLock) {
-            try (OutputStream os = new FileOutputStream(mTraceFile, true)) {
-                while (!mBuffer.isEmpty()) {
-                    ProtoOutputStream proto = mBuffer.poll();
-                    mBufferSize -= proto.getRawSize();
-                    byte[] protoBytes = proto.getBytes();
-                    os.write(protoBytes);
-                }
-            }
-        }
-    }
-
-    private void discardOldest() {
-        ProtoOutputStream item = mBuffer.poll();
-        if (item == null) {
-            throw new IllegalStateException("No element to discard from buffer");
-        }
-        mBufferSize -= item.getRawSize();
-    }
-}
diff --git a/services/core/java/com/android/server/wm/WindowTracing.java b/services/core/java/com/android/server/wm/WindowTracing.java
index abc474d..0ce215c 100644
--- a/services/core/java/com/android/server/wm/WindowTracing.java
+++ b/services/core/java/com/android/server/wm/WindowTracing.java
@@ -31,8 +31,6 @@
 import android.util.proto.ProtoOutputStream;
 import android.view.Choreographer;
 
-import com.android.internal.annotations.VisibleForTesting;
-
 import java.io.File;
 import java.io.IOException;
 import java.io.PrintWriter;
@@ -47,139 +45,191 @@
      * Maximum buffer size, currently defined as 512 KB
      * Size was experimentally defined to fit between 100 to 150 elements.
      */
-    private static final int WINDOW_TRACE_BUFFER_SIZE = 512 * 1024;
+    private static final int BUFFER_CAPACITY_CRITICAL = 512 * 1024;
+    private static final int BUFFER_CAPACITY_TRIM = 2048 * 1024;
+    private static final int BUFFER_CAPACITY_ALL = 4096 * 1024;
+    private static final String TRACE_FILENAME = "/data/misc/wmtrace/wm_trace.pb";
     private static final String TAG = "WindowTracing";
 
     private final WindowManagerService mService;
     private final Choreographer mChoreographer;
     private final WindowManagerGlobalLock mGlobalLock;
 
-    private final Object mLock = new Object();
-    private final WindowTraceBuffer.Builder mBufferBuilder;
+    private final Object mEnabledLock = new Object();
+    private final File mTraceFile;
+    private final WindowTraceBuffer mBuffer;
+    private final Choreographer.FrameCallback mFrameCallback = (frameTimeNanos) ->
+            log("onFrame" /* where */);
 
-    private WindowTraceBuffer mTraceBuffer;
-
-    private @WindowTraceLogLevel int mWindowTraceLogLevel = WindowTraceLogLevel.TRIM;
-    private boolean mContinuousMode;
+    private @WindowTraceLogLevel int mLogLevel = WindowTraceLogLevel.TRIM;
+    private boolean mLogOnFrame = false;
     private boolean mEnabled;
     private volatile boolean mEnabledLockFree;
     private boolean mScheduled;
-    private Choreographer.FrameCallback mFrameCallback = (frameTimeNanos) ->
-            log("onFrame" /* where */);
 
-    private WindowTracing(File file, WindowManagerService service, Choreographer choreographer) {
-        this(file, service, choreographer, service.mGlobalLock);
+    static WindowTracing createDefaultAndStartLooper(WindowManagerService service,
+            Choreographer choreographer) {
+        File file = new File(TRACE_FILENAME);
+        return new WindowTracing(file, service, choreographer, BUFFER_CAPACITY_TRIM);
     }
 
-    @VisibleForTesting
-    WindowTracing(File file, WindowManagerService service, Choreographer choreographer,
-            WindowManagerGlobalLock globalLock) {
-        mBufferBuilder = new WindowTraceBuffer.Builder()
-                .setTraceFile(file)
-                .setBufferCapacity(WINDOW_TRACE_BUFFER_SIZE);
+    private WindowTracing(File file, WindowManagerService service, Choreographer choreographer,
+            int bufferCapacity) {
+        this(file, service, choreographer, service.mGlobalLock, bufferCapacity);
+    }
 
+    WindowTracing(File file, WindowManagerService service, Choreographer choreographer,
+            WindowManagerGlobalLock globalLock, int bufferCapacity) {
         mChoreographer = choreographer;
         mService = service;
         mGlobalLock = globalLock;
+        mTraceFile = file;
+        mBuffer = new WindowTraceBuffer(bufferCapacity);
+        setLogLevel(WindowTraceLogLevel.TRIM, null /* pw */);
     }
 
-    void startTrace(@Nullable PrintWriter pw) throws IOException {
+    void startTrace(@Nullable PrintWriter pw) {
         if (IS_USER) {
             logAndPrintln(pw, "Error: Tracing is not supported on user builds.");
             return;
         }
-        synchronized (mLock) {
-            logAndPrintln(pw, "Start tracing to " + mBufferBuilder.getFile() + ".");
-            if (mTraceBuffer != null) {
-                writeTraceToFileLocked();
-            }
-            mTraceBuffer = mBufferBuilder
-                    .setContinuousMode(mContinuousMode)
-                    .build();
+        synchronized (mEnabledLock) {
+            logAndPrintln(pw, "Start tracing to " + mTraceFile + ".");
+            mBuffer.resetBuffer();
             mEnabled = mEnabledLockFree = true;
         }
     }
 
-    private void logAndPrintln(@Nullable PrintWriter pw, String msg) {
-        Log.i(TAG, msg);
-        if (pw != null) {
-            pw.println(msg);
-            pw.flush();
-        }
-    }
-
     void stopTrace(@Nullable PrintWriter pw) {
         if (IS_USER) {
             logAndPrintln(pw, "Error: Tracing is not supported on user builds.");
             return;
         }
-        synchronized (mLock) {
-            logAndPrintln(pw, "Stop tracing to " + mBufferBuilder.getFile()
-                    + ". Waiting for traces to flush.");
+        synchronized (mEnabledLock) {
+            logAndPrintln(pw, "Stop tracing to " + mTraceFile + ". Waiting for traces to flush.");
             mEnabled = mEnabledLockFree = false;
 
-            synchronized (mLock) {
-                if (mEnabled) {
-                    logAndPrintln(pw, "ERROR: tracing was re-enabled while waiting for flush.");
-                    throw new IllegalStateException("tracing enabled while waiting for flush.");
-                }
-                writeTraceToFileLocked();
-                mTraceBuffer = null;
+            if (mEnabled) {
+                logAndPrintln(pw, "ERROR: tracing was re-enabled while waiting for flush.");
+                throw new IllegalStateException("tracing enabled while waiting for flush.");
             }
-            logAndPrintln(pw, "Trace written to " + mBufferBuilder.getFile() + ".");
+            writeTraceToFileLocked();
+            logAndPrintln(pw, "Trace written to " + mTraceFile + ".");
         }
     }
 
-    @VisibleForTesting
-    void setContinuousMode(boolean continuous, PrintWriter pw) {
-        logAndPrintln(pw, "Setting window tracing continuous mode to " + continuous);
+    private void setLogLevel(@WindowTraceLogLevel int logLevel, PrintWriter pw) {
+        logAndPrintln(pw, "Setting window tracing log level to " + logLevel);
+        mLogLevel = logLevel;
 
-        if (mEnabled) {
-            logAndPrintln(pw, "Trace is currently active, change will take effect once the "
-                    + "trace is restarted.");
+        switch (logLevel) {
+            case WindowTraceLogLevel.ALL: {
+                setBufferCapacity(BUFFER_CAPACITY_ALL, pw);
+                break;
+            }
+            case WindowTraceLogLevel.TRIM: {
+                setBufferCapacity(BUFFER_CAPACITY_TRIM, pw);
+                break;
+            }
+            case WindowTraceLogLevel.CRITICAL: {
+                setBufferCapacity(BUFFER_CAPACITY_CRITICAL, pw);
+                break;
+            }
         }
-        mContinuousMode = continuous;
-        mWindowTraceLogLevel = (continuous) ? WindowTraceLogLevel.CRITICAL :
-                WindowTraceLogLevel.TRIM;
+    }
+
+    private void setLogFrequency(boolean onFrame, PrintWriter pw) {
+        logAndPrintln(pw, "Setting window tracing log frequency to "
+                + ((onFrame) ? "frame" : "transaction"));
+        mLogOnFrame = onFrame;
+    }
+
+    private void setBufferCapacity(int capacity, PrintWriter pw) {
+        logAndPrintln(pw, "Setting window tracing buffer capacity to " + capacity + "bytes");
+        mBuffer.setCapacity(capacity);
     }
 
     boolean isEnabled() {
         return mEnabledLockFree;
     }
 
-    static WindowTracing createDefaultAndStartLooper(WindowManagerService service,
-            Choreographer choreographer) {
-        File file = new File("/data/misc/wmtrace/wm_trace.pb");
-        return new WindowTracing(file, service, choreographer);
-    }
-
     int onShellCommand(ShellCommand shell) {
         PrintWriter pw = shell.getOutPrintWriter();
-        try {
-            String cmd = shell.getNextArgRequired();
-            switch (cmd) {
-                case "start":
-                    startTrace(pw);
-                    return 0;
-                case "stop":
-                    stopTrace(pw);
-                    return 0;
-                case "continuous":
-                    setContinuousMode(Boolean.valueOf(shell.getNextArgRequired()), pw);
-                    return 0;
-                default:
-                    pw.println("Unknown command: " + cmd);
-                    return -1;
-            }
-        } catch (IOException e) {
-            logAndPrintln(pw, e.toString());
-            throw new RuntimeException(e);
+        String cmd = shell.getNextArgRequired();
+        switch (cmd) {
+            case "start":
+                startTrace(pw);
+                return 0;
+            case "stop":
+                stopTrace(pw);
+                return 0;
+            case "status":
+                logAndPrintln(pw, getStatus());
+                return 0;
+            case "frame":
+                setLogFrequency(true /* onFrame */, pw);
+                mBuffer.resetBuffer();
+                return 0;
+            case "transaction":
+                setLogFrequency(false /* onFrame */, pw);
+                mBuffer.resetBuffer();
+                return 0;
+            case "level":
+                String logLevelStr = shell.getNextArgRequired().toLowerCase();
+                switch (logLevelStr) {
+                    case "all": {
+                        setLogLevel(WindowTraceLogLevel.ALL, pw);
+                        break;
+                    }
+                    case "trim": {
+                        setLogLevel(WindowTraceLogLevel.TRIM, pw);
+                        break;
+                    }
+                    case "critical": {
+                        setLogLevel(WindowTraceLogLevel.CRITICAL, pw);
+                        break;
+                    }
+                    default: {
+                        setLogLevel(WindowTraceLogLevel.TRIM, pw);
+                        break;
+                    }
+                }
+                mBuffer.resetBuffer();
+                return 0;
+            case "size":
+                setBufferCapacity(Integer.parseInt(shell.getNextArgRequired()) * 1024, pw);
+                mBuffer.resetBuffer();
+                return 0;
+            default:
+                pw.println("Unknown command: " + cmd);
+                pw.println("Window manager trace options:");
+                pw.println("  start: Start logging");
+                pw.println("  stop: Stop logging");
+                pw.println("  frame: Log trace once per frame");
+                pw.println("  transaction: Log each transaction");
+                pw.println("  size: Set the maximum log size (in KB)");
+                pw.println("  level [lvl]: Set the log level between");
+                pw.println("    lvl may be one of:");
+                pw.println("      critical: Only visible windows with reduced information");
+                pw.println("      trim: All windows with reduced");
+                pw.println("      all: All window and information");
+                return -1;
         }
     }
 
+    private String getStatus() {
+        return "Status: "
+                + ((isEnabled()) ? "Enabled" : "Disabled")
+                + "\n"
+                + "Log level: "
+                + mLogLevel
+                + "\n"
+                + mBuffer.getStatus();
+    }
+
     /**
      * If tracing is enabled, log the current state or schedule the next frame to be logged,
-     * according to {@link #mContinuousMode}.
+     * according to {@link #mLogOnFrame}.
      *
      * @param where Logging point descriptor
      */
@@ -188,7 +238,7 @@
             return;
         }
 
-        if (mContinuousMode) {
+        if (mLogOnFrame) {
             schedule();
         } else {
             log(where);
@@ -215,25 +265,24 @@
     private void log(String where) {
         Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "traceStateLocked");
         try {
-            synchronized (mGlobalLock) {
-                ProtoOutputStream os = new ProtoOutputStream();
-                long tokenOuter = os.start(ENTRY);
-                os.write(ELAPSED_REALTIME_NANOS, SystemClock.elapsedRealtimeNanos());
-                os.write(WHERE, where);
+            ProtoOutputStream os = new ProtoOutputStream();
+            long tokenOuter = os.start(ENTRY);
+            os.write(ELAPSED_REALTIME_NANOS, SystemClock.elapsedRealtimeNanos());
+            os.write(WHERE, where);
 
+            long tokenInner = os.start(WINDOW_MANAGER_SERVICE);
+            synchronized (mGlobalLock) {
                 Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "writeToProtoLocked");
                 try {
-                    long tokenInner = os.start(WINDOW_MANAGER_SERVICE);
-                    mService.writeToProtoLocked(os, mWindowTraceLogLevel);
-                    os.end(tokenInner);
+                    mService.writeToProtoLocked(os, mLogLevel);
                 } finally {
                     Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
                 }
-                os.end(tokenOuter);
-                mTraceBuffer.add(os);
-
-                mScheduled = false;
             }
+            os.end(tokenInner);
+            os.end(tokenOuter);
+            mBuffer.add(os);
+            mScheduled = false;
         } catch (Exception e) {
             Log.wtf(TAG, "Exception while tracing state", e);
         } finally {
@@ -242,31 +291,37 @@
     }
 
     /**
-     * Writes the trace buffer to disk. This method has no internal synchronization and should be
-     * externally synchronized
-     */
-    private void writeTraceToFileLocked() {
-        if (mTraceBuffer == null) {
-            return;
-        }
-
-        try {
-            mTraceBuffer.dump();
-        } catch (IOException e) {
-            Log.e(TAG, "Unable to write buffer to file", e);
-        } catch (InterruptedException e) {
-            Log.e(TAG, "Unable to interrupt window tracing file write thread", e);
-        }
-    }
-
-    /**
-     * Writes the trace buffer to disk and clones it into a new file for the bugreport.
+     * Writes the trace buffer to new file for the bugreport.
+     *
      * This method is synchronized with {@code #startTrace(PrintWriter)} and
      * {@link #stopTrace(PrintWriter)}.
      */
     void writeTraceToFile() {
-        synchronized (mLock) {
+        synchronized (mEnabledLock) {
             writeTraceToFileLocked();
         }
     }
-}
+
+    private void logAndPrintln(@Nullable PrintWriter pw, String msg) {
+        Log.i(TAG, msg);
+        if (pw != null) {
+            pw.println(msg);
+            pw.flush();
+        }
+    }
+
+    /**
+     * Writes the trace buffer to disk. This method has no internal synchronization and should be
+     * externally synchronized
+     */
+    private void writeTraceToFileLocked() {
+        try {
+            Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "writeTraceToFileLocked");
+            mBuffer.writeTraceToFile(mTraceFile);
+        } catch (IOException e) {
+            Log.e(TAG, "Unable to write buffer to file", e);
+        } finally {
+            Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
+        }
+    }
+}
\ No newline at end of file
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTraceBufferTest.java b/services/tests/wmtests/src/com/android/server/wm/WindowTraceBufferTest.java
index 2b8e307..b299f0d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTraceBufferTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTraceBufferTest.java
@@ -36,11 +36,10 @@
 import org.junit.Test;
 
 import java.io.File;
-import java.io.IOException;
 
 
 /**
- * Test class for {@link WindowTraceBuffer} and {@link WindowTraceQueueBuffer}.
+ * Test class for {@link WindowTraceBuffer}.
  *
  * Build/Install/Run:
  *  atest WmTests:WindowTraceBufferTest
@@ -49,12 +48,15 @@
 @Presubmit
 public class WindowTraceBufferTest {
     private File mFile;
+    private WindowTraceBuffer mBuffer;
 
     @Before
     public void setUp() throws Exception {
         final Context testContext = getInstrumentation().getContext();
         mFile = testContext.getFileStreamPath("tracing_test.dat");
         mFile.delete();
+
+        mBuffer = new WindowTraceBuffer(10);
     }
 
     @After
@@ -63,145 +65,112 @@
     }
 
     @Test
-    public void testTraceQueueBuffer_addItem() throws Exception {
-        ProtoOutputStream toWrite1 = getDummy(1);
-        ProtoOutputStream toWrite2 = getDummy(2);
-        ProtoOutputStream toWrite3 = getDummy(3);
-        final int objectSize = toWrite1.getRawSize();
-        final int bufferCapacity = objectSize * 2;
-
-        final WindowTraceBuffer buffer = buildQueueBuffer(bufferCapacity);
-
-        buffer.add(toWrite1);
-        byte[] toWrite1Bytes = toWrite1.getBytes();
-        assertTrue("First element should be in the list",
-                buffer.contains(toWrite1Bytes));
-
-        buffer.add(toWrite2);
-        byte[] toWrite2Bytes = toWrite2.getBytes();
-        assertTrue("First element should be in the list",
-                buffer.contains(toWrite1Bytes));
-        assertTrue("Second element should be in the list",
-                buffer.contains(toWrite2Bytes));
-
-        buffer.add(toWrite3);
-        byte[] toWrite3Bytes = toWrite3.getBytes();
-        assertTrue("First element should be in the list",
-                buffer.contains(toWrite1Bytes));
-        assertTrue("Second element should be in the list",
-                buffer.contains(toWrite2Bytes));
-        assertTrue("Third element should not be in the list",
-                !buffer.contains(toWrite3Bytes));
-
-        assertEquals("Buffer should have 2 elements", buffer.mBuffer.size(), 2);
-        assertEquals(String.format("Buffer is full, used space should be %d", bufferCapacity),
-                buffer.mBufferSize, bufferCapacity);
-        assertEquals("Buffer is full, available space should be 0",
-                buffer.getAvailableSpace(), 0);
-    }
-
-    @Test
-    public void testTraceRingBuffer_addItem() throws Exception {
+    public void test_addItem() {
         ProtoOutputStream toWrite = getDummy(1);
         final int objectSize = toWrite.getRawSize();
+        mBuffer.setCapacity(objectSize);
+        mBuffer.resetBuffer();
 
-        final WindowTraceBuffer buffer = buildRingBuffer(objectSize);
+        Preconditions.checkArgument(mBuffer.size() == 0);
 
-        Preconditions.checkArgument(buffer.mBuffer.isEmpty());
+        mBuffer.add(toWrite);
 
-        buffer.add(toWrite);
-
-        assertEquals("Item was not added to the buffer", buffer.mBuffer.size(), 1);
+        assertEquals("Item was not added to the buffer", 1, mBuffer.size());
         assertEquals("Total buffer getSize differs from inserted object",
-                buffer.mBufferSize, objectSize);
-        assertEquals("Available buffer space does not match used one",
-                buffer.getAvailableSpace(), 0);
+                mBuffer.getBufferSize(), objectSize);
+        assertEquals("Available buffer space does not match used one", 0,
+                mBuffer.getAvailableSpace());
     }
 
     @Test
-    public void testTraceRingBuffer_addItemMustOverwriteOne() throws Exception {
+    public void test_addItemMustOverwriteOne() {
         ProtoOutputStream toWrite1 = getDummy(1);
         ProtoOutputStream toWrite2 = getDummy(2);
         ProtoOutputStream toWrite3 = getDummy(3);
         final int objectSize = toWrite1.getRawSize();
-
         final int bufferCapacity = objectSize * 2 + 1;
-        final WindowTraceBuffer buffer = buildRingBuffer(bufferCapacity);
+        mBuffer.setCapacity(bufferCapacity);
+        mBuffer.resetBuffer();
 
-        buffer.add(toWrite1);
+        mBuffer.add(toWrite1);
         byte[] toWrite1Bytes = toWrite1.getBytes();
         assertTrue("First element should be in the list",
-                buffer.contains(toWrite1Bytes));
+                mBuffer.contains(toWrite1Bytes));
 
-        buffer.add(toWrite2);
+        mBuffer.add(toWrite2);
         byte[] toWrite2Bytes = toWrite2.getBytes();
         assertTrue("First element should be in the list",
-                buffer.contains(toWrite1Bytes));
+                mBuffer.contains(toWrite1Bytes));
         assertTrue("Second element should be in the list",
-                buffer.contains(toWrite2Bytes));
+                mBuffer.contains(toWrite2Bytes));
 
-        buffer.add(toWrite3);
+        mBuffer.add(toWrite3);
         byte[] toWrite3Bytes = toWrite3.getBytes();
         assertTrue("First element should not be in the list",
-                !buffer.contains(toWrite1Bytes));
+                !mBuffer.contains(toWrite1Bytes));
         assertTrue("Second element should be in the list",
-                buffer.contains(toWrite2Bytes));
+                mBuffer.contains(toWrite2Bytes));
         assertTrue("Third element should be in the list",
-                buffer.contains(toWrite3Bytes));
-        assertEquals("Buffer should have 2 elements", buffer.mBuffer.size(), 2);
+                mBuffer.contains(toWrite3Bytes));
+        assertEquals("Buffer should have 2 elements", 2, mBuffer.size());
         assertEquals(String.format("Buffer is full, used space should be %d", bufferCapacity),
-                buffer.mBufferSize, bufferCapacity - 1);
-        assertEquals(" Buffer is full, available space should be 0",
-                buffer.getAvailableSpace(), 1);
+                mBuffer.getBufferSize(), bufferCapacity - 1);
+        assertEquals(" Buffer is full, available space should be 0", 1,
+                mBuffer.getAvailableSpace());
     }
 
     @Test
-    public void testTraceRingBuffer_addItemMustOverwriteMultiple() throws Exception {
+    public void test_addItemMustOverwriteMultiple() {
         ProtoOutputStream toWriteSmall1 = getDummy(1);
         ProtoOutputStream toWriteSmall2 = getDummy(2);
         final int objectSize = toWriteSmall1.getRawSize();
-
         final int bufferCapacity = objectSize * 2;
-        final WindowTraceBuffer buffer = buildRingBuffer(bufferCapacity);
+        mBuffer.setCapacity(bufferCapacity);
+        mBuffer.resetBuffer();
 
         ProtoOutputStream toWriteBig = new ProtoOutputStream();
         toWriteBig.write(MAGIC_NUMBER, 1);
         toWriteBig.write(MAGIC_NUMBER, 2);
 
-        buffer.add(toWriteSmall1);
+        mBuffer.add(toWriteSmall1);
         byte[] toWriteSmall1Bytes = toWriteSmall1.getBytes();
         assertTrue("First element should be in the list",
-                buffer.contains(toWriteSmall1Bytes));
+                mBuffer.contains(toWriteSmall1Bytes));
 
-        buffer.add(toWriteSmall2);
+        mBuffer.add(toWriteSmall2);
         byte[] toWriteSmall2Bytes = toWriteSmall2.getBytes();
         assertTrue("First element should be in the list",
-                buffer.contains(toWriteSmall1Bytes));
+                mBuffer.contains(toWriteSmall1Bytes));
         assertTrue("Second element should be in the list",
-                buffer.contains(toWriteSmall2Bytes));
+                mBuffer.contains(toWriteSmall2Bytes));
 
-        buffer.add(toWriteBig);
+        mBuffer.add(toWriteBig);
         byte[] toWriteBigBytes = toWriteBig.getBytes();
         assertTrue("Third element should overwrite all others",
-                !buffer.contains(toWriteSmall1Bytes));
+                !mBuffer.contains(toWriteSmall1Bytes));
         assertTrue("Third element should overwrite all others",
-                !buffer.contains(toWriteSmall2Bytes));
+                !mBuffer.contains(toWriteSmall2Bytes));
         assertTrue("Third element should overwrite all others",
-                buffer.contains(toWriteBigBytes));
+                mBuffer.contains(toWriteBigBytes));
 
-        assertEquals(" Buffer should have only 1 big element", buffer.mBuffer.size(), 1);
+        assertEquals(" Buffer should have only 1 big element", 1, mBuffer.size());
         assertEquals(String.format(" Buffer is full, used space should be %d", bufferCapacity),
-                buffer.mBufferSize, bufferCapacity);
-        assertEquals(" Buffer is full, available space should be 0",
-                buffer.getAvailableSpace(), 0);
+                mBuffer.getBufferSize(), bufferCapacity);
+        assertEquals(" Buffer is full, available space should be 0", 0,
+                mBuffer.getAvailableSpace());
     }
 
-    private WindowTraceBuffer buildRingBuffer(int capacity) throws IOException {
-        return new WindowTraceBuffer.Builder()
-                .setContinuousMode(true)
-                .setBufferCapacity(capacity)
-                .setTraceFile(mFile)
-                .build();
+    @Test
+    public void test_startResetsBuffer() {
+        ProtoOutputStream toWrite = getDummy(1);
+        mBuffer.resetBuffer();
+        Preconditions.checkArgument(mBuffer.size() == 0);
+
+        mBuffer.add(toWrite);
+        assertEquals("Item was not added to the buffer", 1, mBuffer.size());
+        mBuffer.resetBuffer();
+        assertEquals("Buffer should be empty after reset", 0, mBuffer.size());
+        assertEquals("Buffer size should be 0 after reset", 0, mBuffer.getBufferSize());
     }
 
     private ProtoOutputStream getDummy(int value) {
@@ -212,7 +181,4 @@
         return toWrite;
     }
 
-    private WindowTraceBuffer buildQueueBuffer(int size) throws IOException {
-        return new WindowTraceQueueBuffer(size, mFile, false);
-    }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTracingTest.java b/services/tests/wmtests/src/com/android/server/wm/WindowTracingTest.java
index 3c6e240..8358fdd 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTracingTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTracingTest.java
@@ -88,8 +88,7 @@
         mFile.delete();
 
         mWindowTracing = new WindowTracing(mFile, mWmMock, mChoreographer,
-                new WindowManagerGlobalLock());
-        mWindowTracing.setContinuousMode(false /* continuous */, null /* pw */);
+                new WindowManagerGlobalLock(), 1024);
     }
 
     @After
@@ -103,13 +102,13 @@
     }
 
     @Test
-    public void isEnabled_returnsTrueAfterStart() throws Exception {
+    public void isEnabled_returnsTrueAfterStart() {
         mWindowTracing.startTrace(mock(PrintWriter.class));
         assertTrue(mWindowTracing.isEnabled());
     }
 
     @Test
-    public void isEnabled_returnsFalseAfterStop() throws Exception {
+    public void isEnabled_returnsFalseAfterStop() {
         mWindowTracing.startTrace(mock(PrintWriter.class));
         mWindowTracing.stopTrace(mock(PrintWriter.class));
         assertFalse(mWindowTracing.isEnabled());
@@ -133,6 +132,8 @@
         mWindowTracing.startTrace(mock(PrintWriter.class));
         mWindowTracing.stopTrace(mock(PrintWriter.class));
 
+        assertTrue("Trace file should exist", mFile.exists());
+
         byte[] header = new byte[MAGIC_HEADER.length];
         try (InputStream is = new FileInputStream(mFile)) {
             assertEquals(MAGIC_HEADER.length, is.read(header));
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index bd0d4ae..ae12a17 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -623,7 +623,7 @@
             "android.telecom.event.HANDOVER_FAILED";
 
     /**
-     * Connection extra key used to store SIP invite fields for an incoming call for IMS calls
+     * String Connection extra key used to store SIP invite fields for an incoming call for IMS call
      */
     public static final String EXTRA_SIP_INVITE = "android.telecom.extra.SIP_INVITE";
 
diff --git a/telephony/java/android/provider/Telephony.java b/telephony/java/android/provider/Telephony.java
index a1c32b5..2462bee 100644
--- a/telephony/java/android/provider/Telephony.java
+++ b/telephony/java/android/provider/Telephony.java
@@ -2123,6 +2123,11 @@
      * @hide - not meant for public use
      */
     public interface RcsColumns {
+        // TODO(sahinc): Turn this to true once the schema finalizes, so that people can update
+        //  their messaging databases. NOTE: move the switch/case update in MmsSmsDatabaseHelper to
+        //  the latest version of the database before turning this flag to true.
+        boolean IS_RCS_TABLE_SCHEMA_CODE_COMPLETE = false;
+
         /**
          * The authority for the content provider
          */
diff --git a/telephony/java/android/telephony/ims/ImsCallProfile.java b/telephony/java/android/telephony/ims/ImsCallProfile.java
index 73f0556..d5c7079 100644
--- a/telephony/java/android/telephony/ims/ImsCallProfile.java
+++ b/telephony/java/android/telephony/ims/ImsCallProfile.java
@@ -266,6 +266,11 @@
     public static final String EXTRA_DISPLAY_TEXT = "DisplayText";
     public static final String EXTRA_ADDITIONAL_CALL_INFO = "AdditionalCallInfo";
     public static final String EXTRA_IS_CALL_PULL = "CallPull";
+
+    /**
+     * String extra property
+     *  Containing fields from the SIP INVITE message for an IMS call
+     */
     public static final String EXTRA_ADDITIONAL_SIP_INVITE_FIELDS =
                                   "android.telephony.ims.extra.ADDITIONAL_SIP_INVITE_FIELDS";
 
diff --git a/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp
index 2f8ca2d..b669170 100644
--- a/tools/aapt2/ResourceParser.cpp
+++ b/tools/aapt2/ResourceParser.cpp
@@ -1113,6 +1113,13 @@
     const std::string& element_name = parser->element_name();
     const std::string& element_namespace = parser->element_namespace();
     if (element_namespace.empty() && element_name == "item") {
+      if (current_policies == OverlayableItem::Policy::kNone) {
+        diag_->Error(DiagMessage(element_source)
+                         << "<item> within an <overlayable> must be inside a <policy> block");
+        error = true;
+        continue;
+      }
+
       // Items specify the name and type of resource that should be overlayable
       Maybe<StringPiece> item_name = xml::FindNonEmptyAttribute(parser, "name");
       if (!item_name) {
@@ -1169,6 +1176,8 @@
             current_policies |= OverlayableItem::Policy::kSystem;
           } else if (trimmed_part == "vendor") {
             current_policies |= OverlayableItem::Policy::kVendor;
+          } else if (trimmed_part == "signature") {
+            current_policies |= OverlayableItem::Policy::kSignature;
           } else {
             diag_->Error(DiagMessage(element_source)
                          << "<policy> has unsupported type '" << trimmed_part << "'");
@@ -1176,6 +1185,11 @@
             continue;
           }
         }
+      } else {
+        diag_->Error(DiagMessage(element_source)
+                         << "<policy> must have a 'type' attribute");
+        error = true;
+        continue;
       }
     } else if (!ShouldIgnoreElement(element_namespace, element_name)) {
       diag_->Error(DiagMessage(element_source) << "invalid element <" << element_name << "> "
diff --git a/tools/aapt2/ResourceParser_test.cpp b/tools/aapt2/ResourceParser_test.cpp
index 827c7de..25b76b0 100644
--- a/tools/aapt2/ResourceParser_test.cpp
+++ b/tools/aapt2/ResourceParser_test.cpp
@@ -894,8 +894,10 @@
 TEST_F(ResourceParserTest, ParseOverlayable) {
   std::string input = R"(
       <overlayable name="Name" actor="overlay://theme">
-        <item type="string" name="foo" />
-        <item type="drawable" name="bar" />
+          <policy type="signature">
+            <item type="string" name="foo" />
+            <item type="drawable" name="bar" />
+          </policy>
       </overlayable>)";
   ASSERT_TRUE(TestParse(input));
 
@@ -906,7 +908,7 @@
   OverlayableItem& result_overlayable_item = search_result.value().entry->overlayable_item.value();
   EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
   EXPECT_THAT(result_overlayable_item.overlayable->actor, Eq("overlay://theme"));
-  EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kNone));
+  EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kSignature));
 
   search_result = table_.FindResource(test::ParseNameOrDie("drawable/bar"));
   ASSERT_TRUE(search_result);
@@ -915,7 +917,7 @@
   result_overlayable_item = search_result.value().entry->overlayable_item.value();
   EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
   EXPECT_THAT(result_overlayable_item.overlayable->actor, Eq("overlay://theme"));
-  EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kNone));
+  EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kSignature));
 }
 
 TEST_F(ResourceParserTest, ParseOverlayableRequiresName) {
@@ -931,7 +933,6 @@
 TEST_F(ResourceParserTest, ParseOverlayablePolicy) {
   std::string input = R"(
       <overlayable name="Name">
-        <item type="string" name="foo" />
         <policy type="product">
           <item type="string" name="bar" />
         </policy>
@@ -944,23 +945,18 @@
         <policy type="public">
           <item type="string" name="faz" />
         </policy>
+        <policy type="signature">
+          <item type="string" name="foz" />
+        </policy>
       </overlayable>)";
   ASSERT_TRUE(TestParse(input));
 
-  auto search_result = table_.FindResource(test::ParseNameOrDie("string/foo"));
+  auto search_result = table_.FindResource(test::ParseNameOrDie("string/bar"));
   ASSERT_TRUE(search_result);
   ASSERT_THAT(search_result.value().entry, NotNull());
   ASSERT_TRUE(search_result.value().entry->overlayable_item);
   OverlayableItem result_overlayable_item = search_result.value().entry->overlayable_item.value();
   EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
-  EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kNone));
-
-  search_result = table_.FindResource(test::ParseNameOrDie("string/bar"));
-  ASSERT_TRUE(search_result);
-  ASSERT_THAT(search_result.value().entry, NotNull());
-  ASSERT_TRUE(search_result.value().entry->overlayable_item);
-  result_overlayable_item = search_result.value().entry->overlayable_item.value();
-  EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
   EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kProduct));
 
   search_result = table_.FindResource(test::ParseNameOrDie("string/fiz"));
@@ -986,6 +982,30 @@
   result_overlayable_item = search_result.value().entry->overlayable_item.value();
   EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
   EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kPublic));
+
+  search_result = table_.FindResource(test::ParseNameOrDie("string/foz"));
+  ASSERT_TRUE(search_result);
+  ASSERT_THAT(search_result.value().entry, NotNull());
+  ASSERT_TRUE(search_result.value().entry->overlayable_item);
+  result_overlayable_item = search_result.value().entry->overlayable_item.value();
+  EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
+  EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kSignature));
+}
+
+TEST_F(ResourceParserTest, ParseOverlayableNoPolicyError) {
+  std::string input = R"(
+      <overlayable name="Name">
+        <item type="string" name="foo" />
+      </overlayable>)";
+  EXPECT_FALSE(TestParse(input));
+
+  input = R"(
+      <overlayable name="Name">
+        <policy>
+          <item name="foo" />
+        </policy>
+      </overlayable>)";
+  EXPECT_FALSE(TestParse(input));
 }
 
 TEST_F(ResourceParserTest, ParseOverlayableBadPolicyError) {
diff --git a/tools/aapt2/ResourceTable.h b/tools/aapt2/ResourceTable.h
index 7ca99ea..32dfd26 100644
--- a/tools/aapt2/ResourceTable.h
+++ b/tools/aapt2/ResourceTable.h
@@ -92,6 +92,9 @@
 
     // The resource can be overlaid by any overlay on the product partition.
     kProduct = 0x08,
+
+    // The resource can be overlaid by any overlay signed with the same signature as its actor.
+    kSignature = 0x010,
   };
 
   std::shared_ptr<Overlayable> overlayable;
diff --git a/tools/aapt2/Resources.proto b/tools/aapt2/Resources.proto
index 9a1d942..a2fd7c6 100644
--- a/tools/aapt2/Resources.proto
+++ b/tools/aapt2/Resources.proto
@@ -138,10 +138,10 @@
 
 // Represents a set of overlayable resources.
 message Overlayable {
-  // The name of the <overlyabale>.
+  // The name of the <overlayable>.
   string name = 1;
 
-  // The location of the <overlyabale> declaration in the source.
+  // The location of the <overlayable> declaration in the source.
   Source source = 2;
 
   // The component responsible for enabling and disabling overlays targeting this <overlayable>.
diff --git a/tools/aapt2/format/binary/BinaryResourceParser.cpp b/tools/aapt2/format/binary/BinaryResourceParser.cpp
index 40aaa05..14906ad 100644
--- a/tools/aapt2/format/binary/BinaryResourceParser.cpp
+++ b/tools/aapt2/format/binary/BinaryResourceParser.cpp
@@ -473,6 +473,10 @@
           & ResTable_overlayable_policy_header::POLICY_PRODUCT_PARTITION) {
         policies |= OverlayableItem::Policy::kProduct;
       }
+      if (policy_header->policy_flags
+          & ResTable_overlayable_policy_header::POLICY_SIGNATURE) {
+        policies |= OverlayableItem::Policy::kSignature;
+      }
 
       const ResTable_ref* const ref_begin = reinterpret_cast<const ResTable_ref*>(
           ((uint8_t *)policy_header) + util::DeviceToHost32(policy_header->header.headerSize));
diff --git a/tools/aapt2/format/binary/TableFlattener.cpp b/tools/aapt2/format/binary/TableFlattener.cpp
index 9d341cc..5d7e291 100644
--- a/tools/aapt2/format/binary/TableFlattener.cpp
+++ b/tools/aapt2/format/binary/TableFlattener.cpp
@@ -274,7 +274,9 @@
       FlattenLibrarySpec(buffer);
     }
 
-    FlattenOverlayable(buffer);
+    if (!FlattenOverlayable(buffer)) {
+      return false;
+    }
 
     pkg_writer.Finish();
     return true;
@@ -468,23 +470,29 @@
           overlayable_chunk = &chunk;
         }
 
+        if (item.policies == 0) {
+          context_->GetDiagnostics()->Error(DiagMessage(item.overlayable->source)
+                                                << "overlayable "
+                                                << entry->name
+                                                << " does not specify policy");
+          return false;
+        }
+
         uint32_t policy_flags = 0;
-        if (item.policies == OverlayableItem::Policy::kNone) {
-          // Encode overlayable entries defined without a policy as publicly overlayable
+        if (item.policies & OverlayableItem::Policy::kPublic) {
           policy_flags |= ResTable_overlayable_policy_header::POLICY_PUBLIC;
-        } else {
-          if (item.policies & OverlayableItem::Policy::kPublic) {
-            policy_flags |= ResTable_overlayable_policy_header::POLICY_PUBLIC;
-          }
-          if (item.policies & OverlayableItem::Policy::kSystem) {
-            policy_flags |= ResTable_overlayable_policy_header::POLICY_SYSTEM_PARTITION;
-          }
-          if (item.policies & OverlayableItem::Policy::kVendor) {
-            policy_flags |= ResTable_overlayable_policy_header::POLICY_VENDOR_PARTITION;
-          }
-          if (item.policies & OverlayableItem::Policy::kProduct) {
-            policy_flags |= ResTable_overlayable_policy_header::POLICY_PRODUCT_PARTITION;
-          }
+        }
+        if (item.policies & OverlayableItem::Policy::kSystem) {
+          policy_flags |= ResTable_overlayable_policy_header::POLICY_SYSTEM_PARTITION;
+        }
+        if (item.policies & OverlayableItem::Policy::kVendor) {
+          policy_flags |= ResTable_overlayable_policy_header::POLICY_VENDOR_PARTITION;
+        }
+        if (item.policies & OverlayableItem::Policy::kProduct) {
+          policy_flags |= ResTable_overlayable_policy_header::POLICY_PRODUCT_PARTITION;
+        }
+        if (item.policies & OverlayableItem::Policy::kSignature) {
+          policy_flags |= ResTable_overlayable_policy_header::POLICY_SIGNATURE;
         }
 
         auto policy = overlayable_chunk->policy_ids.find(policy_flags);
diff --git a/tools/aapt2/format/binary/TableFlattener_test.cpp b/tools/aapt2/format/binary/TableFlattener_test.cpp
index ddc1173..4c5dbec 100644
--- a/tools/aapt2/format/binary/TableFlattener_test.cpp
+++ b/tools/aapt2/format/binary/TableFlattener_test.cpp
@@ -671,9 +671,6 @@
   overlayable_item_two.policies |= OverlayableItem::Policy::kSystem;
   overlayable_item_two.policies |= OverlayableItem::Policy::kVendor;
 
-  std::string name_three = "com.app.test:integer/overlayable_three_item";
-  OverlayableItem overlayable_item_three(overlayable);
-
   std::unique_ptr<ResourceTable> table =
       test::ResourceTableBuilder()
           .SetPackageId("com.app.test", 0x7f)
@@ -683,8 +680,6 @@
           .SetOverlayable(name_one, overlayable_item_one)
           .AddSimple(name_two, ResourceId(0x7f020002))
           .SetOverlayable(name_two, overlayable_item_two)
-          .AddSimple(name_three, ResourceId(0x7f020003))
-          .SetOverlayable(name_three, overlayable_item_three)
           .Build();
 
   ResourceTable output_table;
@@ -713,16 +708,6 @@
   EXPECT_EQ(overlayable_item.policies, OverlayableItem::Policy::kSystem
                                        | OverlayableItem::Policy::kProduct
                                        | OverlayableItem::Policy::kVendor);
-
-  search_result = output_table.FindResource(test::ParseNameOrDie(name_three));
-  ASSERT_TRUE(search_result);
-  ASSERT_THAT(search_result.value().entry, NotNull());
-  ASSERT_TRUE(search_result.value().entry->overlayable_item);
-  overlayable_item = search_result.value().entry->overlayable_item.value();
-  EXPECT_EQ(overlayable_item.policies, OverlayableItem::Policy::kPublic);
-  EXPECT_EQ(overlayable_item.overlayable->name, "TestName");
-  EXPECT_EQ(overlayable_item.overlayable->actor, "overlay://theme");
-  EXPECT_EQ(overlayable_item.policies, OverlayableItem::Policy::kPublic);
 }
 
 TEST_F(TableFlattenerTest, FlattenMultipleOverlayable) {
@@ -745,6 +730,8 @@
 
   std::string name_three = "com.app.test:integer/overlayable_three";
   OverlayableItem overlayable_item_three(group_one);
+  overlayable_item_three.policies |= OverlayableItem::Policy::kSignature;
+
   std::unique_ptr<ResourceTable> table =
       test::ResourceTableBuilder()
           .SetPackageId("com.app.test", 0x7f)
@@ -793,7 +780,22 @@
   result_overlayable = search_result.value().entry->overlayable_item.value();
   EXPECT_EQ(result_overlayable.overlayable->name, "OtherName");
   EXPECT_EQ(result_overlayable.overlayable->actor, "overlay://customization");
-  EXPECT_EQ(result_overlayable.policies, OverlayableItem::Policy::kPublic);
+  EXPECT_EQ(result_overlayable.policies, OverlayableItem::Policy::kSignature);
+}
+
+TEST_F(TableFlattenerTest, FlattenOverlayableNoPolicyFails) {
+  auto group = std::make_shared<Overlayable>("TestName", "overlay://theme");
+  std::string name_zero = "com.app.test:integer/overlayable_zero";
+  OverlayableItem overlayable_item_zero(group);
+
+  std::unique_ptr<ResourceTable> table =
+      test::ResourceTableBuilder()
+          .SetPackageId("com.app.test", 0x7f)
+          .AddSimple(name_zero, ResourceId(0x7f020000))
+          .SetOverlayable(name_zero, overlayable_item_zero)
+          .Build();
+  ResourceTable output_table;
+  ASSERT_FALSE(Flatten(context_.get(), {}, table.get(), &output_table));
 }
 
 }  // namespace aapt
diff --git a/tools/aapt2/format/proto/ProtoDeserialize.cpp b/tools/aapt2/format/proto/ProtoDeserialize.cpp
index aff1b39..06f1bf7 100644
--- a/tools/aapt2/format/proto/ProtoDeserialize.cpp
+++ b/tools/aapt2/format/proto/ProtoDeserialize.cpp
@@ -390,6 +390,9 @@
       case pb::OverlayableItem::PRODUCT:
         out_overlayable->policies |= OverlayableItem::Policy::kProduct;
         break;
+      case pb::OverlayableItem::SIGNATURE:
+        out_overlayable->policies |= OverlayableItem::Policy::kSignature;
+        break;
       default:
         *out_error = "unknown overlayable policy";
         return false;
diff --git a/tools/aapt2/format/proto/ProtoSerialize.cpp b/tools/aapt2/format/proto/ProtoSerialize.cpp
index b549e23..eb2b1a2 100644
--- a/tools/aapt2/format/proto/ProtoSerialize.cpp
+++ b/tools/aapt2/format/proto/ProtoSerialize.cpp
@@ -309,6 +309,9 @@
   if (overlayable_item.policies & OverlayableItem::Policy::kVendor) {
     pb_overlayable_item->add_policy(pb::OverlayableItem::VENDOR);
   }
+  if (overlayable_item.policies & OverlayableItem::Policy::kSignature) {
+    pb_overlayable_item->add_policy(pb::OverlayableItem::SIGNATURE);
+  }
 
   SerializeSourceToPb(overlayable_item.source, source_pool,
                       pb_overlayable_item->mutable_source());
diff --git a/tools/aapt2/format/proto/ProtoSerialize_test.cpp b/tools/aapt2/format/proto/ProtoSerialize_test.cpp
index cce3939..d369ac4 100644
--- a/tools/aapt2/format/proto/ProtoSerialize_test.cpp
+++ b/tools/aapt2/format/proto/ProtoSerialize_test.cpp
@@ -526,6 +526,10 @@
       "FontPack", "overlay://theme"));
   overlayable_item_baz.policies |= OverlayableItem::Policy::kPublic;
 
+  OverlayableItem overlayable_item_boz(std::make_shared<Overlayable>(
+      "IconPack", "overlay://theme"));
+  overlayable_item_boz.policies |= OverlayableItem::Policy::kSignature;
+
   OverlayableItem overlayable_item_biz(std::make_shared<Overlayable>(
       "Other", "overlay://customization"));
   overlayable_item_biz.comment ="comment";
@@ -536,6 +540,7 @@
           .SetOverlayable("com.app.a:bool/foo", overlayable_item_foo)
           .SetOverlayable("com.app.a:bool/bar", overlayable_item_bar)
           .SetOverlayable("com.app.a:bool/baz", overlayable_item_baz)
+          .SetOverlayable("com.app.a:bool/boz", overlayable_item_boz)
           .SetOverlayable("com.app.a:bool/biz", overlayable_item_biz)
           .AddValue("com.app.a:bool/fiz", ResourceUtils::TryParseBool("true"))
           .Build();
@@ -576,6 +581,14 @@
   EXPECT_THAT(overlayable_item.overlayable->actor, Eq("overlay://theme"));
   EXPECT_THAT(overlayable_item.policies, Eq(OverlayableItem::Policy::kPublic));
 
+  search_result = new_table.FindResource(test::ParseNameOrDie("com.app.a:bool/boz"));
+  ASSERT_TRUE(search_result);
+  ASSERT_TRUE(search_result.value().entry->overlayable_item);
+  overlayable_item = search_result.value().entry->overlayable_item.value();
+  EXPECT_THAT(overlayable_item.overlayable->name, Eq("IconPack"));
+  EXPECT_THAT(overlayable_item.overlayable->actor, Eq("overlay://theme"));
+  EXPECT_THAT(overlayable_item.policies, Eq(OverlayableItem::Policy::kSignature));
+
   search_result = new_table.FindResource(test::ParseNameOrDie("com.app.a:bool/biz"));
   ASSERT_TRUE(search_result);
   ASSERT_TRUE(search_result.value().entry->overlayable_item);