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);