summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.mk1
-rw-r--r--api/current.txt3
-rw-r--r--api/system-current.txt2
-rw-r--r--cmds/statsd/Android.mk7
-rw-r--r--cmds/statsd/src/HashableDimensionKey.h2
-rw-r--r--cmds/statsd/src/StatsLogProcessor.cpp24
-rw-r--r--cmds/statsd/src/StatsLogProcessor.h16
-rw-r--r--cmds/statsd/src/StatsService.cpp119
-rw-r--r--cmds/statsd/src/StatsService.h13
-rw-r--r--cmds/statsd/src/anomaly/AlarmMonitor.cpp (renamed from cmds/statsd/src/anomaly/AnomalyMonitor.cpp)45
-rw-r--r--cmds/statsd/src/anomaly/AlarmMonitor.h (renamed from cmds/statsd/src/anomaly/AnomalyMonitor.h)34
-rw-r--r--cmds/statsd/src/anomaly/AlarmTracker.cpp79
-rw-r--r--cmds/statsd/src/anomaly/AlarmTracker.h76
-rw-r--r--cmds/statsd/src/anomaly/AnomalyTracker.cpp36
-rw-r--r--cmds/statsd/src/anomaly/AnomalyTracker.h17
-rw-r--r--cmds/statsd/src/anomaly/DurationAnomalyTracker.cpp25
-rw-r--r--cmds/statsd/src/anomaly/DurationAnomalyTracker.h19
-rw-r--r--cmds/statsd/src/anomaly/subscriber_util.cpp75
-rw-r--r--cmds/statsd/src/anomaly/subscriber_util.h34
-rw-r--r--cmds/statsd/src/atoms.proto5
-rw-r--r--cmds/statsd/src/external/Perfetto.h2
-rw-r--r--cmds/statsd/src/guardrail/StatsdStats.cpp24
-rw-r--r--cmds/statsd/src/guardrail/StatsdStats.h8
-rw-r--r--cmds/statsd/src/metrics/DurationMetricProducer.cpp6
-rw-r--r--cmds/statsd/src/metrics/DurationMetricProducer.h3
-rw-r--r--cmds/statsd/src/metrics/MetricProducer.h3
-rw-r--r--cmds/statsd/src/metrics/MetricsManager.cpp71
-rw-r--r--cmds/statsd/src/metrics/MetricsManager.h15
-rw-r--r--cmds/statsd/src/metrics/metrics_manager_util.cpp76
-rw-r--r--cmds/statsd/src/metrics/metrics_manager_util.h14
-rw-r--r--cmds/statsd/src/stats_log.proto2
-rw-r--r--cmds/statsd/src/storage/StorageManager.cpp14
-rw-r--r--cmds/statsd/src/storage/StorageManager.h5
-rw-r--r--cmds/statsd/src/subscriber/IncidentdReporter.cpp6
-rw-r--r--cmds/statsd/src/subscriber/IncidentdReporter.h2
-rw-r--r--cmds/statsd/tests/AlarmMonitor_test.cpp (renamed from cmds/statsd/tests/AnomalyMonitor_test.cpp)21
-rw-r--r--cmds/statsd/tests/ConfigManager_test.cpp8
-rw-r--r--cmds/statsd/tests/MetricsManager_test.cpp56
-rw-r--r--cmds/statsd/tests/StatsLogProcessor_test.cpp27
-rw-r--r--cmds/statsd/tests/UidMap_test.cpp6
-rw-r--r--cmds/statsd/tests/anomaly/AlarmTracker_test.cpp68
-rw-r--r--cmds/statsd/tests/metrics/CountMetricProducer_test.cpp6
-rw-r--r--cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp3
-rw-r--r--cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp7
-rw-r--r--cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp14
-rw-r--r--cmds/statsd/tests/metrics/OringDurationTracker_test.cpp16
-rw-r--r--cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp3
-rw-r--r--cmds/statsd/tests/statsd_test_util.cpp5
-rw-r--r--core/java/android/app/ActivityThread.java12
-rw-r--r--core/java/android/app/ClientTransactionHandler.java3
-rw-r--r--core/java/android/app/StatsManager.java20
-rw-r--r--core/java/android/app/servertransaction/PauseActivityItem.java4
-rw-r--r--core/java/android/app/servertransaction/TransactionExecutor.java4
-rw-r--r--core/java/android/content/ClipboardManager.java54
-rw-r--r--core/java/android/content/IClipboard.aidl1
-rw-r--r--core/java/android/content/res/Configuration.java14
-rw-r--r--core/java/android/os/IStatsCompanionService.aidl14
-rw-r--r--core/java/android/os/IStatsManager.aidl7
-rw-r--r--core/java/android/provider/CallLog.java8
-rw-r--r--core/java/android/provider/Settings.java1
-rw-r--r--core/java/android/security/ConfirmationDialog.java38
-rw-r--r--core/java/android/security/keystore/BadCertificateFormatException.java3
-rw-r--r--core/java/android/security/keystore/DecryptionFailedException.java4
-rw-r--r--core/java/android/security/keystore/InternalRecoveryServiceException.java6
-rw-r--r--core/java/android/security/keystore/KeyDerivationParams.java4
-rw-r--r--core/java/android/security/keystore/KeychainProtectionParams.java18
-rw-r--r--core/java/android/security/keystore/KeychainSnapshot.java16
-rw-r--r--core/java/android/security/keystore/LockScreenRequiredException.java5
-rw-r--r--core/java/android/security/keystore/RecoveryClaim.java3
-rw-r--r--core/java/android/security/keystore/RecoveryController.java16
-rw-r--r--core/java/android/security/keystore/RecoveryControllerException.java3
-rw-r--r--core/java/android/security/keystore/RecoverySession.java4
-rw-r--r--core/java/android/security/keystore/SessionExpiredException.java3
-rw-r--r--core/java/android/security/keystore/WrappedApplicationKey.java11
-rw-r--r--core/java/android/security/keystore/recovery/RecoveryClaim.java55
-rw-r--r--core/java/android/security/keystore/recovery/RecoveryController.java43
-rw-r--r--core/java/android/service/notification/NotificationListenerService.java1
-rw-r--r--core/java/android/util/StatsLog.java3
-rw-r--r--core/java/android/widget/Magnifier.java8
-rw-r--r--core/java/com/android/internal/widget/ILockSettings.aidl1
-rw-r--r--core/jni/android_media_AudioTrack.cpp2
-rw-r--r--core/proto/android/server/alarmmanagerservice.proto2
-rw-r--r--core/res/res/values/dimens.xml2
-rw-r--r--keystore/java/android/security/KeyStore.java14
-rw-r--r--libs/hwui/JankTracker.cpp2
-rw-r--r--media/jni/android_media_MediaCodec.cpp2
-rw-r--r--media/jni/android_media_MediaCrypto.cpp4
-rw-r--r--media/jni/android_media_MediaDrm.cpp4
-rw-r--r--packages/SystemUI/res/values/config.xml2
-rw-r--r--packages/SystemUI/res/values/strings.xml2
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/PageIndicator.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java21
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java50
-rw-r--r--proto/src/metrics_constants.proto15
-rw-r--r--services/core/java/com/android/server/AlarmManagerService.java32
-rw-r--r--services/core/java/com/android/server/am/ActivityDisplay.java12
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java19
-rw-r--r--services/core/java/com/android/server/am/ActivityRecord.java18
-rw-r--r--services/core/java/com/android/server/audio/AudioService.java25
-rw-r--r--services/core/java/com/android/server/camera/CameraServiceProxy.java16
-rw-r--r--services/core/java/com/android/server/clipboard/ClipboardService.java152
-rw-r--r--services/core/java/com/android/server/locksettings/LockSettingsService.java5
-rw-r--r--services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyGenerator.java53
-rw-r--r--services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java61
-rw-r--r--services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbContract.java116
-rw-r--r--services/core/java/com/android/server/notification/ValidateNotificationPeople.java33
-rw-r--r--services/core/java/com/android/server/stats/StatsCompanionService.java55
-rw-r--r--services/core/java/com/android/server/wm/AppWindowToken.java22
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java7
-rw-r--r--services/core/java/com/android/server/wm/WindowContainer.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java16
-rw-r--r--services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java8
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyGeneratorTest.java27
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java34
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java17
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/UsbTerminalTypes.java1
-rw-r--r--telephony/java/android/telephony/CarrierConfigManager.java11
-rw-r--r--telephony/java/android/telephony/SignalStrength.java37
-rw-r--r--tests/UsbTests/res/raw/readme.txt35
-rw-r--r--tests/UsbTests/res/raw/usbdescriptors_headphones.binbin0 -> 180 bytes
-rw-r--r--tests/UsbTests/res/raw/usbdescriptors_headset.binbin0 -> 305 bytes
-rw-r--r--tests/UsbTests/res/raw/usbdescriptors_lineout.binbin0 -> 180 bytes
-rw-r--r--tests/UsbTests/res/raw/usbdescriptors_nothing.binbin0 -> 52 bytes
-rw-r--r--tests/UsbTests/src/com/android/server/usb/UsbDescriptorParserTests.java115
-rw-r--r--tests/net/java/com/android/server/IpSecServiceParameterizedTest.java1
-rw-r--r--tools/aapt2/format/binary/XmlFlattener.cpp58
-rw-r--r--tools/aapt2/format/binary/XmlFlattener_test.cpp88
-rw-r--r--tools/aapt2/xml/XmlDom.cpp20
129 files changed, 1998 insertions, 740 deletions
diff --git a/Android.mk b/Android.mk
index 59278b03a7d2..470714b184ae 100644
--- a/Android.mk
+++ b/Android.mk
@@ -194,6 +194,7 @@ framework_docs_LOCAL_DROIDDOC_OPTIONS := \
-since $(SRC_API_DIR)/25.txt 25 \
-since $(SRC_API_DIR)/26.txt 26 \
-since $(SRC_API_DIR)/27.txt 27 \
+ -since ./frameworks/base/api/current.txt P \
-werror -lerror -hide 111 -hide 113 -hide 125 -hide 126 -hide 127 -hide 128 \
-overview $(LOCAL_PATH)/core/java/overview.html \
diff --git a/api/current.txt b/api/current.txt
index fff502a577db..41cf0536d147 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -8972,6 +8972,7 @@ package android.content {
public class ClipboardManager extends android.text.ClipboardManager {
method public void addPrimaryClipChangedListener(android.content.ClipboardManager.OnPrimaryClipChangedListener);
+ method public void clearPrimaryClip();
method public android.content.ClipData getPrimaryClip();
method public android.content.ClipDescription getPrimaryClipDescription();
method public deprecated java.lang.CharSequence getText();
@@ -35103,7 +35104,7 @@ package android.provider {
field public static final java.lang.String FEATURES = "features";
field public static final int FEATURES_HD_CALL = 4; // 0x4
field public static final int FEATURES_PULLED_EXTERNALLY = 2; // 0x2
- field public static final int FEATURES_RTT = 16; // 0x10
+ field public static final int FEATURES_RTT = 32; // 0x20
field public static final int FEATURES_VIDEO = 1; // 0x1
field public static final int FEATURES_WIFI = 8; // 0x8
field public static final java.lang.String GEOCODED_LOCATION = "geocoded_location";
diff --git a/api/system-current.txt b/api/system-current.txt
index 003a9ba8199f..bd1eef69e889 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -368,7 +368,6 @@ package android.app {
}
public final class StatsManager {
- method public boolean addConfiguration(long, byte[], java.lang.String, java.lang.String);
method public boolean addConfiguration(long, byte[]);
method public byte[] getData(long);
method public byte[] getMetadata();
@@ -4181,6 +4180,7 @@ package android.provider {
field public static final java.lang.String CARRIER_APP_NAMES = "carrier_app_names";
field public static final java.lang.String CARRIER_APP_WHITELIST = "carrier_app_whitelist";
field public static final java.lang.String DEFAULT_SM_DP_PLUS = "default_sm_dp_plus";
+ field public static final java.lang.String EUICC_PROVISIONED = "euicc_provisioned";
field public static final java.lang.String INSTALL_CARRIER_APP_NOTIFICATION_PERSISTENT = "install_carrier_app_notification_persistent";
field public static final java.lang.String INSTALL_CARRIER_APP_NOTIFICATION_SLEEP_MILLIS = "install_carrier_app_notification_sleep_millis";
field public static final java.lang.String OTA_DISABLE_AUTOMATIC_UPDATE = "ota_disable_automatic_update";
diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk
index d7ce352d4b78..7f76ab14a171 100644
--- a/cmds/statsd/Android.mk
+++ b/cmds/statsd/Android.mk
@@ -21,9 +21,11 @@ statsd_common_src := \
src/statsd_config.proto \
src/FieldValue.cpp \
src/stats_log_util.cpp \
- src/anomaly/AnomalyMonitor.cpp \
+ src/anomaly/AlarmMonitor.cpp \
+ src/anomaly/AlarmTracker.cpp \
src/anomaly/AnomalyTracker.cpp \
src/anomaly/DurationAnomalyTracker.cpp \
+ src/anomaly/subscriber_util.cpp \
src/condition/CombinationConditionTracker.cpp \
src/condition/condition_util.cpp \
src/condition/SimpleConditionTracker.cpp \
@@ -172,7 +174,8 @@ LOCAL_SRC_FILES := \
src/atom_field_options.proto \
src/atoms.proto \
src/stats_log.proto \
- tests/AnomalyMonitor_test.cpp \
+ tests/AlarmMonitor_test.cpp \
+ tests/anomaly/AlarmTracker_test.cpp \
tests/anomaly/AnomalyTracker_test.cpp \
tests/ConfigManager_test.cpp \
tests/external/puller_util_test.cpp \
diff --git a/cmds/statsd/src/HashableDimensionKey.h b/cmds/statsd/src/HashableDimensionKey.h
index 89fe317834d8..5d016e9f2804 100644
--- a/cmds/statsd/src/HashableDimensionKey.h
+++ b/cmds/statsd/src/HashableDimensionKey.h
@@ -40,7 +40,7 @@ public:
mValues = values;
}
- HashableDimensionKey(){};
+ HashableDimensionKey() {};
HashableDimensionKey(const HashableDimensionKey& that) : mValues(that.getValues()){};
diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp
index 3c9dd68c1cc2..087e59657247 100644
--- a/cmds/statsd/src/StatsLogProcessor.cpp
+++ b/cmds/statsd/src/StatsLogProcessor.cpp
@@ -66,11 +66,13 @@ const int FIELD_ID_CURRENT_REPORT_ELAPSED_NANOS = 4;
#define STATS_DATA_DIR "/data/misc/stats-data"
StatsLogProcessor::StatsLogProcessor(const sp<UidMap>& uidMap,
- const sp<AnomalyMonitor>& anomalyMonitor,
+ const sp<AlarmMonitor>& anomalyAlarmMonitor,
+ const sp<AlarmMonitor>& periodicAlarmMonitor,
const long timeBaseSec,
const std::function<void(const ConfigKey&)>& sendBroadcast)
: mUidMap(uidMap),
- mAnomalyMonitor(anomalyMonitor),
+ mAnomalyAlarmMonitor(anomalyAlarmMonitor),
+ mPeriodicAlarmMonitor(periodicAlarmMonitor),
mSendBroadcast(sendBroadcast),
mTimeBaseSec(timeBaseSec) {
StatsPullerManager statsPullerManager;
@@ -82,10 +84,19 @@ StatsLogProcessor::~StatsLogProcessor() {
void StatsLogProcessor::onAnomalyAlarmFired(
const uint64_t timestampNs,
- unordered_set<sp<const AnomalyAlarm>, SpHash<AnomalyAlarm>> anomalySet) {
+ unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>> alarmSet) {
std::lock_guard<std::mutex> lock(mMetricsMutex);
for (const auto& itr : mMetricsManagers) {
- itr.second->onAnomalyAlarmFired(timestampNs, anomalySet);
+ itr.second->onAnomalyAlarmFired(timestampNs, alarmSet);
+ }
+}
+void StatsLogProcessor::onPeriodicAlarmFired(
+ const uint64_t timestampNs,
+ unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>> alarmSet) {
+
+ std::lock_guard<std::mutex> lock(mMetricsMutex);
+ for (const auto& itr : mMetricsManagers) {
+ itr.second->onPeriodicAlarmFired(timestampNs, alarmSet);
}
}
@@ -170,7 +181,9 @@ void StatsLogProcessor::OnLogEvent(LogEvent* event) {
void StatsLogProcessor::OnConfigUpdated(const ConfigKey& key, const StatsdConfig& config) {
std::lock_guard<std::mutex> lock(mMetricsMutex);
VLOG("Updated configuration for key %s", key.ToString().c_str());
- sp<MetricsManager> newMetricsManager = new MetricsManager(key, config, mTimeBaseSec, mUidMap);
+ sp<MetricsManager> newMetricsManager =
+ new MetricsManager(key, config, mTimeBaseSec, mUidMap,
+ mAnomalyAlarmMonitor, mPeriodicAlarmMonitor);
auto it = mMetricsManagers.find(key);
if (it == mMetricsManagers.end() && mMetricsManagers.size() > StatsdStats::kMaxConfigCount) {
ALOGE("Can't accept more configs!");
@@ -179,7 +192,6 @@ void StatsLogProcessor::OnConfigUpdated(const ConfigKey& key, const StatsdConfig
if (newMetricsManager->isConfigValid()) {
mUidMap->OnConfigUpdated(key);
- newMetricsManager->setAnomalyMonitor(mAnomalyMonitor);
if (newMetricsManager->shouldAddUidMapListener()) {
// We have to add listener after the MetricsManager is constructed because it's
// not safe to create wp or sp from this pointer inside its constructor.
diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h
index 144430639d9f..4d9f18509ddb 100644
--- a/cmds/statsd/src/StatsLogProcessor.h
+++ b/cmds/statsd/src/StatsLogProcessor.h
@@ -34,7 +34,8 @@ namespace statsd {
class StatsLogProcessor : public ConfigListener {
public:
- StatsLogProcessor(const sp<UidMap>& uidMap, const sp<AnomalyMonitor>& anomalyMonitor,
+ StatsLogProcessor(const sp<UidMap>& uidMap, const sp<AlarmMonitor>& anomalyAlarmMonitor,
+ const sp<AlarmMonitor>& subscriberTriggerAlarmMonitor,
const long timeBaseSec,
const std::function<void(const ConfigKey&)>& sendBroadcast);
virtual ~StatsLogProcessor();
@@ -48,10 +49,15 @@ public:
void onDumpReport(const ConfigKey& key, const uint64_t dumpTimeNs, vector<uint8_t>* outData);
- /* Tells MetricsManager that the alarms in anomalySet have fired. Modifies anomalySet. */
+ /* Tells MetricsManager that the alarms in alarmSet have fired. Modifies anomaly alarmSet. */
void onAnomalyAlarmFired(
const uint64_t timestampNs,
- unordered_set<sp<const AnomalyAlarm>, SpHash<AnomalyAlarm>> anomalySet);
+ unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>> alarmSet);
+
+ /* Tells MetricsManager that the alarms in alarmSet have fired. Modifies periodic alarmSet. */
+ void onPeriodicAlarmFired(
+ const uint64_t timestampNs,
+ unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>> alarmSet);
/* Flushes data to disk. Data on memory will be gone after written to disk. */
void WriteDataToDisk();
@@ -76,7 +82,9 @@ private:
StatsPullerManager mStatsPullerManager;
- sp<AnomalyMonitor> mAnomalyMonitor;
+ sp<AlarmMonitor> mAnomalyAlarmMonitor;
+
+ sp<AlarmMonitor> mPeriodicAlarmMonitor;
void onDumpReportLocked(const ConfigKey& key, const uint64_t dumpTimeNs,
vector<uint8_t>* outData);
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index c27b130678cd..ee4f434aa6a2 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -50,49 +50,73 @@ namespace statsd {
constexpr const char* kPermissionDump = "android.permission.DUMP";
#define STATS_SERVICE_DIR "/data/misc/stats-service"
-// ======================================================================
/**
* Watches for the death of the stats companion (system process).
*/
class CompanionDeathRecipient : public IBinder::DeathRecipient {
public:
- CompanionDeathRecipient(const sp<AnomalyMonitor>& anomalyMonitor);
+ CompanionDeathRecipient(const sp<AlarmMonitor>& anomalyAlarmMonitor,
+ const sp<AlarmMonitor>& periodicAlarmMonitor) :
+ mAnomalyAlarmMonitor(anomalyAlarmMonitor),
+ mPeriodicAlarmMonitor(periodicAlarmMonitor) {}
virtual void binderDied(const wp<IBinder>& who);
private:
- const sp<AnomalyMonitor> mAnomalyMonitor;
+ sp<AlarmMonitor> mAnomalyAlarmMonitor;
+ sp<AlarmMonitor> mPeriodicAlarmMonitor;
};
-CompanionDeathRecipient::CompanionDeathRecipient(const sp<AnomalyMonitor>& anomalyMonitor)
- : mAnomalyMonitor(anomalyMonitor) {
-}
-
void CompanionDeathRecipient::binderDied(const wp<IBinder>& who) {
ALOGW("statscompanion service died");
- mAnomalyMonitor->setStatsCompanionService(nullptr);
+ mAnomalyAlarmMonitor->setStatsCompanionService(nullptr);
+ mPeriodicAlarmMonitor->setStatsCompanionService(nullptr);
SubscriberReporter::getInstance().setStatsCompanionService(nullptr);
}
-// ======================================================================
StatsService::StatsService(const sp<Looper>& handlerLooper)
- : mAnomalyMonitor(new AnomalyMonitor(MIN_DIFF_TO_UPDATE_REGISTERED_ALARM_SECS))
-{
+ : mAnomalyAlarmMonitor(new AlarmMonitor(MIN_DIFF_TO_UPDATE_REGISTERED_ALARM_SECS,
+ [](const sp<IStatsCompanionService>& sc, int64_t timeMillis) {
+ if (sc != nullptr) {
+ sc->setAnomalyAlarm(timeMillis);
+ StatsdStats::getInstance().noteRegisteredAnomalyAlarmChanged();
+ }
+ },
+ [](const sp<IStatsCompanionService>& sc) {
+ if (sc != nullptr) {
+ sc->cancelAnomalyAlarm();
+ StatsdStats::getInstance().noteRegisteredAnomalyAlarmChanged();
+ }
+ })),
+ mPeriodicAlarmMonitor(new AlarmMonitor(MIN_DIFF_TO_UPDATE_REGISTERED_ALARM_SECS,
+ [](const sp<IStatsCompanionService>& sc, int64_t timeMillis) {
+ if (sc != nullptr) {
+ sc->setAlarmForSubscriberTriggering(timeMillis);
+ StatsdStats::getInstance().noteRegisteredPeriodicAlarmChanged();
+ }
+ },
+ [](const sp<IStatsCompanionService>& sc) {
+ if (sc != nullptr) {
+ sc->cancelAlarmForSubscriberTriggering();
+ StatsdStats::getInstance().noteRegisteredPeriodicAlarmChanged();
+ }
+
+ })) {
mUidMap = new UidMap();
StatsPuller::SetUidMap(mUidMap);
mConfigManager = new ConfigManager();
- mProcessor = new StatsLogProcessor(mUidMap, mAnomalyMonitor, getElapsedRealtimeSec(),
- [this](const ConfigKey& key) {
- sp<IStatsCompanionService> sc = getStatsCompanionService();
- auto receiver = mConfigManager->GetConfigReceiver(key);
- if (sc == nullptr) {
- VLOG("Could not find StatsCompanionService");
- } else if (receiver == nullptr) {
- VLOG("Statscompanion could not find a broadcast receiver for %s",
- key.ToString().c_str());
- } else {
- sc->sendDataBroadcast(receiver);
- }
+ mProcessor = new StatsLogProcessor(mUidMap, mAnomalyAlarmMonitor, mPeriodicAlarmMonitor,
+ getElapsedRealtimeSec(), [this](const ConfigKey& key) {
+ sp<IStatsCompanionService> sc = getStatsCompanionService();
+ auto receiver = mConfigManager->GetConfigReceiver(key);
+ if (sc == nullptr) {
+ VLOG("Could not find StatsCompanionService");
+ } else if (receiver == nullptr) {
+ VLOG("Statscompanion could not find a broadcast receiver for %s",
+ key.ToString().c_str());
+ } else {
+ sc->sendDataBroadcast(receiver);
}
+ }
);
mConfigManager->AddListener(mProcessor);
@@ -423,6 +447,13 @@ status_t StatsService::cmd_config(FILE* in, FILE* out, FILE* err, Vector<String8
}
if (args[1] == "update") {
+ char* endp;
+ int64_t configID = strtoll(name.c_str(), &endp, 10);
+ if (endp == name.c_str() || *endp != '\0') {
+ fprintf(err, "Error parsing config ID.\n");
+ return UNKNOWN_ERROR;
+ }
+
// Read stream into buffer.
string buffer;
if (!android::base::ReadFdToString(fileno(in), &buffer)) {
@@ -438,7 +469,7 @@ status_t StatsService::cmd_config(FILE* in, FILE* out, FILE* err, Vector<String8
}
// Add / update the config.
- mConfigManager->UpdateConfig(ConfigKey(uid, StrToInt64(name)), config);
+ mConfigManager->UpdateConfig(ConfigKey(uid, configID), config);
} else {
if (argCount == 2) {
cmd_remove_all_configs(out);
@@ -615,7 +646,8 @@ status_t StatsService::cmd_dump_memory_info(FILE* out) {
status_t StatsService::cmd_clear_puller_cache(FILE* out) {
IPCThreadState* ipc = IPCThreadState::self();
- VLOG("StatsService::cmd_clear_puller_cache with Pid %i, Uid %i", ipc->getCallingPid(), ipc->getCallingUid());
+ VLOG("StatsService::cmd_clear_puller_cache with Pid %i, Uid %i",
+ ipc->getCallingPid(), ipc->getCallingUid());
if (checkCallingPermission(String16(kPermissionDump))) {
int cleared = mStatsPullerManager.ForceClearPullerCache();
fprintf(out, "Puller removed %d cached data!\n", cleared);
@@ -670,18 +702,40 @@ Status StatsService::informAnomalyAlarmFired() {
return Status::fromExceptionCode(Status::EX_SECURITY,
"Only system uid can call informAnomalyAlarmFired");
}
+
uint64_t currentTimeSec = getElapsedRealtimeSec();
- std::unordered_set<sp<const AnomalyAlarm>, SpHash<AnomalyAlarm>> anomalySet =
- mAnomalyMonitor->popSoonerThan(static_cast<uint32_t>(currentTimeSec));
- if (anomalySet.size() > 0) {
+ std::unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>> alarmSet =
+ mAnomalyAlarmMonitor->popSoonerThan(static_cast<uint32_t>(currentTimeSec));
+ if (alarmSet.size() > 0) {
VLOG("Found an anomaly alarm that fired.");
- mProcessor->onAnomalyAlarmFired(currentTimeSec * NS_PER_SEC, anomalySet);
+ mProcessor->onAnomalyAlarmFired(currentTimeSec * NS_PER_SEC, alarmSet);
} else {
VLOG("Cannot find an anomaly alarm that fired. Perhaps it was recently cancelled.");
}
return Status::ok();
}
+Status StatsService::informAlarmForSubscriberTriggeringFired() {
+ VLOG("StatsService::informAlarmForSubscriberTriggeringFired was called");
+
+ if (IPCThreadState::self()->getCallingUid() != AID_SYSTEM) {
+ return Status::fromExceptionCode(
+ Status::EX_SECURITY,
+ "Only system uid can call informAlarmForSubscriberTriggeringFired");
+ }
+
+ uint64_t currentTimeSec = time(nullptr);
+ std::unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>> alarmSet =
+ mPeriodicAlarmMonitor->popSoonerThan(static_cast<uint32_t>(currentTimeSec));
+ if (alarmSet.size() > 0) {
+ VLOG("Found periodic alarm fired.");
+ mProcessor->onPeriodicAlarmFired(currentTimeSec * NS_PER_SEC, alarmSet);
+ } else {
+ ALOGW("Cannot find an periodic alarm that fired. Perhaps it was recently cancelled.");
+ }
+ return Status::ok();
+}
+
Status StatsService::informPollAlarmFired() {
VLOG("StatsService::informPollAlarmFired was called");
@@ -766,10 +820,11 @@ Status StatsService::statsCompanionReady() {
"statscompanion unavailable despite it contacting statsd!");
}
VLOG("StatsService::statsCompanionReady linking to statsCompanion.");
- IInterface::asBinder(statsCompanion)->linkToDeath(new CompanionDeathRecipient(mAnomalyMonitor));
- mAnomalyMonitor->setStatsCompanionService(statsCompanion);
+ IInterface::asBinder(statsCompanion)->linkToDeath(
+ new CompanionDeathRecipient(mAnomalyAlarmMonitor, mPeriodicAlarmMonitor));
+ mAnomalyAlarmMonitor->setStatsCompanionService(statsCompanion);
+ mPeriodicAlarmMonitor->setStatsCompanionService(statsCompanion);
SubscriberReporter::getInstance().setStatsCompanionService(statsCompanion);
-
return Status::ok();
}
diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h
index 9690de702c24..e0a1299a9c38 100644
--- a/cmds/statsd/src/StatsService.h
+++ b/cmds/statsd/src/StatsService.h
@@ -18,7 +18,7 @@
#define STATS_SERVICE_H
#include "StatsLogProcessor.h"
-#include "anomaly/AnomalyMonitor.h"
+#include "anomaly/AlarmMonitor.h"
#include "config/ConfigManager.h"
#include "external/StatsPullerManager.h"
#include "packages/UidMap.h"
@@ -58,6 +58,8 @@ public:
virtual Status statsCompanionReady();
virtual Status informAnomalyAlarmFired();
virtual Status informPollAlarmFired();
+ virtual Status informAlarmForSubscriberTriggeringFired();
+
virtual Status informAllUidData(const vector<int32_t>& uid, const vector<int64_t>& version,
const vector<String16>& app);
virtual Status informOnePackage(const String16& app, int32_t uid, int64_t version);
@@ -244,9 +246,14 @@ private:
sp<StatsLogProcessor> mProcessor;
/**
- * The anomaly detector.
+ * The alarm monitor for anomaly detection.
+ */
+ const sp<AlarmMonitor> mAnomalyAlarmMonitor;
+
+ /**
+ * The alarm monitor for alarms to directly trigger subscriber.
*/
- const sp<AnomalyMonitor> mAnomalyMonitor;
+ const sp<AlarmMonitor> mPeriodicAlarmMonitor;
/**
* Whether this is an eng build.
diff --git a/cmds/statsd/src/anomaly/AnomalyMonitor.cpp b/cmds/statsd/src/anomaly/AlarmMonitor.cpp
index ca34dc6d87fd..78f0c2b09537 100644
--- a/cmds/statsd/src/anomaly/AnomalyMonitor.cpp
+++ b/cmds/statsd/src/anomaly/AlarmMonitor.cpp
@@ -17,21 +17,24 @@
#define DEBUG false
#include "Log.h"
-#include "anomaly/AnomalyMonitor.h"
+#include "anomaly/AlarmMonitor.h"
#include "guardrail/StatsdStats.h"
namespace android {
namespace os {
namespace statsd {
-AnomalyMonitor::AnomalyMonitor(uint32_t minDiffToUpdateRegisteredAlarmTimeSec)
- : mRegisteredAlarmTimeSec(0), mMinUpdateTimeSec(minDiffToUpdateRegisteredAlarmTimeSec) {
-}
+AlarmMonitor::AlarmMonitor(
+ uint32_t minDiffToUpdateRegisteredAlarmTimeSec,
+ const std::function<void(const sp<IStatsCompanionService>&, int64_t)>& updateAlarm,
+ const std::function<void(const sp<IStatsCompanionService>&)>& cancelAlarm)
+ : mRegisteredAlarmTimeSec(0), mMinUpdateTimeSec(minDiffToUpdateRegisteredAlarmTimeSec),
+ mUpdateAlarm(updateAlarm),
+ mCancelAlarm(cancelAlarm) {}
-AnomalyMonitor::~AnomalyMonitor() {
-}
+AlarmMonitor::~AlarmMonitor() {}
-void AnomalyMonitor::setStatsCompanionService(sp<IStatsCompanionService> statsCompanionService) {
+void AlarmMonitor::setStatsCompanionService(sp<IStatsCompanionService> statsCompanionService) {
std::lock_guard<std::mutex> lock(mLock);
sp<IStatsCompanionService> tmpForLock = mStatsCompanionService;
mStatsCompanionService = statsCompanionService;
@@ -40,13 +43,13 @@ void AnomalyMonitor::setStatsCompanionService(sp<IStatsCompanionService> statsCo
return;
}
VLOG("Creating link to statsCompanionService");
- const sp<const AnomalyAlarm> top = mPq.top();
+ const sp<const InternalAlarm> top = mPq.top();
if (top != nullptr) {
updateRegisteredAlarmTime_l(top->timestampSec);
}
}
-void AnomalyMonitor::add(sp<const AnomalyAlarm> alarm) {
+void AlarmMonitor::add(sp<const InternalAlarm> alarm) {
std::lock_guard<std::mutex> lock(mLock);
if (alarm == nullptr) {
ALOGW("Asked to add a null alarm.");
@@ -66,7 +69,7 @@ void AnomalyMonitor::add(sp<const AnomalyAlarm> alarm) {
}
}
-void AnomalyMonitor::remove(sp<const AnomalyAlarm> alarm) {
+void AlarmMonitor::remove(sp<const InternalAlarm> alarm) {
std::lock_guard<std::mutex> lock(mLock);
if (alarm == nullptr) {
ALOGW("Asked to remove a null alarm.");
@@ -89,13 +92,13 @@ void AnomalyMonitor::remove(sp<const AnomalyAlarm> alarm) {
// More efficient than repeatedly calling remove(mPq.top()) since it batches the
// updates to the registered alarm.
-unordered_set<sp<const AnomalyAlarm>, SpHash<AnomalyAlarm>> AnomalyMonitor::popSoonerThan(
+unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>> AlarmMonitor::popSoonerThan(
uint32_t timestampSec) {
VLOG("Removing alarms with time <= %u", timestampSec);
- unordered_set<sp<const AnomalyAlarm>, SpHash<AnomalyAlarm>> oldAlarms;
+ unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>> oldAlarms;
std::lock_guard<std::mutex> lock(mLock);
- for (sp<const AnomalyAlarm> t = mPq.top(); t != nullptr && t->timestampSec <= timestampSec;
+ for (sp<const InternalAlarm> t = mPq.top(); t != nullptr && t->timestampSec <= timestampSec;
t = mPq.top()) {
oldAlarms.insert(t);
mPq.pop(); // remove t
@@ -113,25 +116,19 @@ unordered_set<sp<const AnomalyAlarm>, SpHash<AnomalyAlarm>> AnomalyMonitor::popS
return oldAlarms;
}
-void AnomalyMonitor::updateRegisteredAlarmTime_l(uint32_t timestampSec) {
+void AlarmMonitor::updateRegisteredAlarmTime_l(uint32_t timestampSec) {
VLOG("Updating reg alarm time to %u", timestampSec);
mRegisteredAlarmTimeSec = timestampSec;
- if (mStatsCompanionService != nullptr) {
- mStatsCompanionService->setAnomalyAlarm(secToMs(mRegisteredAlarmTimeSec));
- StatsdStats::getInstance().noteRegisteredAnomalyAlarmChanged();
- }
+ mUpdateAlarm(mStatsCompanionService, secToMs(mRegisteredAlarmTimeSec));
}
-void AnomalyMonitor::cancelRegisteredAlarmTime_l() {
+void AlarmMonitor::cancelRegisteredAlarmTime_l() {
VLOG("Cancelling reg alarm.");
mRegisteredAlarmTimeSec = 0;
- if (mStatsCompanionService != nullptr) {
- mStatsCompanionService->cancelAnomalyAlarm();
- StatsdStats::getInstance().noteRegisteredAnomalyAlarmChanged();
- }
+ mCancelAlarm(mStatsCompanionService);
}
-int64_t AnomalyMonitor::secToMs(uint32_t timeSec) {
+int64_t AlarmMonitor::secToMs(uint32_t timeSec) {
return ((int64_t)timeSec) * 1000;
}
diff --git a/cmds/statsd/src/anomaly/AnomalyMonitor.h b/cmds/statsd/src/anomaly/AlarmMonitor.h
index 7acc7904bb57..3badb1faece6 100644
--- a/cmds/statsd/src/anomaly/AnomalyMonitor.h
+++ b/cmds/statsd/src/anomaly/AlarmMonitor.h
@@ -41,33 +41,34 @@ namespace statsd {
* threshold.
* Timestamps are in seconds since epoch in a uint32, so will fail in year 2106.
*/
-struct AnomalyAlarm : public RefBase {
- AnomalyAlarm(uint32_t timestampSec) : timestampSec(timestampSec) {
+struct InternalAlarm : public RefBase {
+ InternalAlarm(uint32_t timestampSec) : timestampSec(timestampSec) {
}
const uint32_t timestampSec;
- /** AnomalyAlarm a is smaller (higher priority) than b if its timestamp is sooner. */
+ /** InternalAlarm a is smaller (higher priority) than b if its timestamp is sooner. */
struct SmallerTimestamp {
- bool operator()(sp<const AnomalyAlarm> a, sp<const AnomalyAlarm> b) const {
+ bool operator()(sp<const InternalAlarm> a, sp<const InternalAlarm> b) const {
return (a->timestampSec < b->timestampSec);
}
};
};
-// TODO: Rename this file to AnomalyAlarmMonitor.
/**
- * Manages alarms for Anomaly Detection.
+ * Manages internal alarms that may get registered with the AlarmManager.
*/
-class AnomalyMonitor : public RefBase {
+class AlarmMonitor : public RefBase {
public:
/**
* @param minDiffToUpdateRegisteredAlarmTimeSec If the soonest alarm differs
* from the registered alarm by more than this amount, update the registered
* alarm.
*/
- AnomalyMonitor(uint32_t minDiffToUpdateRegisteredAlarmTimeSec);
- ~AnomalyMonitor();
+ AlarmMonitor(uint32_t minDiffToUpdateRegisteredAlarmTimeSec,
+ const std::function<void(const sp<IStatsCompanionService>&, int64_t)>& updateAlarm,
+ const std::function<void(const sp<IStatsCompanionService>&)>& cancelAlarm);
+ ~AlarmMonitor();
/**
* Tells AnomalyMonitor what IStatsCompanionService to use and, if
@@ -80,20 +81,20 @@ public:
/**
* Adds the given alarm (reference) to the queue.
*/
- void add(sp<const AnomalyAlarm> alarm);
+ void add(sp<const InternalAlarm> alarm);
/**
* Removes the given alarm (reference) from the queue.
* Note that alarm comparison is reference-based; if another alarm exists
* with the same timestampSec, that alarm will still remain in the queue.
*/
- void remove(sp<const AnomalyAlarm> alarm);
+ void remove(sp<const InternalAlarm> alarm);
/**
* Returns and removes all alarms whose timestamp <= the given timestampSec.
* Always updates the registered alarm if return is non-empty.
*/
- unordered_set<sp<const AnomalyAlarm>, SpHash<AnomalyAlarm>> popSoonerThan(
+ unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>> popSoonerThan(
uint32_t timestampSec);
/**
@@ -119,7 +120,7 @@ private:
/**
* Priority queue of alarms, prioritized by soonest alarm.timestampSec.
*/
- indexed_priority_queue<AnomalyAlarm, AnomalyAlarm::SmallerTimestamp> mPq;
+ indexed_priority_queue<InternalAlarm, InternalAlarm::SmallerTimestamp> mPq;
/**
* Binder interface for communicating with StatsCompanionService.
@@ -146,6 +147,13 @@ private:
/** Converts uint32 timestamp in seconds to a Java long in msec. */
int64_t secToMs(uint32_t timeSec);
+
+ // Callback function to update the alarm via StatsCompanionService.
+ std::function<void(const sp<IStatsCompanionService>, int64_t)> mUpdateAlarm;
+
+ // Callback function to cancel the alarm via StatsCompanionService.
+ std::function<void(const sp<IStatsCompanionService>)> mCancelAlarm;
+
};
} // namespace statsd
diff --git a/cmds/statsd/src/anomaly/AlarmTracker.cpp b/cmds/statsd/src/anomaly/AlarmTracker.cpp
new file mode 100644
index 000000000000..eb283838afd7
--- /dev/null
+++ b/cmds/statsd/src/anomaly/AlarmTracker.cpp
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define DEBUG true // STOPSHIP if true
+#include "Log.h"
+
+#include "anomaly/AlarmTracker.h"
+#include "anomaly/subscriber_util.h"
+#include "HashableDimensionKey.h"
+#include "stats_util.h"
+#include "storage/StorageManager.h"
+
+#include <statslog.h>
+#include <time.h>
+
+namespace android {
+namespace os {
+namespace statsd {
+
+AlarmTracker::AlarmTracker(uint64_t startMillis,
+ const Alarm& alarm, const ConfigKey& configKey,
+ const sp<AlarmMonitor>& alarmMonitor)
+ : mAlarmConfig(alarm),
+ mConfigKey(configKey),
+ mAlarmMonitor(alarmMonitor) {
+ VLOG("AlarmTracker() called");
+ mAlarmSec = (startMillis + mAlarmConfig.offset_millis()) / MS_PER_SEC;
+ mInternalAlarm = new InternalAlarm{static_cast<uint32_t>(mAlarmSec)};
+ mAlarmMonitor->add(mInternalAlarm);
+}
+
+AlarmTracker::~AlarmTracker() {
+ VLOG("~AlarmTracker() called");
+ if (mInternalAlarm != nullptr) {
+ mAlarmMonitor->remove(mInternalAlarm);
+ }
+}
+
+void AlarmTracker::addSubscription(const Subscription& subscription) {
+ mSubscriptions.push_back(subscription);
+}
+
+uint64_t AlarmTracker::findNextAlarmSec(uint64_t currentTimeSec) {
+ int periodsForward = (currentTimeSec - mAlarmSec) * MS_PER_SEC / mAlarmConfig.period_millis();
+ return mAlarmSec + (periodsForward + 1) * mAlarmConfig.period_millis() / MS_PER_SEC;
+}
+
+void AlarmTracker::informAlarmsFired(
+ const uint64_t& timestampNs,
+ unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>>& firedAlarms) {
+ if (firedAlarms.empty() || firedAlarms.find(mInternalAlarm) == firedAlarms.end()) {
+ return;
+ }
+ if (!mSubscriptions.empty()) {
+ triggerSubscribers(mAlarmConfig.id(), DEFAULT_METRIC_DIMENSION_KEY, mConfigKey,
+ mSubscriptions);
+ }
+ firedAlarms.erase(mInternalAlarm);
+ mAlarmSec = findNextAlarmSec(timestampNs / NS_PER_SEC);
+ mInternalAlarm = new InternalAlarm{static_cast<uint32_t>(mAlarmSec)};
+ mAlarmMonitor->add(mInternalAlarm);
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/anomaly/AlarmTracker.h b/cmds/statsd/src/anomaly/AlarmTracker.h
new file mode 100644
index 000000000000..d59dacaa1b69
--- /dev/null
+++ b/cmds/statsd/src/anomaly/AlarmTracker.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <gtest/gtest_prod.h>
+
+#include "AlarmMonitor.h"
+#include "config/ConfigKey.h"
+#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" // Alarm
+
+#include <android/os/IStatsCompanionService.h>
+#include <stdlib.h>
+#include <utils/RefBase.h>
+
+using android::os::IStatsCompanionService;
+
+namespace android {
+namespace os {
+namespace statsd {
+
+class AlarmTracker : public virtual RefBase {
+public:
+ AlarmTracker(uint64_t startMillis,
+ const Alarm& alarm, const ConfigKey& configKey,
+ const sp<AlarmMonitor>& subscriberAlarmMonitor);
+
+ virtual ~AlarmTracker();
+
+ void onAlarmFired();
+
+ void addSubscription(const Subscription& subscription);
+
+ void informAlarmsFired(const uint64_t& timestampNs,
+ unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>>& firedAlarms);
+
+protected:
+ uint64_t findNextAlarmSec(uint64_t currentTimeMillis);
+
+ // statsd_config.proto Alarm message that defines this tracker.
+ const Alarm mAlarmConfig;
+
+ // A reference to the Alarm's config key.
+ const ConfigKey& mConfigKey;
+
+ // The subscriptions that depend on this alarm.
+ std::vector<Subscription> mSubscriptions;
+
+ // Alarm monitor.
+ sp<AlarmMonitor> mAlarmMonitor;
+
+ // The current expected alarm time in seconds.
+ uint64_t mAlarmSec;
+
+ // The current alarm.
+ sp<const InternalAlarm> mInternalAlarm;
+
+ FRIEND_TEST(AlarmTrackerTest, TestTriggerTimestamp);
+};
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/anomaly/AnomalyTracker.cpp b/cmds/statsd/src/anomaly/AnomalyTracker.cpp
index c40eb812f949..642604e17bf5 100644
--- a/cmds/statsd/src/anomaly/AnomalyTracker.cpp
+++ b/cmds/statsd/src/anomaly/AnomalyTracker.cpp
@@ -18,6 +18,7 @@
#include "Log.h"
#include "AnomalyTracker.h"
+#include "subscriber_util.h"
#include "external/Perfetto.h"
#include "guardrail/StatsdStats.h"
#include "subscriber/IncidentdReporter.h"
@@ -231,40 +232,7 @@ bool AnomalyTracker::isInRefractoryPeriod(const uint64_t& timestampNs,
}
void AnomalyTracker::informSubscribers(const MetricDimensionKey& key) {
- VLOG("informSubscribers called.");
- if (mSubscriptions.empty()) {
- // The config just wanted to log the anomaly. That's fine.
- VLOG("No Subscriptions were associated with the alert.");
- return;
- }
-
- for (const Subscription& subscription : mSubscriptions) {
- if (subscription.probability_of_informing() < 1
- && ((float)rand() / RAND_MAX) >= subscription.probability_of_informing()) {
- // Note that due to float imprecision, 0.0 and 1.0 might not truly mean never/always.
- // The config writer was advised to use -0.1 and 1.1 for never/always.
- ALOGI("Fate decided that a subscriber would not be informed.");
- continue;
- }
- switch (subscription.subscriber_information_case()) {
- case Subscription::SubscriberInformationCase::kIncidentdDetails:
- if (!GenerateIncidentReport(subscription.incidentd_details(), mAlert, mConfigKey)) {
- ALOGW("Failed to generate incident report.");
- }
- break;
- case Subscription::SubscriberInformationCase::kPerfettoDetails:
- if (!CollectPerfettoTraceAndUploadToDropbox(subscription.perfetto_details())) {
- ALOGW("Failed to generate prefetto traces.");
- }
- break;
- case Subscription::SubscriberInformationCase::kBroadcastSubscriberDetails:
- SubscriberReporter::getInstance().alertBroadcastSubscriber(mConfigKey, subscription,
- key);
- break;
- default:
- break;
- }
- }
+ triggerSubscribers(mAlert.id(), key, mConfigKey, mSubscriptions);
}
} // namespace statsd
diff --git a/cmds/statsd/src/anomaly/AnomalyTracker.h b/cmds/statsd/src/anomaly/AnomalyTracker.h
index 3be959d14109..e3f493cfe7cf 100644
--- a/cmds/statsd/src/anomaly/AnomalyTracker.h
+++ b/cmds/statsd/src/anomaly/AnomalyTracker.h
@@ -23,7 +23,7 @@
#include <gtest/gtest_prod.h>
#include <utils/RefBase.h>
-#include "AnomalyMonitor.h"
+#include "AlarmMonitor.h"
#include "config/ConfigKey.h"
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" // Alert
#include "stats_util.h" // HashableDimensionKey and DimToValMap
@@ -64,9 +64,9 @@ public:
void detectAndDeclareAnomaly(const uint64_t& timestampNs, const int64_t& currBucketNum,
const MetricDimensionKey& key, const int64_t& currentBucketValue);
- // Init the AnomalyMonitor which is shared across anomaly trackers.
- virtual void setAnomalyMonitor(const sp<AnomalyMonitor>& anomalyMonitor) {
- return; // Base AnomalyTracker class has no need for the AnomalyMonitor.
+ // Init the AlarmMonitor which is shared across anomaly trackers.
+ virtual void setAlarmMonitor(const sp<AlarmMonitor>& alarmMonitor) {
+ return; // Base AnomalyTracker class has no need for the AlarmMonitor.
}
// Helper function to return the sum value of past buckets at given dimension.
@@ -92,11 +92,10 @@ public:
}
// Declares an anomaly for each alarm in firedAlarms that belongs to this AnomalyTracker,
- // and removes it from firedAlarms. Does NOT remove the alarm from the AnomalyMonitor.
- virtual void informAlarmsFired(
- const uint64_t& timestampNs,
- unordered_set<sp<const AnomalyAlarm>, SpHash<AnomalyAlarm>>& firedAlarms) {
- return; // The base AnomalyTracker class doesn't have alarms.
+ // and removes it from firedAlarms. Does NOT remove the alarm from the AlarmMonitor.
+ virtual void informAlarmsFired(const uint64_t& timestampNs,
+ unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>>& firedAlarms) {
+ return; // The base AnomalyTracker class doesn't have alarms.
}
protected:
diff --git a/cmds/statsd/src/anomaly/DurationAnomalyTracker.cpp b/cmds/statsd/src/anomaly/DurationAnomalyTracker.cpp
index 3ba943c310bb..31d50be7ec26 100644
--- a/cmds/statsd/src/anomaly/DurationAnomalyTracker.cpp
+++ b/cmds/statsd/src/anomaly/DurationAnomalyTracker.cpp
@@ -24,8 +24,9 @@ namespace android {
namespace os {
namespace statsd {
-DurationAnomalyTracker::DurationAnomalyTracker(const Alert& alert, const ConfigKey& configKey)
- : AnomalyTracker(alert, configKey) {
+DurationAnomalyTracker::DurationAnomalyTracker(const Alert& alert, const ConfigKey& configKey,
+ const sp<AlarmMonitor>& alarmMonitor)
+ : AnomalyTracker(alert, configKey), mAlarmMonitor(alarmMonitor) {
}
DurationAnomalyTracker::~DurationAnomalyTracker() {
@@ -59,10 +60,10 @@ void DurationAnomalyTracker::startAlarm(const MetricDimensionKey& dimensionKey,
VLOG("Setting a delayed anomaly alarm lest it fall in the refractory period");
timestampSec = getRefractoryPeriodEndsSec(dimensionKey) + 1;
}
- sp<const AnomalyAlarm> alarm = new AnomalyAlarm{timestampSec};
+ sp<const InternalAlarm> alarm = new InternalAlarm{timestampSec};
mAlarms.insert({dimensionKey, alarm});
- if (mAnomalyMonitor != nullptr) {
- mAnomalyMonitor->add(alarm);
+ if (mAlarmMonitor != nullptr) {
+ mAlarmMonitor->add(alarm);
}
}
@@ -70,8 +71,8 @@ void DurationAnomalyTracker::stopAlarm(const MetricDimensionKey& dimensionKey) {
auto itr = mAlarms.find(dimensionKey);
if (itr != mAlarms.end()) {
mAlarms.erase(dimensionKey);
- if (mAnomalyMonitor != nullptr) {
- mAnomalyMonitor->remove(itr->second);
+ if (mAlarmMonitor != nullptr) {
+ mAlarmMonitor->remove(itr->second);
}
}
}
@@ -86,16 +87,16 @@ void DurationAnomalyTracker::stopAllAlarms() {
}
}
-void DurationAnomalyTracker::informAlarmsFired(
- const uint64_t& timestampNs,
- unordered_set<sp<const AnomalyAlarm>, SpHash<AnomalyAlarm>>& firedAlarms) {
+void DurationAnomalyTracker::informAlarmsFired(const uint64_t& timestampNs,
+ unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>>& firedAlarms) {
+
if (firedAlarms.empty() || mAlarms.empty()) return;
// Find the intersection of firedAlarms and mAlarms.
// The for loop is inefficient, since it loops over all keys, but that's okay since it is very
- // seldomly called. The alternative would be having AnomalyAlarms store information about the
+ // seldomly called. The alternative would be having InternalAlarms store information about the
// DurationAnomalyTracker and key, but that's a lot of data overhead to speed up something that
// is rarely ever called.
- unordered_map<MetricDimensionKey, sp<const AnomalyAlarm>> matchedAlarms;
+ unordered_map<MetricDimensionKey, sp<const InternalAlarm>> matchedAlarms;
for (const auto& kv : mAlarms) {
if (firedAlarms.count(kv.second) > 0) {
matchedAlarms.insert({kv.first, kv.second});
diff --git a/cmds/statsd/src/anomaly/DurationAnomalyTracker.h b/cmds/statsd/src/anomaly/DurationAnomalyTracker.h
index 15aef29bc644..51186df3e93d 100644
--- a/cmds/statsd/src/anomaly/DurationAnomalyTracker.h
+++ b/cmds/statsd/src/anomaly/DurationAnomalyTracker.h
@@ -16,7 +16,7 @@
#pragma once
-#include "AnomalyMonitor.h"
+#include "AlarmMonitor.h"
#include "AnomalyTracker.h"
namespace android {
@@ -27,7 +27,8 @@ using std::unordered_map;
class DurationAnomalyTracker : public virtual AnomalyTracker {
public:
- DurationAnomalyTracker(const Alert& alert, const ConfigKey& configKey);
+ DurationAnomalyTracker(const Alert& alert, const ConfigKey& configKey,
+ const sp<AlarmMonitor>& alarmMonitor);
virtual ~DurationAnomalyTracker();
@@ -40,11 +41,6 @@ public:
// Stop all the alarms owned by this tracker.
void stopAllAlarms();
- // Init the AnomalyMonitor which is shared across anomaly trackers.
- void setAnomalyMonitor(const sp<AnomalyMonitor>& anomalyMonitor) override {
- mAnomalyMonitor = anomalyMonitor;
- }
-
// Declares the anomaly when the alarm expired given the current timestamp.
void declareAnomalyIfAlarmExpired(const MetricDimensionKey& dimensionKey,
const uint64_t& timestampNs);
@@ -53,17 +49,16 @@ public:
// and removes it from firedAlarms.
// Note that this will generally be called from a different thread from the other functions;
// the caller is responsible for thread safety.
- void informAlarmsFired(
- const uint64_t& timestampNs,
- unordered_set<sp<const AnomalyAlarm>, SpHash<AnomalyAlarm>>& firedAlarms) override;
+ void informAlarmsFired(const uint64_t& timestampNs,
+ unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>>& firedAlarms) override;
protected:
// The alarms owned by this tracker. The alarm monitor also shares the alarm pointers when they
// are still active.
- std::unordered_map<MetricDimensionKey, sp<const AnomalyAlarm>> mAlarms;
+ std::unordered_map<MetricDimensionKey, sp<const InternalAlarm>> mAlarms;
// Anomaly alarm monitor.
- sp<AnomalyMonitor> mAnomalyMonitor;
+ sp<AlarmMonitor> mAlarmMonitor;
// Resets all bucket data. For use when all the data gets stale.
void resetStorage() override;
diff --git a/cmds/statsd/src/anomaly/subscriber_util.cpp b/cmds/statsd/src/anomaly/subscriber_util.cpp
new file mode 100644
index 000000000000..e796d19efd93
--- /dev/null
+++ b/cmds/statsd/src/anomaly/subscriber_util.cpp
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define DEBUG true // STOPSHIP if true
+#include "Log.h"
+
+#include <android/os/IIncidentManager.h>
+#include <android/os/IncidentReportArgs.h>
+#include <binder/IServiceManager.h>
+
+#include "external/Perfetto.h"
+#include "frameworks/base/libs/incident/proto/android/os/header.pb.h"
+#include "subscriber/IncidentdReporter.h"
+#include "subscriber/SubscriberReporter.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+void triggerSubscribers(const int64_t rule_id,
+ const MetricDimensionKey& dimensionKey,
+ const ConfigKey& configKey,
+ const std::vector<Subscription>& subscriptions) {
+ VLOG("informSubscribers called.");
+ if (subscriptions.empty()) {
+ VLOG("No Subscriptions were associated.");
+ return;
+ }
+
+ for (const Subscription& subscription : subscriptions) {
+ if (subscription.probability_of_informing() < 1
+ && ((float)rand() / RAND_MAX) >= subscription.probability_of_informing()) {
+ // Note that due to float imprecision, 0.0 and 1.0 might not truly mean never/always.
+ // The config writer was advised to use -0.1 and 1.1 for never/always.
+ ALOGI("Fate decided that a subscriber would not be informed.");
+ continue;
+ }
+ switch (subscription.subscriber_information_case()) {
+ case Subscription::SubscriberInformationCase::kIncidentdDetails:
+ if (!GenerateIncidentReport(subscription.incidentd_details(), rule_id, configKey)) {
+ ALOGW("Failed to generate incident report.");
+ }
+ break;
+ case Subscription::SubscriberInformationCase::kPerfettoDetails:
+ if (!CollectPerfettoTraceAndUploadToDropbox(subscription.perfetto_details())) {
+ ALOGW("Failed to generate prefetto traces.");
+ }
+ break;
+ case Subscription::SubscriberInformationCase::kBroadcastSubscriberDetails:
+ SubscriberReporter::getInstance().alertBroadcastSubscriber(configKey, subscription,
+ dimensionKey);
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/anomaly/subscriber_util.h b/cmds/statsd/src/anomaly/subscriber_util.h
new file mode 100644
index 000000000000..dba8981a72aa
--- /dev/null
+++ b/cmds/statsd/src/anomaly/subscriber_util.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "config/ConfigKey.h"
+#include "HashableDimensionKey.h"
+#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+void triggerSubscribers(const int64_t rule_id,
+ const MetricDimensionKey& dimensionKey,
+ const ConfigKey& configKey,
+ const std::vector<Subscription>& subscriptions);
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 7159b9b24a6a..1c1d16bf371c 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -753,8 +753,11 @@ message CallStateChanged {
* frameworks/base/libs/hwui/JankTracker.cpp
*/
message DaveyOccurred {
+ // The UID that logged this atom.
+ optional int32 uid = 1;
+
// Amount of time it took to render the frame. Should be >=700ms.
- optional int64 jank_duration_millis = 1;
+ optional int64 jank_duration_millis = 2;
}
/**
diff --git a/cmds/statsd/src/external/Perfetto.h b/cmds/statsd/src/external/Perfetto.h
index e2e02533f17f..2a5679cc79fd 100644
--- a/cmds/statsd/src/external/Perfetto.h
+++ b/cmds/statsd/src/external/Perfetto.h
@@ -16,6 +16,8 @@
#pragma once
+#include <android/os/StatsLogEventWrapper.h>
+
using android::os::StatsLogEventWrapper;
namespace android {
diff --git a/cmds/statsd/src/guardrail/StatsdStats.cpp b/cmds/statsd/src/guardrail/StatsdStats.cpp
index 66cb1d04a4e1..0881d44c96d4 100644
--- a/cmds/statsd/src/guardrail/StatsdStats.cpp
+++ b/cmds/statsd/src/guardrail/StatsdStats.cpp
@@ -47,11 +47,13 @@ const int FIELD_ID_UIDMAP_STATS = 8;
const int FIELD_ID_ANOMALY_ALARM_STATS = 9;
// const int FIELD_ID_PULLED_ATOM_STATS = 10; // The proto is written in stats_log_util.cpp
const int FIELD_ID_LOGGER_ERROR_STATS = 11;
+const int FIELD_ID_SUBSCRIBER_ALARM_STATS = 12;
const int FIELD_ID_ATOM_STATS_TAG = 1;
const int FIELD_ID_ATOM_STATS_COUNT = 2;
const int FIELD_ID_ANOMALY_ALARMS_REGISTERED = 1;
+const int FIELD_ID_SUBSCRIBER_ALARMS_REGISTERED = 1;
const int FIELD_ID_LOGGER_STATS_TIME = 1;
const int FIELD_ID_LOGGER_STATS_ERROR_CODE = 2;
@@ -248,6 +250,11 @@ void StatsdStats::noteRegisteredAnomalyAlarmChanged() {
mAnomalyAlarmRegisteredStats++;
}
+void StatsdStats::noteRegisteredPeriodicAlarmChanged() {
+ lock_guard<std::mutex> lock(mLock);
+ mPeriodicAlarmRegisteredStats++;
+}
+
void StatsdStats::updateMinPullIntervalSec(int pullAtomId, long intervalSec) {
lock_guard<std::mutex> lock(mLock);
mPulledAtomStats[pullAtomId].minPullIntervalSec = intervalSec;
@@ -297,6 +304,7 @@ void StatsdStats::resetInternalLocked() {
std::fill(mPushedAtomStats.begin(), mPushedAtomStats.end(), 0);
mAlertStats.clear();
mAnomalyAlarmRegisteredStats = 0;
+ mPeriodicAlarmRegisteredStats = 0;
mMatcherStats.clear();
mLoggerErrors.clear();
for (auto& config : mConfigStats) {
@@ -367,7 +375,7 @@ void StatsdStats::dumpStats(FILE* out) const {
fprintf(out, "%lu Config in icebox: \n", (unsigned long)mIceBox.size());
for (const auto& configStats : mIceBox) {
fprintf(out,
- "Config {%d-%lld}: creation=%d, deletion=%d, #metric=%d, #condition=%d, "
+ "Config {%d_%lld}: creation=%d, deletion=%d, #metric=%d, #condition=%d, "
"#matcher=%d, #alert=%d, valid=%d\n",
configStats.uid(), (long long)configStats.id(), configStats.creation_time_sec(),
configStats.deletion_time_sec(), configStats.metric_count(),
@@ -462,6 +470,11 @@ void StatsdStats::dumpStats(FILE* out) const {
fprintf(out, "Anomaly alarm registrations: %d\n", mAnomalyAlarmRegisteredStats);
}
+ if (mPeriodicAlarmRegisteredStats > 0) {
+ fprintf(out, "********SubscriberAlarmStats stats***********\n");
+ fprintf(out, "Subscriber alarm registrations: %d\n", mPeriodicAlarmRegisteredStats);
+ }
+
fprintf(out,
"UID map stats: bytes=%d, snapshots=%d, changes=%d, snapshots lost=%d, changes "
"lost=%d\n",
@@ -531,6 +544,13 @@ void StatsdStats::dumpStats(std::vector<uint8_t>* output, bool reset) {
proto.end(token);
}
+ if (mPeriodicAlarmRegisteredStats > 0) {
+ long long token = proto.start(FIELD_TYPE_MESSAGE | FIELD_ID_SUBSCRIBER_ALARM_STATS);
+ proto.write(FIELD_TYPE_INT32 | FIELD_ID_SUBSCRIBER_ALARMS_REGISTERED,
+ mPeriodicAlarmRegisteredStats);
+ proto.end(token);
+ }
+
const int numBytes = mUidMapStats.ByteSize();
vector<char> buffer(numBytes);
mUidMapStats.SerializeToArray(&buffer[0], numBytes);
@@ -566,4 +586,4 @@ void StatsdStats::dumpStats(std::vector<uint8_t>* output, bool reset) {
} // namespace statsd
} // namespace os
-} // namespace android \ No newline at end of file
+} // namespace android
diff --git a/cmds/statsd/src/guardrail/StatsdStats.h b/cmds/statsd/src/guardrail/StatsdStats.h
index 8c16e4e9c2eb..24ac6883f444 100644
--- a/cmds/statsd/src/guardrail/StatsdStats.h
+++ b/cmds/statsd/src/guardrail/StatsdStats.h
@@ -170,6 +170,11 @@ public:
void noteRegisteredAnomalyAlarmChanged();
/**
+ * Report that statsd modified the periodic alarm registered with StatsCompanionService.
+ */
+ void noteRegisteredPeriodicAlarmChanged();
+
+ /**
* Records the number of snapshot and delta entries that are being dropped from the uid map.
*/
void noteUidMapDropped(int snapshots, int deltas);
@@ -264,6 +269,9 @@ private:
// StatsCompanionService.
int mAnomalyAlarmRegisteredStats = 0;
+ // Stores the number of times statsd registers the periodic alarm changes
+ int mPeriodicAlarmRegisteredStats = 0;
+
// Stores the number of times an anomaly detection alert has been declared
// (per config, per alert name). The map size is capped by kMaxConfigCount.
std::map<const ConfigKey, std::map<const int64_t, int>> mAlertStats;
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
index 9c65371ba958..16cac99f1fb9 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
@@ -109,9 +109,11 @@ DurationMetricProducer::~DurationMetricProducer() {
VLOG("~DurationMetric() called");
}
-sp<AnomalyTracker> DurationMetricProducer::addAnomalyTracker(const Alert &alert) {
+sp<AnomalyTracker> DurationMetricProducer::addAnomalyTracker(
+ const Alert &alert, const sp<AlarmMonitor>& anomalyAlarmMonitor) {
std::lock_guard<std::mutex> lock(mMutex);
- sp<DurationAnomalyTracker> anomalyTracker = new DurationAnomalyTracker(alert, mConfigKey);
+ sp<DurationAnomalyTracker> anomalyTracker =
+ new DurationAnomalyTracker(alert, mConfigKey, anomalyAlarmMonitor);
if (anomalyTracker != nullptr) {
mAnomalyTrackers.push_back(anomalyTracker);
}
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.h b/cmds/statsd/src/metrics/DurationMetricProducer.h
index 5f29281a8a3c..f41c278c5915 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.h
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.h
@@ -46,7 +46,8 @@ public:
virtual ~DurationMetricProducer();
- sp<AnomalyTracker> addAnomalyTracker(const Alert &alert) override;
+ sp<AnomalyTracker> addAnomalyTracker(const Alert &alert,
+ const sp<AlarmMonitor>& anomalyAlarmMonitor) override;
protected:
void onMatchedLogEventInternalLocked(
diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h
index 8663e5eed7a7..83e1740c1fad 100644
--- a/cmds/statsd/src/metrics/MetricProducer.h
+++ b/cmds/statsd/src/metrics/MetricProducer.h
@@ -124,7 +124,8 @@ public:
}
/* If alert is valid, adds an AnomalyTracker and returns it. If invalid, returns nullptr. */
- virtual sp<AnomalyTracker> addAnomalyTracker(const Alert &alert) {
+ virtual sp<AnomalyTracker> addAnomalyTracker(const Alert &alert,
+ const sp<AlarmMonitor>& anomalyAlarmMonitor) {
std::lock_guard<std::mutex> lock(mMutex);
sp<AnomalyTracker> anomalyTracker = new AnomalyTracker(alert, mConfigKey);
if (anomalyTracker != nullptr) {
diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp
index e75b710cc9db..4c8a7d8a92d5 100644
--- a/cmds/statsd/src/metrics/MetricsManager.cpp
+++ b/cmds/statsd/src/metrics/MetricsManager.cpp
@@ -49,13 +49,17 @@ namespace statsd {
const int FIELD_ID_METRICS = 1;
MetricsManager::MetricsManager(const ConfigKey& key, const StatsdConfig& config,
- const long timeBaseSec, sp<UidMap> uidMap)
+ const long timeBaseSec,
+ const sp<UidMap> &uidMap,
+ const sp<AlarmMonitor>& anomalyAlarmMonitor,
+ const sp<AlarmMonitor>& periodicAlarmMonitor)
: mConfigKey(key), mUidMap(uidMap), mLastReportTimeNs(timeBaseSec * NS_PER_SEC) {
mConfigValid =
- initStatsdConfig(key, config, *uidMap, timeBaseSec, mTagIds, mAllAtomMatchers,
- mAllConditionTrackers,
- mAllMetricProducers, mAllAnomalyTrackers, mConditionToMetricMap,
- mTrackerToMetricMap, mTrackerToConditionMap, mNoReportMetricIds);
+ initStatsdConfig(key, config, *uidMap, anomalyAlarmMonitor, periodicAlarmMonitor,
+ timeBaseSec, mTagIds, mAllAtomMatchers,
+ mAllConditionTrackers, mAllMetricProducers, mAllAnomalyTrackers,
+ mAllPeriodicAlarmTrackers, mConditionToMetricMap, mTrackerToMetricMap,
+ mTrackerToConditionMap, mNoReportMetricIds);
if (config.allowed_log_source_size() == 0) {
// TODO(b/70794411): uncomment the following line and remove the hard coded log source
@@ -198,31 +202,59 @@ void MetricsManager::onLogEvent(const LogEvent& event) {
// Uid is 3rd from last field and must match the caller's uid,
// unless that caller is statsd itself (statsd is allowed to spoof uids).
long appHookUid = event.GetLong(event.size()-2, &err);
+ if (err != NO_ERROR ) {
+ VLOG("APP_BREADCRUMB_REPORTED had error when parsing the uid");
+ return;
+ }
int32_t loggerUid = event.GetUid();
- if (err != NO_ERROR || (loggerUid != appHookUid && loggerUid != AID_STATSD)) {
- VLOG("AppHook has invalid uid: claimed %ld but caller is %d", appHookUid, loggerUid);
+ if (loggerUid != appHookUid && loggerUid != AID_STATSD) {
+ VLOG("APP_BREADCRUMB_REPORTED has invalid uid: claimed %ld but caller is %d",
+ appHookUid, loggerUid);
return;
}
// Label is 2nd from last field and must be from [0, 15].
long appHookLabel = event.GetLong(event.size()-1, &err);
- if (err != NO_ERROR || appHookLabel < 0 || appHookLabel > 15) {
- VLOG("AppHook does not have valid label %ld", appHookLabel);
+ if (err != NO_ERROR ) {
+ VLOG("APP_BREADCRUMB_REPORTED had error when parsing the label field");
+ return;
+ } else if (appHookLabel < 0 || appHookLabel > 15) {
+ VLOG("APP_BREADCRUMB_REPORTED does not have valid label %ld", appHookLabel);
return;
}
// The state must be from 0,3. This part of code must be manually updated.
long appHookState = event.GetLong(event.size(), &err);
- if (err != NO_ERROR || appHookState < 0 || appHookState > 3) {
- VLOG("AppHook does not have valid state %ld", appHookState);
+ if (err != NO_ERROR ) {
+ VLOG("APP_BREADCRUMB_REPORTED had error when parsing the state field");
+ return;
+ } else if (appHookState < 0 || appHookState > 3) {
+ VLOG("APP_BREADCRUMB_REPORTED does not have valid state %ld", appHookState);
return;
}
} else if (event.GetTagId() == android::util::DAVEY_OCCURRED) {
// Daveys can be logged from any app since they are logged in libs/hwui/JankTracker.cpp.
// Check that the davey duration is reasonable. Max length check is for privacy.
status_t err = NO_ERROR;
+
+ // Uid is the first field provided.
+ long jankUid = event.GetLong(1, &err);
+ if (err != NO_ERROR ) {
+ VLOG("Davey occurred had error when parsing the uid");
+ return;
+ }
+ int32_t loggerUid = event.GetUid();
+ if (loggerUid != jankUid && loggerUid != AID_STATSD) {
+ VLOG("DAVEY_OCCURRED has invalid uid: claimed %ld but caller is %d", jankUid,
+ loggerUid);
+ return;
+ }
+
long duration = event.GetLong(event.size(), &err);
- if (err != NO_ERROR || duration > 100000) {
+ if (err != NO_ERROR ) {
+ VLOG("Davey occurred had error when parsing the duration");
+ return;
+ } else if (duration > 100000) {
VLOG("Davey duration is unreasonably long: %ld", duration);
return;
}
@@ -312,16 +344,19 @@ void MetricsManager::onLogEvent(const LogEvent& event) {
}
}
-void MetricsManager::onAnomalyAlarmFired(const uint64_t timestampNs,
- unordered_set<sp<const AnomalyAlarm>, SpHash<AnomalyAlarm>>& anomalySet) {
+void MetricsManager::onAnomalyAlarmFired(
+ const uint64_t timestampNs,
+ unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>>& alarmSet) {
for (const auto& itr : mAllAnomalyTrackers) {
- itr->informAlarmsFired(timestampNs, anomalySet);
+ itr->informAlarmsFired(timestampNs, alarmSet);
}
}
-void MetricsManager::setAnomalyMonitor(const sp<AnomalyMonitor>& anomalyMonitor) {
- for (auto& itr : mAllAnomalyTrackers) {
- itr->setAnomalyMonitor(anomalyMonitor);
+void MetricsManager::onPeriodicAlarmFired(
+ const uint64_t timestampNs,
+ unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>>& alarmSet) {
+ for (const auto& itr : mAllPeriodicAlarmTrackers) {
+ itr->informAlarmsFired(timestampNs, alarmSet);
}
}
diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h
index d4f844fe386c..b50ef4a0c5a2 100644
--- a/cmds/statsd/src/metrics/MetricsManager.h
+++ b/cmds/statsd/src/metrics/MetricsManager.h
@@ -16,7 +16,8 @@
#pragma once
-#include "anomaly/AnomalyMonitor.h"
+#include "anomaly/AlarmMonitor.h"
+#include "anomaly/AlarmTracker.h"
#include "anomaly/AnomalyTracker.h"
#include "condition/ConditionTracker.h"
#include "config/ConfigKey.h"
@@ -36,7 +37,8 @@ namespace statsd {
class MetricsManager : public PackageInfoListener {
public:
MetricsManager(const ConfigKey& configKey, const StatsdConfig& config, const long timeBaseSec,
- sp<UidMap> uidMap);
+ const sp<UidMap>& uidMap, const sp<AlarmMonitor>& anomalyAlarmMonitor,
+ const sp<AlarmMonitor>& periodicAlarmMonitor);
virtual ~MetricsManager();
@@ -47,9 +49,11 @@ public:
void onAnomalyAlarmFired(
const uint64_t timestampNs,
- unordered_set<sp<const AnomalyAlarm>, SpHash<AnomalyAlarm>>& anomalySet);
+ unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>>& alarmSet);
- void setAnomalyMonitor(const sp<AnomalyMonitor>& anomalyMonitor);
+ void onPeriodicAlarmFired(
+ const uint64_t timestampNs,
+ unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>>& alarmSet);
void notifyAppUpgrade(const uint64_t& eventTimeNs, const string& apk, const int uid,
const int64_t version) override;
@@ -120,6 +124,9 @@ private:
// Hold all alert trackers.
std::vector<sp<AnomalyTracker>> mAllAnomalyTrackers;
+ // Hold all periodic alarm trackers.
+ std::vector<sp<AlarmTracker>> mAllPeriodicAlarmTrackers;
+
// To make the log processing more efficient, we want to do as much filtering as possible
// before we go into individual trackers and conditions to match.
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.cpp b/cmds/statsd/src/metrics/metrics_manager_util.cpp
index 71e5c33b1b88..9912afa01c3d 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.cpp
+++ b/cmds/statsd/src/metrics/metrics_manager_util.cpp
@@ -17,16 +17,19 @@
#define DEBUG false // STOPSHIP if true
#include "Log.h"
+#include "metrics_manager_util.h"
+
#include "../condition/CombinationConditionTracker.h"
#include "../condition/SimpleConditionTracker.h"
#include "../external/StatsPullerManager.h"
#include "../matchers/CombinationLogMatchingTracker.h"
#include "../matchers/SimpleLogMatchingTracker.h"
-#include "CountMetricProducer.h"
-#include "DurationMetricProducer.h"
-#include "EventMetricProducer.h"
-#include "GaugeMetricProducer.h"
-#include "ValueMetricProducer.h"
+#include "../metrics/CountMetricProducer.h"
+#include "../metrics/DurationMetricProducer.h"
+#include "../metrics/EventMetricProducer.h"
+#include "../metrics/GaugeMetricProducer.h"
+#include "../metrics/ValueMetricProducer.h"
+
#include "stats_util.h"
using std::set;
@@ -494,6 +497,7 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const long ti
bool initAlerts(const StatsdConfig& config,
const unordered_map<int64_t, int>& metricProducerMap,
+ const sp<AlarmMonitor>& anomalyAlarmMonitor,
vector<sp<MetricProducer>>& allMetricProducers,
vector<sp<AnomalyTracker>>& allAnomalyTrackers) {
unordered_map<int64_t, int> anomalyTrackerMap;
@@ -512,7 +516,7 @@ bool initAlerts(const StatsdConfig& config,
}
const int metricIndex = itr->second;
sp<MetricProducer> metric = allMetricProducers[metricIndex];
- sp<AnomalyTracker> anomalyTracker = metric->addAnomalyTracker(alert);
+ sp<AnomalyTracker> anomalyTracker = metric->addAnomalyTracker(alert, anomalyAlarmMonitor);
if (anomalyTracker == nullptr) {
// The ALOGW for this invalid alert was already displayed in addAnomalyTracker().
return false;
@@ -522,6 +526,9 @@ bool initAlerts(const StatsdConfig& config,
}
for (int i = 0; i < config.subscription_size(); ++i) {
const Subscription& subscription = config.subscription(i);
+ if (subscription.rule_type() != Subscription::ALERT) {
+ continue;
+ }
if (subscription.subscriber_information_case() ==
Subscription::SubscriberInformationCase::SUBSCRIBER_INFORMATION_NOT_SET) {
ALOGW("subscription \"%lld\" has no subscriber info.\"",
@@ -540,13 +547,60 @@ bool initAlerts(const StatsdConfig& config,
return true;
}
+bool initAlarms(const StatsdConfig& config, const ConfigKey& key,
+ const sp<AlarmMonitor>& periodicAlarmMonitor,
+ const long timeBaseSec,
+ vector<sp<AlarmTracker>>& allAlarmTrackers) {
+ unordered_map<int64_t, int> alarmTrackerMap;
+ uint64_t startMillis = (uint64_t)timeBaseSec * MS_PER_SEC;
+ for (int i = 0; i < config.alarm_size(); i++) {
+ const Alarm& alarm = config.alarm(i);
+ if (alarm.offset_millis() <= 0) {
+ ALOGW("Alarm offset_millis should be larger than 0.");
+ return false;
+ }
+ if (alarm.period_millis() <= 0) {
+ ALOGW("Alarm period_millis should be larger than 0.");
+ return false;
+ }
+ alarmTrackerMap.insert(std::make_pair(alarm.id(), allAlarmTrackers.size()));
+ allAlarmTrackers.push_back(
+ new AlarmTracker(startMillis, alarm, key, periodicAlarmMonitor));
+ }
+ for (int i = 0; i < config.subscription_size(); ++i) {
+ const Subscription& subscription = config.subscription(i);
+ if (subscription.rule_type() != Subscription::ALARM) {
+ continue;
+ }
+ if (subscription.subscriber_information_case() ==
+ Subscription::SubscriberInformationCase::SUBSCRIBER_INFORMATION_NOT_SET) {
+ ALOGW("subscription \"%lld\" has no subscriber info.\"",
+ (long long)subscription.id());
+ return false;
+ }
+ const auto& itr = alarmTrackerMap.find(subscription.rule_id());
+ if (itr == alarmTrackerMap.end()) {
+ ALOGW("subscription \"%lld\" has unknown rule id: \"%lld\"",
+ (long long)subscription.id(), (long long)subscription.rule_id());
+ return false;
+ }
+ const int trackerIndex = itr->second;
+ allAlarmTrackers[trackerIndex]->addSubscription(subscription);
+ }
+ return true;
+}
+
bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config,
const UidMap& uidMap,
- const long timeBaseSec, set<int>& allTagIds,
+ const sp<AlarmMonitor>& anomalyAlarmMonitor,
+ const sp<AlarmMonitor>& periodicAlarmMonitor,
+ const long timeBaseSec,
+ set<int>& allTagIds,
vector<sp<LogMatchingTracker>>& allAtomMatchers,
vector<sp<ConditionTracker>>& allConditionTrackers,
vector<sp<MetricProducer>>& allMetricProducers,
vector<sp<AnomalyTracker>>& allAnomalyTrackers,
+ vector<sp<AlarmTracker>>& allPeriodicAlarmTrackers,
unordered_map<int, std::vector<int>>& conditionToMetricMap,
unordered_map<int, std::vector<int>>& trackerToMetricMap,
unordered_map<int, std::vector<int>>& trackerToConditionMap,
@@ -573,10 +627,16 @@ bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config,
ALOGE("initMetricProducers failed");
return false;
}
- if (!initAlerts(config, metricProducerMap, allMetricProducers, allAnomalyTrackers)) {
+ if (!initAlerts(config, metricProducerMap, anomalyAlarmMonitor, allMetricProducers,
+ allAnomalyTrackers)) {
ALOGE("initAlerts failed");
return false;
}
+ if (!initAlarms(config, key, periodicAlarmMonitor, timeBaseSec, allPeriodicAlarmTrackers)) {
+ ALOGE("initAlarms failed");
+ return false;
+ }
+
return true;
}
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.h b/cmds/statsd/src/metrics/metrics_manager_util.h
index 4f19ada5b022..edda53d5e0cf 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.h
+++ b/cmds/statsd/src/metrics/metrics_manager_util.h
@@ -13,16 +13,19 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-#ifndef METRIC_UTIL_H
-#define METRIC_UTIL_H
+
+#pragma once
+
#include <memory>
#include <set>
#include <unordered_map>
#include <vector>
+#include "../anomaly/AlarmTracker.h"
#include "../condition/ConditionTracker.h"
#include "../external/StatsPullerManagerImpl.h"
#include "../matchers/LogMatchingTracker.h"
+#include "../metrics/MetricProducer.h"
namespace android {
namespace os {
@@ -93,11 +96,15 @@ bool initMetrics(
// Parameters are the members of MetricsManager. See MetricsManager for declaration.
bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config,
const UidMap& uidMap,
- const long timeBaseSec, std::set<int>& allTagIds,
+ const sp<AlarmMonitor>& anomalyAlarmMonitor,
+ const sp<AlarmMonitor>& periodicAlarmMonitor,
+ const long timeBaseSec,
+ std::set<int>& allTagIds,
std::vector<sp<LogMatchingTracker>>& allAtomMatchers,
std::vector<sp<ConditionTracker>>& allConditionTrackers,
std::vector<sp<MetricProducer>>& allMetricProducers,
vector<sp<AnomalyTracker>>& allAnomalyTrackers,
+ vector<sp<AlarmTracker>>& allPeriodicAlarmTrackers,
std::unordered_map<int, std::vector<int>>& conditionToMetricMap,
std::unordered_map<int, std::vector<int>>& trackerToMetricMap,
std::unordered_map<int, std::vector<int>>& trackerToConditionMap,
@@ -106,4 +113,3 @@ bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config,
} // namespace statsd
} // namespace os
} // namespace android
-#endif // METRIC_UTIL_H
diff --git a/cmds/statsd/src/stats_log.proto b/cmds/statsd/src/stats_log.proto
index 272e90be662d..269f25b30c84 100644
--- a/cmds/statsd/src/stats_log.proto
+++ b/cmds/statsd/src/stats_log.proto
@@ -164,4 +164,4 @@ message ConfigMetricsReportList {
optional ConfigKey config_key = 1;
repeated ConfigMetricsReport reports = 2;
-} \ No newline at end of file
+}
diff --git a/cmds/statsd/src/storage/StorageManager.cpp b/cmds/statsd/src/storage/StorageManager.cpp
index 6a1db72b3911..781ecede1700 100644
--- a/cmds/statsd/src/storage/StorageManager.cpp
+++ b/cmds/statsd/src/storage/StorageManager.cpp
@@ -195,6 +195,20 @@ void StorageManager::appendConfigMetricsReport(ProtoOutputStream& proto) {
}
}
+bool StorageManager::readFileToString(const char* file, string* content) {
+ int fd = open(file, O_RDONLY | O_CLOEXEC);
+ bool res = false;
+ if (fd != -1) {
+ if (android::base::ReadFdToString(fd, content)) {
+ res = true;
+ } else {
+ VLOG("Failed to read file %s\n", file);
+ }
+ close(fd);
+ }
+ return res;
+}
+
void StorageManager::readConfigFromDisk(map<ConfigKey, StatsdConfig>& configsMap) {
unique_ptr<DIR, decltype(&closedir)> dir(opendir(STATS_SERVICE_DIR), closedir);
if (dir == NULL) {
diff --git a/cmds/statsd/src/storage/StorageManager.h b/cmds/statsd/src/storage/StorageManager.h
index d319674b8987..6c8ed0a96704 100644
--- a/cmds/statsd/src/storage/StorageManager.h
+++ b/cmds/statsd/src/storage/StorageManager.h
@@ -37,6 +37,11 @@ public:
static void writeFile(const char* file, const void* buffer, int numBytes);
/**
+ * Reads the file content to the buffer.
+ */
+ static bool readFileToString(const char* file, string* content);
+
+ /**
* Deletes a single file given a file name.
*/
static void deleteFile(const char* file);
diff --git a/cmds/statsd/src/subscriber/IncidentdReporter.cpp b/cmds/statsd/src/subscriber/IncidentdReporter.cpp
index d9a8fc894804..1c18f673aeb9 100644
--- a/cmds/statsd/src/subscriber/IncidentdReporter.cpp
+++ b/cmds/statsd/src/subscriber/IncidentdReporter.cpp
@@ -28,10 +28,10 @@ namespace android {
namespace os {
namespace statsd {
-bool GenerateIncidentReport(const IncidentdDetails& config, const Alert& alert,
+bool GenerateIncidentReport(const IncidentdDetails& config, const int64_t& rule_id,
const ConfigKey& configKey) {
if (config.section_size() == 0) {
- VLOG("The alert %lld contains zero section in config(%d,%lld)", alert.id(),
+ VLOG("The alert %lld contains zero section in config(%d,%lld)", (unsigned long long)rule_id,
configKey.GetUid(), (long long) configKey.GetId());
return false;
}
@@ -39,7 +39,7 @@ bool GenerateIncidentReport(const IncidentdDetails& config, const Alert& alert,
IncidentReportArgs incidentReport;
android::os::IncidentHeaderProto header;
- header.set_alert_id(alert.id());
+ header.set_alert_id(rule_id);
header.mutable_config_key()->set_uid(configKey.GetUid());
header.mutable_config_key()->set_id(configKey.GetId());
incidentReport.addHeader(header);
diff --git a/cmds/statsd/src/subscriber/IncidentdReporter.h b/cmds/statsd/src/subscriber/IncidentdReporter.h
index 229ed778af3a..1b83fe23de8f 100644
--- a/cmds/statsd/src/subscriber/IncidentdReporter.h
+++ b/cmds/statsd/src/subscriber/IncidentdReporter.h
@@ -26,7 +26,7 @@ namespace statsd {
/**
* Calls incidentd to trigger an incident report and put in dropbox for uploading.
*/
-bool GenerateIncidentReport(const IncidentdDetails& config, const Alert& alert,
+bool GenerateIncidentReport(const IncidentdDetails& config, const int64_t& rule_id,
const ConfigKey& configKey);
} // namespace statsd
diff --git a/cmds/statsd/tests/AnomalyMonitor_test.cpp b/cmds/statsd/tests/AlarmMonitor_test.cpp
index 920ca08ef955..1fccb35eccb5 100644
--- a/cmds/statsd/tests/AnomalyMonitor_test.cpp
+++ b/cmds/statsd/tests/AlarmMonitor_test.cpp
@@ -12,28 +12,29 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "anomaly/AnomalyMonitor.h"
+#include "anomaly/AlarmMonitor.h"
#include <gtest/gtest.h>
using namespace android::os::statsd;
#ifdef __ANDROID__
-TEST(AnomalyMonitor, popSoonerThan) {
+TEST(AlarmMonitor, popSoonerThan) {
std::string emptyMetricId;
std::string emptyDimensionId;
- unordered_set<sp<const AnomalyAlarm>, SpHash<AnomalyAlarm>> set;
- AnomalyMonitor am(2);
+ unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>> set;
+ AlarmMonitor am(2, [](const sp<IStatsCompanionService>&, int64_t){},
+ [](const sp<IStatsCompanionService>&){});
set = am.popSoonerThan(5);
EXPECT_TRUE(set.empty());
- sp<const AnomalyAlarm> a = new AnomalyAlarm{10};
- sp<const AnomalyAlarm> b = new AnomalyAlarm{20};
- sp<const AnomalyAlarm> c = new AnomalyAlarm{20};
- sp<const AnomalyAlarm> d = new AnomalyAlarm{30};
- sp<const AnomalyAlarm> e = new AnomalyAlarm{40};
- sp<const AnomalyAlarm> f = new AnomalyAlarm{50};
+ sp<const InternalAlarm> a = new InternalAlarm{10};
+ sp<const InternalAlarm> b = new InternalAlarm{20};
+ sp<const InternalAlarm> c = new InternalAlarm{20};
+ sp<const InternalAlarm> d = new InternalAlarm{30};
+ sp<const InternalAlarm> e = new InternalAlarm{40};
+ sp<const InternalAlarm> f = new InternalAlarm{50};
am.add(a);
am.add(b);
diff --git a/cmds/statsd/tests/ConfigManager_test.cpp b/cmds/statsd/tests/ConfigManager_test.cpp
index 62bdba406de2..90c3a2f1a539 100644
--- a/cmds/statsd/tests/ConfigManager_test.cpp
+++ b/cmds/statsd/tests/ConfigManager_test.cpp
@@ -65,8 +65,12 @@ MATCHER_P(StatsdConfigEq, id, 0) {
const int64_t testConfigId = 12345;
TEST(ConfigManagerTest, TestFakeConfig) {
- auto metricsManager = std::make_unique<MetricsManager>(ConfigKey(0, testConfigId),
- build_fake_config(), 1000, new UidMap());
+ auto metricsManager = std::make_unique<MetricsManager>(
+ ConfigKey(0, testConfigId), build_fake_config(), 1000, new UidMap(),
+ new AlarmMonitor(10, [](const sp<IStatsCompanionService>&, int64_t){},
+ [](const sp<IStatsCompanionService>&){}),
+ new AlarmMonitor(10, [](const sp<IStatsCompanionService>&, int64_t){},
+ [](const sp<IStatsCompanionService>&){}));
EXPECT_TRUE(metricsManager->isConfigValid());
}
diff --git a/cmds/statsd/tests/MetricsManager_test.cpp b/cmds/statsd/tests/MetricsManager_test.cpp
index f90ca409e84c..5d8c3f72551e 100644
--- a/cmds/statsd/tests/MetricsManager_test.cpp
+++ b/cmds/statsd/tests/MetricsManager_test.cpp
@@ -271,19 +271,25 @@ StatsdConfig buildCirclePredicates() {
TEST(MetricsManagerTest, TestGoodConfig) {
UidMap uidMap;
+ sp<AlarmMonitor> anomalyAlarmMonitor;
+ sp<AlarmMonitor> periodicAlarmMonitor;
StatsdConfig config = buildGoodConfig();
set<int> allTagIds;
vector<sp<LogMatchingTracker>> allAtomMatchers;
vector<sp<ConditionTracker>> allConditionTrackers;
vector<sp<MetricProducer>> allMetricProducers;
std::vector<sp<AnomalyTracker>> allAnomalyTrackers;
+ std::vector<sp<AlarmTracker>> allAlarmTrackers;
unordered_map<int, std::vector<int>> conditionToMetricMap;
unordered_map<int, std::vector<int>> trackerToMetricMap;
unordered_map<int, std::vector<int>> trackerToConditionMap;
std::set<int64_t> noReportMetricIds;
- EXPECT_TRUE(initStatsdConfig(kConfigKey, config, uidMap, timeBaseSec, allTagIds, allAtomMatchers,
+ EXPECT_TRUE(initStatsdConfig(kConfigKey, config, uidMap,
+ anomalyAlarmMonitor, periodicAlarmMonitor,
+ timeBaseSec, allTagIds, allAtomMatchers,
allConditionTrackers, allMetricProducers, allAnomalyTrackers,
+ allAlarmTrackers,
conditionToMetricMap, trackerToMetricMap, trackerToConditionMap,
noReportMetricIds));
EXPECT_EQ(1u, allMetricProducers.size());
@@ -293,112 +299,148 @@ TEST(MetricsManagerTest, TestGoodConfig) {
TEST(MetricsManagerTest, TestDimensionMetricsWithMultiTags) {
UidMap uidMap;
+ sp<AlarmMonitor> anomalyAlarmMonitor;
+ sp<AlarmMonitor> periodicAlarmMonitor;
StatsdConfig config = buildDimensionMetricsWithMultiTags();
set<int> allTagIds;
vector<sp<LogMatchingTracker>> allAtomMatchers;
vector<sp<ConditionTracker>> allConditionTrackers;
vector<sp<MetricProducer>> allMetricProducers;
std::vector<sp<AnomalyTracker>> allAnomalyTrackers;
+ std::vector<sp<AlarmTracker>> allAlarmTrackers;
unordered_map<int, std::vector<int>> conditionToMetricMap;
unordered_map<int, std::vector<int>> trackerToMetricMap;
unordered_map<int, std::vector<int>> trackerToConditionMap;
std::set<int64_t> noReportMetricIds;
- EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, timeBaseSec, allTagIds, allAtomMatchers,
+ EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap,
+ anomalyAlarmMonitor, periodicAlarmMonitor,
+ timeBaseSec, allTagIds, allAtomMatchers,
allConditionTrackers, allMetricProducers, allAnomalyTrackers,
+ allAlarmTrackers,
conditionToMetricMap, trackerToMetricMap, trackerToConditionMap,
noReportMetricIds));
}
TEST(MetricsManagerTest, TestCircleLogMatcherDependency) {
UidMap uidMap;
+ sp<AlarmMonitor> anomalyAlarmMonitor;
+ sp<AlarmMonitor> periodicAlarmMonitor;
StatsdConfig config = buildCircleMatchers();
set<int> allTagIds;
vector<sp<LogMatchingTracker>> allAtomMatchers;
vector<sp<ConditionTracker>> allConditionTrackers;
vector<sp<MetricProducer>> allMetricProducers;
std::vector<sp<AnomalyTracker>> allAnomalyTrackers;
+ std::vector<sp<AlarmTracker>> allAlarmTrackers;
unordered_map<int, std::vector<int>> conditionToMetricMap;
unordered_map<int, std::vector<int>> trackerToMetricMap;
unordered_map<int, std::vector<int>> trackerToConditionMap;
std::set<int64_t> noReportMetricIds;
- EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, timeBaseSec, allTagIds, allAtomMatchers,
+ EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap,
+ anomalyAlarmMonitor, periodicAlarmMonitor,
+ timeBaseSec, allTagIds, allAtomMatchers,
allConditionTrackers, allMetricProducers, allAnomalyTrackers,
+ allAlarmTrackers,
conditionToMetricMap, trackerToMetricMap, trackerToConditionMap,
noReportMetricIds));
}
TEST(MetricsManagerTest, TestMissingMatchers) {
UidMap uidMap;
+ sp<AlarmMonitor> anomalyAlarmMonitor;
+ sp<AlarmMonitor> periodicAlarmMonitor;
StatsdConfig config = buildMissingMatchers();
set<int> allTagIds;
vector<sp<LogMatchingTracker>> allAtomMatchers;
vector<sp<ConditionTracker>> allConditionTrackers;
vector<sp<MetricProducer>> allMetricProducers;
std::vector<sp<AnomalyTracker>> allAnomalyTrackers;
+ std::vector<sp<AlarmTracker>> allAlarmTrackers;
unordered_map<int, std::vector<int>> conditionToMetricMap;
unordered_map<int, std::vector<int>> trackerToMetricMap;
unordered_map<int, std::vector<int>> trackerToConditionMap;
std::set<int64_t> noReportMetricIds;
- EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, timeBaseSec, allTagIds, allAtomMatchers,
+ EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap,
+ anomalyAlarmMonitor, periodicAlarmMonitor,
+ timeBaseSec, allTagIds, allAtomMatchers,
allConditionTrackers, allMetricProducers, allAnomalyTrackers,
+ allAlarmTrackers,
conditionToMetricMap, trackerToMetricMap, trackerToConditionMap,
noReportMetricIds));
}
TEST(MetricsManagerTest, TestMissingPredicate) {
UidMap uidMap;
+ sp<AlarmMonitor> anomalyAlarmMonitor;
+ sp<AlarmMonitor> periodicAlarmMonitor;
StatsdConfig config = buildMissingPredicate();
set<int> allTagIds;
vector<sp<LogMatchingTracker>> allAtomMatchers;
vector<sp<ConditionTracker>> allConditionTrackers;
vector<sp<MetricProducer>> allMetricProducers;
std::vector<sp<AnomalyTracker>> allAnomalyTrackers;
+ std::vector<sp<AlarmTracker>> allAlarmTrackers;
unordered_map<int, std::vector<int>> conditionToMetricMap;
unordered_map<int, std::vector<int>> trackerToMetricMap;
unordered_map<int, std::vector<int>> trackerToConditionMap;
std::set<int64_t> noReportMetricIds;
- EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, timeBaseSec, allTagIds, allAtomMatchers,
+ EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap,
+ anomalyAlarmMonitor, periodicAlarmMonitor,
+ timeBaseSec, allTagIds, allAtomMatchers,
allConditionTrackers, allMetricProducers, allAnomalyTrackers,
+ allAlarmTrackers,
conditionToMetricMap, trackerToMetricMap, trackerToConditionMap,
noReportMetricIds));
}
TEST(MetricsManagerTest, TestCirclePredicateDependency) {
UidMap uidMap;
+ sp<AlarmMonitor> anomalyAlarmMonitor;
+ sp<AlarmMonitor> periodicAlarmMonitor;
StatsdConfig config = buildCirclePredicates();
set<int> allTagIds;
vector<sp<LogMatchingTracker>> allAtomMatchers;
vector<sp<ConditionTracker>> allConditionTrackers;
vector<sp<MetricProducer>> allMetricProducers;
std::vector<sp<AnomalyTracker>> allAnomalyTrackers;
+ std::vector<sp<AlarmTracker>> allAlarmTrackers;
unordered_map<int, std::vector<int>> conditionToMetricMap;
unordered_map<int, std::vector<int>> trackerToMetricMap;
unordered_map<int, std::vector<int>> trackerToConditionMap;
std::set<int64_t> noReportMetricIds;
- EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, timeBaseSec, allTagIds, allAtomMatchers,
+ EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap,
+ anomalyAlarmMonitor, periodicAlarmMonitor,
+ timeBaseSec, allTagIds, allAtomMatchers,
allConditionTrackers, allMetricProducers, allAnomalyTrackers,
+ allAlarmTrackers,
conditionToMetricMap, trackerToMetricMap, trackerToConditionMap,
noReportMetricIds));
}
TEST(MetricsManagerTest, testAlertWithUnknownMetric) {
UidMap uidMap;
+ sp<AlarmMonitor> anomalyAlarmMonitor;
+ sp<AlarmMonitor> periodicAlarmMonitor;
StatsdConfig config = buildAlertWithUnknownMetric();
set<int> allTagIds;
vector<sp<LogMatchingTracker>> allAtomMatchers;
vector<sp<ConditionTracker>> allConditionTrackers;
vector<sp<MetricProducer>> allMetricProducers;
std::vector<sp<AnomalyTracker>> allAnomalyTrackers;
+ std::vector<sp<AlarmTracker>> allAlarmTrackers;
unordered_map<int, std::vector<int>> conditionToMetricMap;
unordered_map<int, std::vector<int>> trackerToMetricMap;
unordered_map<int, std::vector<int>> trackerToConditionMap;
std::set<int64_t> noReportMetricIds;
- EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, timeBaseSec, allTagIds, allAtomMatchers,
+ EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap,
+ anomalyAlarmMonitor, periodicAlarmMonitor,
+ timeBaseSec, allTagIds, allAtomMatchers,
allConditionTrackers, allMetricProducers, allAnomalyTrackers,
+ allAlarmTrackers,
conditionToMetricMap, trackerToMetricMap, trackerToConditionMap,
noReportMetricIds));
}
diff --git a/cmds/statsd/tests/StatsLogProcessor_test.cpp b/cmds/statsd/tests/StatsLogProcessor_test.cpp
index cb72697941e0..3238b74fb635 100644
--- a/cmds/statsd/tests/StatsLogProcessor_test.cpp
+++ b/cmds/statsd/tests/StatsLogProcessor_test.cpp
@@ -41,7 +41,13 @@ using android::util::ProtoOutputStream;
*/
class MockMetricsManager : public MetricsManager {
public:
- MockMetricsManager() : MetricsManager(ConfigKey(1, 12345), StatsdConfig(), 1000, new UidMap()) {
+ MockMetricsManager() : MetricsManager(
+ ConfigKey(1, 12345), StatsdConfig(), 1000,
+ new UidMap(),
+ new AlarmMonitor(10, [](const sp<IStatsCompanionService>&, int64_t){},
+ [](const sp<IStatsCompanionService>&){}),
+ new AlarmMonitor(10, [](const sp<IStatsCompanionService>&, int64_t){},
+ [](const sp<IStatsCompanionService>&){})) {
}
MOCK_METHOD0(byteSize, size_t());
@@ -50,9 +56,11 @@ public:
TEST(StatsLogProcessorTest, TestRateLimitByteSize) {
sp<UidMap> m = new UidMap();
- sp<AnomalyMonitor> anomalyMonitor;
+ sp<AlarmMonitor> anomalyAlarmMonitor;
+ sp<AlarmMonitor> periodicAlarmMonitor;
// Construct the processor with a dummy sendBroadcast function that does nothing.
- StatsLogProcessor p(m, anomalyMonitor, 0, [](const ConfigKey& key) {});
+ StatsLogProcessor p(m, anomalyAlarmMonitor, periodicAlarmMonitor, 0,
+ [](const ConfigKey& key) {});
MockMetricsManager mockMetricsManager;
@@ -67,11 +75,11 @@ TEST(StatsLogProcessorTest, TestRateLimitByteSize) {
TEST(StatsLogProcessorTest, TestRateLimitBroadcast) {
sp<UidMap> m = new UidMap();
- sp<AnomalyMonitor> anomalyMonitor;
+ sp<AlarmMonitor> anomalyAlarmMonitor;
+ sp<AlarmMonitor> subscriberAlarmMonitor;
int broadcastCount = 0;
- StatsLogProcessor p(m, anomalyMonitor, 0, [&broadcastCount](const ConfigKey& key) {
- broadcastCount++;
- });
+ StatsLogProcessor p(m, anomalyAlarmMonitor, subscriberAlarmMonitor, 0,
+ [&broadcastCount](const ConfigKey& key) { broadcastCount++; });
MockMetricsManager mockMetricsManager;
@@ -93,9 +101,10 @@ TEST(StatsLogProcessorTest, TestRateLimitBroadcast) {
TEST(StatsLogProcessorTest, TestDropWhenByteSizeTooLarge) {
sp<UidMap> m = new UidMap();
- sp<AnomalyMonitor> anomalyMonitor;
+ sp<AlarmMonitor> anomalyAlarmMonitor;
+ sp<AlarmMonitor> subscriberAlarmMonitor;
int broadcastCount = 0;
- StatsLogProcessor p(m, anomalyMonitor, 0,
+ StatsLogProcessor p(m, anomalyAlarmMonitor, subscriberAlarmMonitor, 0,
[&broadcastCount](const ConfigKey& key) { broadcastCount++; });
MockMetricsManager mockMetricsManager;
diff --git a/cmds/statsd/tests/UidMap_test.cpp b/cmds/statsd/tests/UidMap_test.cpp
index f26c10d33e67..ca656ed9ab35 100644
--- a/cmds/statsd/tests/UidMap_test.cpp
+++ b/cmds/statsd/tests/UidMap_test.cpp
@@ -36,9 +36,11 @@ const string kApp2 = "app2.sharing.1";
TEST(UidMapTest, TestIsolatedUID) {
sp<UidMap> m = new UidMap();
- sp<AnomalyMonitor> anomalyMonitor;
+ sp<AlarmMonitor> anomalyAlarmMonitor;
+ sp<AlarmMonitor> subscriberAlarmMonitor;
// Construct the processor with a dummy sendBroadcast function that does nothing.
- StatsLogProcessor p(m, anomalyMonitor, 0, [](const ConfigKey& key) {});
+ StatsLogProcessor p(m, anomalyAlarmMonitor, subscriberAlarmMonitor, 0,
+ [](const ConfigKey& key) {});
LogEvent addEvent(android::util::ISOLATED_UID_CHANGED, 1);
addEvent.write(100); // parent UID
addEvent.write(101); // isolated UID
diff --git a/cmds/statsd/tests/anomaly/AlarmTracker_test.cpp b/cmds/statsd/tests/anomaly/AlarmTracker_test.cpp
new file mode 100644
index 000000000000..3330ee93eddf
--- /dev/null
+++ b/cmds/statsd/tests/anomaly/AlarmTracker_test.cpp
@@ -0,0 +1,68 @@
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "src/anomaly/AlarmTracker.h"
+
+#include <gtest/gtest.h>
+#include <stdio.h>
+#include <vector>
+
+using namespace testing;
+using android::sp;
+using std::set;
+using std::unordered_map;
+using std::vector;
+
+#ifdef __ANDROID__
+
+namespace android {
+namespace os {
+namespace statsd {
+
+const ConfigKey kConfigKey(0, 12345);
+
+TEST(AlarmTrackerTest, TestTriggerTimestamp) {
+ sp<AlarmMonitor> subscriberAlarmMonitor =
+ new AlarmMonitor(100, [](const sp<IStatsCompanionService>&, int64_t){},
+ [](const sp<IStatsCompanionService>&){});
+ Alarm alarm;
+ alarm.set_offset_millis(15 * MS_PER_SEC);
+ alarm.set_period_millis(60 * 60 * MS_PER_SEC); // 1hr
+ uint64_t startMillis = 100000000 * MS_PER_SEC;
+ AlarmTracker tracker(startMillis, alarm, kConfigKey,
+ subscriberAlarmMonitor);
+
+ EXPECT_EQ(tracker.mAlarmSec, startMillis / MS_PER_SEC + 15);
+
+ uint64_t currentTimeSec = startMillis / MS_PER_SEC + 10;
+ std::unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>> firedAlarmSet =
+ subscriberAlarmMonitor->popSoonerThan(static_cast<uint32_t>(currentTimeSec));
+ EXPECT_TRUE(firedAlarmSet.empty());
+ tracker.informAlarmsFired(currentTimeSec * NS_PER_SEC, firedAlarmSet);
+ EXPECT_EQ(tracker.mAlarmSec, startMillis / MS_PER_SEC + 15);
+
+ currentTimeSec = startMillis / MS_PER_SEC + 7000;
+ firedAlarmSet = subscriberAlarmMonitor->popSoonerThan(static_cast<uint32_t>(currentTimeSec));
+ EXPECT_EQ(firedAlarmSet.size(), 1u);
+ tracker.informAlarmsFired(currentTimeSec * NS_PER_SEC, firedAlarmSet);
+ EXPECT_TRUE(firedAlarmSet.empty());
+ EXPECT_EQ(tracker.mAlarmSec, startMillis / MS_PER_SEC + 15 + 2 * 60 * 60);
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
+#else
+GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
diff --git a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
index 20ddbe9f0e38..9a0de0d81802 100644
--- a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
@@ -201,6 +201,7 @@ TEST(CountMetricProducerTest, TestEventsWithSlicedCondition) {
}
TEST(CountMetricProducerTest, TestEventWithAppUpgrade) {
+ sp<AlarmMonitor> alarmMonitor;
uint64_t bucketStartTimeNs = 10000000000;
uint64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
uint64_t eventUpgradeTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC;
@@ -222,7 +223,7 @@ TEST(CountMetricProducerTest, TestEventWithAppUpgrade) {
bucketStartTimeNs);
countProducer.setBucketSize(60 * NS_PER_SEC);
- sp<AnomalyTracker> anomalyTracker = countProducer.addAnomalyTracker(alert);
+ sp<AnomalyTracker> anomalyTracker = countProducer.addAnomalyTracker(alert, alarmMonitor);
EXPECT_TRUE(anomalyTracker != nullptr);
// Bucket is flushed yet.
@@ -315,6 +316,7 @@ TEST(CountMetricProducerTest, TestEventWithAppUpgradeInNextBucket) {
}
TEST(CountMetricProducerTest, TestAnomalyDetectionUnSliced) {
+ sp<AlarmMonitor> alarmMonitor;
Alert alert;
alert.set_id(11);
alert.set_metric_id(1);
@@ -337,7 +339,7 @@ TEST(CountMetricProducerTest, TestAnomalyDetectionUnSliced) {
bucketStartTimeNs);
countProducer.setBucketSize(60 * NS_PER_SEC);
- sp<AnomalyTracker> anomalyTracker = countProducer.addAnomalyTracker(alert);
+ sp<AnomalyTracker> anomalyTracker = countProducer.addAnomalyTracker(alert, alarmMonitor);
int tagId = 1;
LogEvent event1(tagId, bucketStartTimeNs + 1);
diff --git a/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp b/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp
index 79695967a6dd..1b22d75da7b4 100644
--- a/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp
@@ -239,6 +239,7 @@ TEST(DurationMetricTrackerTest, TestSumDurationWithUpgradeInFollowingBucket) {
}
TEST(DurationMetricTrackerTest, TestSumDurationAnomalyWithUpgrade) {
+ sp<AlarmMonitor> alarmMonitor;
uint64_t bucketStartTimeNs = 10000000000;
uint64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
uint64_t eventUpgradeTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC;
@@ -263,7 +264,7 @@ TEST(DurationMetricTrackerTest, TestSumDurationAnomalyWithUpgrade) {
3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs);
durationProducer.setBucketSize(60 * NS_PER_SEC);
- sp<AnomalyTracker> anomalyTracker = durationProducer.addAnomalyTracker(alert);
+ sp<AnomalyTracker> anomalyTracker = durationProducer.addAnomalyTracker(alert, alarmMonitor);
EXPECT_TRUE(anomalyTracker != nullptr);
LogEvent start_event(tagId, startTimeNs);
diff --git a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
index 0eb8ce2603bd..77b3ace90aff 100644
--- a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
@@ -129,6 +129,7 @@ TEST(GaugeMetricProducerTest, TestNoCondition) {
}
TEST(GaugeMetricProducerTest, TestPushedEventsWithUpgrade) {
+ sp<AlarmMonitor> alarmMonitor;
GaugeMetric metric;
metric.set_id(metricId);
metric.set_bucket(ONE_MINUTE);
@@ -145,8 +146,9 @@ TEST(GaugeMetricProducerTest, TestPushedEventsWithUpgrade) {
GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
-1 /* -1 means no pulling */, bucketStartTimeNs,
pullerManager);
+
gaugeProducer.setBucketSize(60 * NS_PER_SEC);
- sp<AnomalyTracker> anomalyTracker = gaugeProducer.addAnomalyTracker(alert);
+ sp<AnomalyTracker> anomalyTracker = gaugeProducer.addAnomalyTracker(alert, alarmMonitor);
EXPECT_TRUE(anomalyTracker != nullptr);
shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
@@ -339,6 +341,7 @@ TEST(GaugeMetricProducerTest, TestWithCondition) {
}
TEST(GaugeMetricProducerTest, TestAnomalyDetection) {
+ sp<AlarmMonitor> alarmMonitor;
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
shared_ptr<MockStatsPullerManager> pullerManager =
@@ -363,7 +366,7 @@ TEST(GaugeMetricProducerTest, TestAnomalyDetection) {
alert.set_num_buckets(2);
const int32_t refPeriodSec = 60;
alert.set_refractory_period_secs(refPeriodSec);
- sp<AnomalyTracker> anomalyTracker = gaugeProducer.addAnomalyTracker(alert);
+ sp<AnomalyTracker> anomalyTracker = gaugeProducer.addAnomalyTracker(alert, alarmMonitor);
int tagId = 1;
std::shared_ptr<LogEvent> event1 = std::make_shared<LogEvent>(tagId, bucketStartTimeNs + 1);
diff --git a/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp b/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp
index a164c12134b5..83b1cbfb6fb3 100644
--- a/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp
+++ b/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp
@@ -276,13 +276,15 @@ TEST(MaxDurationTrackerTest, TestAnomalyDetection) {
alert.set_num_buckets(2);
const int32_t refPeriodSec = 45;
alert.set_refractory_period_secs(refPeriodSec);
- sp<DurationAnomalyTracker> anomalyTracker = new DurationAnomalyTracker(alert, kConfigKey);
+ sp<AlarmMonitor> alarmMonitor;
+ sp<DurationAnomalyTracker> anomalyTracker =
+ new DurationAnomalyTracker(alert, kConfigKey, alarmMonitor);
MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
false, bucketStartTimeNs, bucketNum, bucketStartTimeNs, bucketSizeNs,
true, {anomalyTracker});
tracker.noteStart(key1, true, eventStartTimeNs, conditionKey1);
- sp<const AnomalyAlarm> alarm = anomalyTracker->mAlarms.begin()->second;
+ sp<const InternalAlarm> alarm = anomalyTracker->mAlarms.begin()->second;
EXPECT_EQ((long long)(53ULL * NS_PER_SEC), (long long)(alarm->timestampSec * NS_PER_SEC));
// Remove the anomaly alarm when the duration is no longer fully met.
@@ -336,7 +338,9 @@ TEST(MaxDurationTrackerTest, TestAnomalyPredictedTimestamp) {
alert.set_num_buckets(2);
const int32_t refPeriodSec = 45;
alert.set_refractory_period_secs(refPeriodSec);
- sp<DurationAnomalyTracker> anomalyTracker = new DurationAnomalyTracker(alert, kConfigKey);
+ sp<AlarmMonitor> alarmMonitor;
+ sp<DurationAnomalyTracker> anomalyTracker =
+ new DurationAnomalyTracker(alert, kConfigKey, alarmMonitor);
MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
false, bucketStartTimeNs, bucketNum, bucketStartTimeNs, bucketSizeNs,
true, {anomalyTracker});
@@ -390,7 +394,9 @@ TEST(MaxDurationTrackerTest, TestAnomalyPredictedTimestamp_UpdatedOnStop) {
alert.set_num_buckets(2);
const int32_t refPeriodSec = 45;
alert.set_refractory_period_secs(refPeriodSec);
- sp<DurationAnomalyTracker> anomalyTracker = new DurationAnomalyTracker(alert, kConfigKey);
+ sp<AlarmMonitor> alarmMonitor;
+ sp<DurationAnomalyTracker> anomalyTracker =
+ new DurationAnomalyTracker(alert, kConfigKey, alarmMonitor);
MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
false, bucketStartTimeNs, bucketNum, bucketStartTimeNs, bucketSizeNs,
true, {anomalyTracker});
diff --git a/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp b/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp
index cb731c555e90..aa41038b9acb 100644
--- a/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp
+++ b/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp
@@ -334,7 +334,9 @@ TEST(OringDurationTrackerTest, TestPredictAnomalyTimestamp) {
uint64_t bucketNum = 0;
uint64_t eventStartTimeNs = bucketStartTimeNs + NS_PER_SEC + 1;
- sp<DurationAnomalyTracker> anomalyTracker = new DurationAnomalyTracker(alert, kConfigKey);
+ sp<AlarmMonitor> alarmMonitor;
+ sp<DurationAnomalyTracker> anomalyTracker =
+ new DurationAnomalyTracker(alert, kConfigKey, alarmMonitor);
OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
true, bucketStartTimeNs, bucketNum, bucketStartTimeNs,
bucketSizeNs, true, {anomalyTracker});
@@ -403,7 +405,9 @@ TEST(OringDurationTrackerTest, TestAnomalyDetectionExpiredAlarm) {
uint64_t bucketNum = 0;
uint64_t eventStartTimeNs = bucketStartTimeNs + NS_PER_SEC + 1;
- sp<DurationAnomalyTracker> anomalyTracker = new DurationAnomalyTracker(alert, kConfigKey);
+ sp<AlarmMonitor> alarmMonitor;
+ sp<DurationAnomalyTracker> anomalyTracker =
+ new DurationAnomalyTracker(alert, kConfigKey, alarmMonitor);
OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
true /*nesting*/, bucketStartTimeNs, bucketNum, bucketStartTimeNs,
bucketSizeNs, false, {anomalyTracker});
@@ -453,14 +457,16 @@ TEST(OringDurationTrackerTest, TestAnomalyDetectionFiredAlarm) {
uint64_t bucketStartTimeNs = 10 * NS_PER_SEC;
uint64_t bucketSizeNs = 30 * NS_PER_SEC;
- sp<DurationAnomalyTracker> anomalyTracker = new DurationAnomalyTracker(alert, kConfigKey);
+ sp<AlarmMonitor> alarmMonitor;
+ sp<DurationAnomalyTracker> anomalyTracker =
+ new DurationAnomalyTracker(alert, kConfigKey, alarmMonitor);
OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
true /*nesting*/, bucketStartTimeNs, 0, bucketStartTimeNs,
bucketSizeNs, false, {anomalyTracker});
tracker.noteStart(kEventKey1, true, 15 * NS_PER_SEC, conkey); // start key1
EXPECT_EQ(1u, anomalyTracker->mAlarms.size());
- sp<const AnomalyAlarm> alarm = anomalyTracker->mAlarms.begin()->second;
+ sp<const InternalAlarm> alarm = anomalyTracker->mAlarms.begin()->second;
EXPECT_EQ((long long)(55ULL * NS_PER_SEC), (long long)(alarm->timestampSec * NS_PER_SEC));
EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(eventKey), 0U);
@@ -487,7 +493,7 @@ TEST(OringDurationTrackerTest, TestAnomalyDetectionFiredAlarm) {
EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(eventKey), 0U);
// Now, at 60s, which is 38s after key1 started again, we have reached 40s of 'on' time.
- std::unordered_set<sp<const AnomalyAlarm>, SpHash<AnomalyAlarm>> firedAlarms({alarm});
+ std::unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>> firedAlarms({alarm});
anomalyTracker->informAlarmsFired(62 * NS_PER_SEC, firedAlarms);
EXPECT_EQ(0u, anomalyTracker->mAlarms.size());
EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(eventKey), 62U + refPeriodSec);
diff --git a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
index ce4fa3278493..a0addcccb7a1 100644
--- a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
@@ -346,6 +346,7 @@ TEST(ValueMetricProducerTest, TestPushedEventsWithoutCondition) {
}
TEST(ValueMetricProducerTest, TestAnomalyDetection) {
+ sp<AlarmMonitor> alarmMonitor;
Alert alert;
alert.set_id(101);
alert.set_metric_id(metricId);
@@ -365,7 +366,7 @@ TEST(ValueMetricProducerTest, TestAnomalyDetection) {
-1 /*not pulled*/, bucketStartTimeNs);
valueProducer.setBucketSize(60 * NS_PER_SEC);
- sp<AnomalyTracker> anomalyTracker = valueProducer.addAnomalyTracker(alert);
+ sp<AnomalyTracker> anomalyTracker = valueProducer.addAnomalyTracker(alert, alarmMonitor);
shared_ptr<LogEvent> event1
diff --git a/cmds/statsd/tests/statsd_test_util.cpp b/cmds/statsd/tests/statsd_test_util.cpp
index 7568348108c4..242b6ebe5ec1 100644
--- a/cmds/statsd/tests/statsd_test_util.cpp
+++ b/cmds/statsd/tests/statsd_test_util.cpp
@@ -391,9 +391,10 @@ std::unique_ptr<LogEvent> CreateIsolatedUidChangedEvent(
sp<StatsLogProcessor> CreateStatsLogProcessor(const long timeBaseSec, const StatsdConfig& config,
const ConfigKey& key) {
sp<UidMap> uidMap = new UidMap();
- sp<AnomalyMonitor> anomalyMonitor = new AnomalyMonitor(10); // 10 seconds
+ sp<AlarmMonitor> anomalyAlarmMonitor;
+ sp<AlarmMonitor> periodicAlarmMonitor;
sp<StatsLogProcessor> processor = new StatsLogProcessor(
- uidMap, anomalyMonitor, timeBaseSec, [](const ConfigKey&){});
+ uidMap, anomalyAlarmMonitor, periodicAlarmMonitor, timeBaseSec, [](const ConfigKey&){});
processor->OnConfigUpdated(key, config);
return processor;
}
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 21d146a5500a..872370e0d0ed 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -3900,8 +3900,7 @@ public final class ActivityThread extends ClientTransactionHandler {
@Override
public void handlePauseActivity(IBinder token, boolean finished, boolean userLeaving,
- int configChanges, boolean dontReport, PendingTransactionActions pendingActions,
- String reason) {
+ int configChanges, PendingTransactionActions pendingActions, String reason) {
ActivityClientRecord r = mActivities.get(token);
if (r != null) {
if (userLeaving) {
@@ -3915,15 +3914,6 @@ public final class ActivityThread extends ClientTransactionHandler {
if (r.isPreHoneycomb()) {
QueuedWork.waitToFinish();
}
-
- // Tell the activity manager we have paused.
- if (!dontReport) {
- try {
- ActivityManager.getService().activityPaused(token);
- } catch (RemoteException ex) {
- throw ex.rethrowFromSystemServer();
- }
- }
mSomeActivitiesChanged = true;
}
}
diff --git a/core/java/android/app/ClientTransactionHandler.java b/core/java/android/app/ClientTransactionHandler.java
index cb52a855e7ca..6bc66ecdb261 100644
--- a/core/java/android/app/ClientTransactionHandler.java
+++ b/core/java/android/app/ClientTransactionHandler.java
@@ -65,8 +65,7 @@ public abstract class ClientTransactionHandler {
/** Pause the activity. */
public abstract void handlePauseActivity(IBinder token, boolean finished, boolean userLeaving,
- int configChanges, boolean dontReport, PendingTransactionActions pendingActions,
- String reason);
+ int configChanges, PendingTransactionActions pendingActions, String reason);
/** Resume the activity. */
public abstract void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward,
diff --git a/core/java/android/app/StatsManager.java b/core/java/android/app/StatsManager.java
index c2c91c2bcbd6..ee6a5cafefd6 100644
--- a/core/java/android/app/StatsManager.java
+++ b/core/java/android/app/StatsManager.java
@@ -16,6 +16,7 @@
package android.app;
import android.Manifest;
+import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.os.IBinder;
@@ -75,14 +76,6 @@ public final class StatsManager {
}
/**
- * Temporary. Will be deleted.
- */
- @RequiresPermission(Manifest.permission.DUMP)
- public boolean addConfiguration(long configKey, byte[] config, String a, String b) {
- return addConfiguration(configKey, config);
- }
-
- /**
* Clients can send a configuration and simultaneously registers the name of a broadcast
* receiver that listens for when it should request data.
*
@@ -226,10 +219,11 @@ public final class StatsManager {
* the retrieved metrics from statsd memory.
*
* @param configKey Configuration key to retrieve data from.
- * @return Serialized ConfigMetricsReportList proto. Returns null on failure.
+ * @return Serialized ConfigMetricsReportList proto. Returns null on failure (eg, if statsd
+ * crashed).
*/
@RequiresPermission(Manifest.permission.DUMP)
- public byte[] getData(long configKey) {
+ public @Nullable byte[] getData(long configKey) {
synchronized (this) {
try {
IStatsManager service = getIStatsManagerLocked();
@@ -239,7 +233,7 @@ public final class StatsManager {
}
return service.getData(configKey);
} catch (RemoteException e) {
- if (DEBUG) Slog.d(TAG, "Failed to connecto statsd when getting data");
+ if (DEBUG) Slog.d(TAG, "Failed to connect to statsd when getting data");
return null;
}
}
@@ -250,10 +244,10 @@ public final class StatsManager {
* the actual metrics themselves (metrics must be collected via {@link #getData(String)}.
* This getter is not destructive and will not reset any metrics/counters.
*
- * @return Serialized StatsdStatsReport proto. Returns null on failure.
+ * @return Serialized StatsdStatsReport proto. Returns null on failure (eg, if statsd crashed).
*/
@RequiresPermission(Manifest.permission.DUMP)
- public byte[] getMetadata() {
+ public @Nullable byte[] getMetadata() {
synchronized (this) {
try {
IStatsManager service = getIStatsManagerLocked();
diff --git a/core/java/android/app/servertransaction/PauseActivityItem.java b/core/java/android/app/servertransaction/PauseActivityItem.java
index 578f0e3d24cb..65e429126ac2 100644
--- a/core/java/android/app/servertransaction/PauseActivityItem.java
+++ b/core/java/android/app/servertransaction/PauseActivityItem.java
@@ -42,8 +42,8 @@ public class PauseActivityItem extends ActivityLifecycleItem {
public void execute(ClientTransactionHandler client, IBinder token,
PendingTransactionActions pendingActions) {
Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityPause");
- client.handlePauseActivity(token, mFinished, mUserLeaving, mConfigChanges, mDontReport,
- pendingActions, "PAUSE_ACTIVITY_ITEM");
+ client.handlePauseActivity(token, mFinished, mUserLeaving, mConfigChanges, pendingActions,
+ "PAUSE_ACTIVITY_ITEM");
Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}
diff --git a/core/java/android/app/servertransaction/TransactionExecutor.java b/core/java/android/app/servertransaction/TransactionExecutor.java
index b66d61b76d5f..059e0af27a33 100644
--- a/core/java/android/app/servertransaction/TransactionExecutor.java
+++ b/core/java/android/app/servertransaction/TransactionExecutor.java
@@ -185,8 +185,8 @@ public class TransactionExecutor {
break;
case ON_PAUSE:
mTransactionHandler.handlePauseActivity(r.token, false /* finished */,
- false /* userLeaving */, 0 /* configChanges */,
- true /* dontReport */, mPendingActions, "LIFECYCLER_PAUSE_ACTIVITY");
+ false /* userLeaving */, 0 /* configChanges */, mPendingActions,
+ "LIFECYCLER_PAUSE_ACTIVITY");
break;
case ON_STOP:
mTransactionHandler.handleStopActivity(r.token, false /* show */,
diff --git a/core/java/android/content/ClipboardManager.java b/core/java/android/content/ClipboardManager.java
index 718e465bf0de..73b6eb27bed3 100644
--- a/core/java/android/content/ClipboardManager.java
+++ b/core/java/android/content/ClipboardManager.java
@@ -16,13 +16,16 @@
package android.content;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.SystemService;
import android.os.Handler;
-import android.os.Message;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.ServiceManager.ServiceNotFoundException;
+import com.android.internal.util.Preconditions;
+
import java.util.ArrayList;
/**
@@ -45,6 +48,7 @@ import java.util.ArrayList;
@SystemService(Context.CLIPBOARD_SERVICE)
public class ClipboardManager extends android.text.ClipboardManager {
private final Context mContext;
+ private final Handler mHandler;
private final IClipboard mService;
private final ArrayList<OnPrimaryClipChangedListener> mPrimaryClipChangedListeners
@@ -52,20 +56,11 @@ public class ClipboardManager extends android.text.ClipboardManager {
private final IOnPrimaryClipChangedListener.Stub mPrimaryClipChangedServiceListener
= new IOnPrimaryClipChangedListener.Stub() {
- public void dispatchPrimaryClipChanged() {
- mHandler.sendEmptyMessage(MSG_REPORT_PRIMARY_CLIP_CHANGED);
- }
- };
-
- static final int MSG_REPORT_PRIMARY_CLIP_CHANGED = 1;
-
- private final Handler mHandler = new Handler() {
@Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_REPORT_PRIMARY_CLIP_CHANGED:
- reportPrimaryClipChanged();
- }
+ public void dispatchPrimaryClipChanged() {
+ mHandler.post(() -> {
+ reportPrimaryClipChanged();
+ });
}
};
@@ -89,6 +84,7 @@ public class ClipboardManager extends android.text.ClipboardManager {
/** {@hide} */
public ClipboardManager(Context context, Handler handler) throws ServiceNotFoundException {
mContext = context;
+ mHandler = handler;
mService = IClipboard.Stub.asInterface(
ServiceManager.getServiceOrThrow(Context.CLIPBOARD_SERVICE));
}
@@ -98,12 +94,13 @@ public class ClipboardManager extends android.text.ClipboardManager {
* is involved in normal cut and paste operations.
*
* @param clip The clipped data item to set.
+ * @see #getPrimaryClip()
+ * @see #clearPrimaryClip()
*/
- public void setPrimaryClip(ClipData clip) {
+ public void setPrimaryClip(@NonNull ClipData clip) {
try {
- if (clip != null) {
- clip.prepareToLeaveProcess(true);
- }
+ Preconditions.checkNotNull(clip);
+ clip.prepareToLeaveProcess(true);
mService.setPrimaryClip(clip, mContext.getOpPackageName());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -111,9 +108,24 @@ public class ClipboardManager extends android.text.ClipboardManager {
}
/**
+ * Clears any current primary clip on the clipboard.
+ *
+ * @see #setPrimaryClip(ClipData)
+ */
+ public void clearPrimaryClip() {
+ try {
+ mService.clearPrimaryClip(mContext.getOpPackageName());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Returns the current primary clip on the clipboard.
+ *
+ * @see #setPrimaryClip(ClipData)
*/
- public ClipData getPrimaryClip() {
+ public @Nullable ClipData getPrimaryClip() {
try {
return mService.getPrimaryClip(mContext.getOpPackageName());
} catch (RemoteException e) {
@@ -124,8 +136,10 @@ public class ClipboardManager extends android.text.ClipboardManager {
/**
* Returns a description of the current primary clip on the clipboard
* but not a copy of its data.
+ *
+ * @see #setPrimaryClip(ClipData)
*/
- public ClipDescription getPrimaryClipDescription() {
+ public @Nullable ClipDescription getPrimaryClipDescription() {
try {
return mService.getPrimaryClipDescription(mContext.getOpPackageName());
} catch (RemoteException e) {
diff --git a/core/java/android/content/IClipboard.aidl b/core/java/android/content/IClipboard.aidl
index af0b8f0593a6..135a4363ef21 100644
--- a/core/java/android/content/IClipboard.aidl
+++ b/core/java/android/content/IClipboard.aidl
@@ -27,6 +27,7 @@ import android.content.IOnPrimaryClipChangedListener;
*/
interface IClipboard {
void setPrimaryClip(in ClipData clip, String callingPackage);
+ void clearPrimaryClip(String callingPackage);
ClipData getPrimaryClip(String pkg);
ClipDescription getPrimaryClipDescription(String callingPackage);
boolean hasPrimaryClip(String callingPackage);
diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java
index 93690bf3aef9..2baf539317e9 100644
--- a/core/java/android/content/res/Configuration.java
+++ b/core/java/android/content/res/Configuration.java
@@ -1615,12 +1615,7 @@ public final class Configuration implements Parcelable, Comparable<Configuration
dest.writeInt(mnc);
fixUpLocaleList();
- final int localeListSize = mLocaleList.size();
- dest.writeInt(localeListSize);
- for (int i = 0; i < localeListSize; ++i) {
- final Locale l = mLocaleList.get(i);
- dest.writeString(l.toLanguageTag());
- }
+ dest.writeParcelable(mLocaleList, flags);
if(userSetLocale) {
dest.writeInt(1);
@@ -1654,12 +1649,7 @@ public final class Configuration implements Parcelable, Comparable<Configuration
mcc = source.readInt();
mnc = source.readInt();
- final int localeListSize = source.readInt();
- final Locale[] localeArray = new Locale[localeListSize];
- for (int i = 0; i < localeListSize; ++i) {
- localeArray[i] = Locale.forLanguageTag(source.readString());
- }
- mLocaleList = new LocaleList(localeArray);
+ mLocaleList = source.readParcelable(LocaleList.class.getClassLoader());
locale = mLocaleList.get(0);
userSetLocale = (source.readInt()==1);
diff --git a/core/java/android/os/IStatsCompanionService.aidl b/core/java/android/os/IStatsCompanionService.aidl
index eae52171ee48..29c298e31ae9 100644
--- a/core/java/android/os/IStatsCompanionService.aidl
+++ b/core/java/android/os/IStatsCompanionService.aidl
@@ -41,7 +41,7 @@ interface IStatsCompanionService {
oneway void cancelAnomalyAlarm();
/**
- * Register a repeating alarm for polling to fire at the given timestamp and every
+ * Register a repeating alarm for pulling to fire at the given timestamp and every
* intervalMs thereafter (in ms since epoch).
* If polling alarm had already been registered, it will be replaced by new one.
* Uses AlarmManager.setRepeating API, so if the timestamp is in past, alarm fires immediately,
@@ -49,9 +49,19 @@ interface IStatsCompanionService {
*/
oneway void setPullingAlarms(long timestampMs, long intervalMs);
- /** Cancel any repeating polling alarm. */
+ /** Cancel any repeating pulling alarm. */
oneway void cancelPullingAlarms();
+ /**
+ * Register an alarm when we want to trigger subscribers at the given
+ * timestamp (in ms since epoch).
+ * If an alarm had already been registered, it will be replaced by new one.
+ */
+ oneway void setAlarmForSubscriberTriggering(long timestampMs);
+
+ /** Cancel any alarm for the purpose of subscriber triggering. */
+ oneway void cancelAlarmForSubscriberTriggering();
+
/** Pull the specified data. Results will be sent to statsd when complete. */
StatsLogEventWrapper[] pullData(int pullCode);
diff --git a/core/java/android/os/IStatsManager.aidl b/core/java/android/os/IStatsManager.aidl
index 682a24f17648..2a68714431d4 100644
--- a/core/java/android/os/IStatsManager.aidl
+++ b/core/java/android/os/IStatsManager.aidl
@@ -47,6 +47,13 @@ interface IStatsManager {
void informPollAlarmFired();
/**
+ * Tells statsd that it is time to handle periodic alarms. Statsd will be responsible for
+ * determing what alarm subscriber to trigger.
+ * Two-way binder call so that caller's method (and corresponding wakelocks) will linger.
+ */
+ void informAlarmForSubscriberTriggeringFired();
+
+ /**
* Tells statsd to store data to disk.
*/
void writeDataToDisk();
diff --git a/core/java/android/provider/CallLog.java b/core/java/android/provider/CallLog.java
index 60df467bc20f..70de09ebe85f 100644
--- a/core/java/android/provider/CallLog.java
+++ b/core/java/android/provider/CallLog.java
@@ -223,14 +223,14 @@ public class CallLog {
/** Call was WIFI call. */
public static final int FEATURES_WIFI = 1 << 3;
- /** Call was on RTT at some point */
- public static final int FEATURES_RTT = 1 << 4;
-
/**
* Indicates the call underwent Assisted Dialing.
* @hide
*/
- public static final Integer FEATURES_ASSISTED_DIALING_USED = 0x10;
+ public static final int FEATURES_ASSISTED_DIALING_USED = 1 << 4;
+
+ /** Call was on RTT at some point */
+ public static final int FEATURES_RTT = 1 << 5;
/**
* The phone number as the user entered it.
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index cb38c0fc55a3..02994079d6ec 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -8581,6 +8581,7 @@ public final class Settings {
* (0 = false, 1 = true)
* @hide
*/
+ @SystemApi
public static final String EUICC_PROVISIONED = "euicc_provisioned";
/**
diff --git a/core/java/android/security/ConfirmationDialog.java b/core/java/android/security/ConfirmationDialog.java
index e9df3705db6e..f6127e184139 100644
--- a/core/java/android/security/ConfirmationDialog.java
+++ b/core/java/android/security/ConfirmationDialog.java
@@ -17,7 +17,10 @@
package android.security;
import android.annotation.NonNull;
+import android.content.ContentResolver;
import android.content.Context;
+import android.provider.Settings;
+import android.provider.Settings.SettingNotFoundException;
import android.text.TextUtils;
import android.util.Log;
@@ -86,6 +89,7 @@ public class ConfirmationDialog {
private byte[] mExtraData;
private ConfirmationCallback mCallback;
private Executor mExecutor;
+ private Context mContext;
private final KeyStore mKeyStore = KeyStore.getInstance();
@@ -190,15 +194,39 @@ public class ConfirmationDialog {
if (mExtraData == null) {
throw new IllegalArgumentException("extraData must be set");
}
- return new ConfirmationDialog(mPromptText, mExtraData);
+ return new ConfirmationDialog(context, mPromptText, mExtraData);
}
}
- private ConfirmationDialog(CharSequence promptText, byte[] extraData) {
+ private ConfirmationDialog(Context context, CharSequence promptText, byte[] extraData) {
+ mContext = context;
mPromptText = promptText;
mExtraData = extraData;
}
+ private static final int UI_OPTION_ACCESSIBILITY_INVERTED_FLAG = 1 << 0;
+ private static final int UI_OPTION_ACCESSIBILITY_MAGNIFIED_FLAG = 1 << 1;
+
+ private int getUiOptionsAsFlags() {
+ int uiOptionsAsFlags = 0;
+ try {
+ ContentResolver contentResolver = mContext.getContentResolver();
+ int inversionEnabled = Settings.Secure.getInt(contentResolver,
+ Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED);
+ if (inversionEnabled == 1) {
+ uiOptionsAsFlags |= UI_OPTION_ACCESSIBILITY_INVERTED_FLAG;
+ }
+ float fontScale = Settings.System.getFloat(contentResolver,
+ Settings.System.FONT_SCALE);
+ if (fontScale > 1.0) {
+ uiOptionsAsFlags |= UI_OPTION_ACCESSIBILITY_MAGNIFIED_FLAG;
+ }
+ } catch (SettingNotFoundException e) {
+ Log.w(TAG, "Unexpected SettingNotFoundException");
+ }
+ return uiOptionsAsFlags;
+ }
+
/**
* Requests a confirmation prompt to be presented to the user.
*
@@ -220,8 +248,7 @@ public class ConfirmationDialog {
mCallback = callback;
mExecutor = executor;
- int uiOptionsAsFlags = 0;
- // TODO: set AccessibilityInverted, AccessibilityMagnified in uiOptionsAsFlags as needed.
+ int uiOptionsAsFlags = getUiOptionsAsFlags();
String locale = Locale.getDefault().toLanguageTag();
int responseCode = mKeyStore.presentConfirmationPrompt(
mCallbackBinder, mPromptText.toString(), mExtraData, locale, uiOptionsAsFlags);
@@ -277,7 +304,6 @@ public class ConfirmationDialog {
* @return true if confirmation prompts are supported by the device.
*/
public static boolean isSupported() {
- // TODO: read and return system property.
- return true;
+ return KeyStore.getInstance().isConfirmationPromptSupported();
}
}
diff --git a/core/java/android/security/keystore/BadCertificateFormatException.java b/core/java/android/security/keystore/BadCertificateFormatException.java
index ddc7bd2366ac..c51b7737e823 100644
--- a/core/java/android/security/keystore/BadCertificateFormatException.java
+++ b/core/java/android/security/keystore/BadCertificateFormatException.java
@@ -17,8 +17,7 @@
package android.security.keystore;
/**
- * Error thrown when the recovery agent supplies an invalid X509 certificate.
- *
+ * @deprecated Use {@link android.security.keystore.recovery.BadCertificateFormatException}.
* @hide
*/
public class BadCertificateFormatException extends RecoveryControllerException {
diff --git a/core/java/android/security/keystore/DecryptionFailedException.java b/core/java/android/security/keystore/DecryptionFailedException.java
index 945fcf6f88f2..c0b52f714d0b 100644
--- a/core/java/android/security/keystore/DecryptionFailedException.java
+++ b/core/java/android/security/keystore/DecryptionFailedException.java
@@ -17,9 +17,7 @@
package android.security.keystore;
/**
- * Error thrown when decryption failed, due to an agent error. i.e., using the incorrect key,
- * trying to decrypt garbage data, trying to decrypt data that has somehow been corrupted, etc.
- *
+ * @deprecated Use {@link android.security.keystore.recovery.DecryptionFailedException}.
* @hide
*/
public class DecryptionFailedException extends RecoveryControllerException {
diff --git a/core/java/android/security/keystore/InternalRecoveryServiceException.java b/core/java/android/security/keystore/InternalRecoveryServiceException.java
index 85829bed9191..40076f732b98 100644
--- a/core/java/android/security/keystore/InternalRecoveryServiceException.java
+++ b/core/java/android/security/keystore/InternalRecoveryServiceException.java
@@ -17,11 +17,7 @@
package android.security.keystore;
/**
- * An error thrown when something went wrong internally in the recovery service.
- *
- * <p>This is an unexpected error, and indicates a problem with the service itself, rather than the
- * caller having performed some kind of illegal action.
- *
+ * @deprecated Use {@link android.security.keystore.recovery.InternalRecoveryServiceException}.
* @hide
*/
public class InternalRecoveryServiceException extends RecoveryControllerException {
diff --git a/core/java/android/security/keystore/KeyDerivationParams.java b/core/java/android/security/keystore/KeyDerivationParams.java
index b19cee2d31a4..e475dc36e1c3 100644
--- a/core/java/android/security/keystore/KeyDerivationParams.java
+++ b/core/java/android/security/keystore/KeyDerivationParams.java
@@ -27,9 +27,7 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
- * Collection of parameters which define a key derivation function.
- * Currently only supports salted SHA-256
- *
+ * @deprecated Use {@link android.security.keystore.recovery.KeyDerivationParams}.
* @hide
*/
public final class KeyDerivationParams implements Parcelable {
diff --git a/core/java/android/security/keystore/KeychainProtectionParams.java b/core/java/android/security/keystore/KeychainProtectionParams.java
index a940fdc778a9..19a087d5d1d4 100644
--- a/core/java/android/security/keystore/KeychainProtectionParams.java
+++ b/core/java/android/security/keystore/KeychainProtectionParams.java
@@ -28,23 +28,7 @@ import java.lang.annotation.RetentionPolicy;
import java.util.Arrays;
/**
- * A {@link KeychainSnapshot} is protected with a key derived from the user's lock screen. This
- * class wraps all the data necessary to derive the same key on a recovering device:
- *
- * <ul>
- * <li>UI parameters for the user's lock screen - so that if e.g., the user was using a pattern,
- * the recovering device can display the pattern UI to the user when asking them to enter
- * the lock screen from their previous device.
- * <li>The algorithm used to derive a key from the user's lock screen, e.g. SHA-256 with a salt.
- * </ul>
- *
- * <p>As such, this data is sent along with the {@link KeychainSnapshot} when syncing the current
- * version of the keychain.
- *
- * <p>For now, the recoverable keychain only supports a single layer of protection, which is the
- * user's lock screen. In the future, the keychain will support multiple layers of protection
- * (e.g. an additional keychain password, along with the lock screen).
- *
+ * @deprecated Use {@link android.security.keystore.recovery.KeyChainProtectionParams}.
* @hide
*/
public final class KeychainProtectionParams implements Parcelable {
diff --git a/core/java/android/security/keystore/KeychainSnapshot.java b/core/java/android/security/keystore/KeychainSnapshot.java
index 23aec25eb128..cf18fd1c6a0b 100644
--- a/core/java/android/security/keystore/KeychainSnapshot.java
+++ b/core/java/android/security/keystore/KeychainSnapshot.java
@@ -25,21 +25,7 @@ import com.android.internal.util.Preconditions;
import java.util.List;
/**
- * A snapshot of a version of the keystore. Two events can trigger the generation of a new snapshot:
- *
- * <ul>
- * <li>The user's lock screen changes. (A key derived from the user's lock screen is used to
- * protected the keychain, which is why this forces a new snapshot.)
- * <li>A key is added to or removed from the recoverable keychain.
- * </ul>
- *
- * <p>The snapshot data is also encrypted with the remote trusted hardware's public key, so even
- * the recovery agent itself should not be able to decipher the data. The recovery agent sends an
- * instance of this to the remote trusted hardware whenever a new snapshot is generated. During a
- * recovery flow, the recovery agent retrieves a snapshot from the remote trusted hardware. It then
- * sends it to the framework, where it is decrypted using the user's lock screen from their previous
- * device.
- *
+ * @deprecated Use {@link android.security.keystore.recovery.KeyChainSnapshot}.
* @hide
*/
public final class KeychainSnapshot implements Parcelable {
diff --git a/core/java/android/security/keystore/LockScreenRequiredException.java b/core/java/android/security/keystore/LockScreenRequiredException.java
index b07fb9cdd002..097028457c9e 100644
--- a/core/java/android/security/keystore/LockScreenRequiredException.java
+++ b/core/java/android/security/keystore/LockScreenRequiredException.java
@@ -17,10 +17,7 @@
package android.security.keystore;
/**
- * Error thrown when trying to generate keys for a profile that has no lock screen set.
- *
- * <p>A lock screen must be set, as the lock screen is used to encrypt the snapshot.
- *
+ * @deprecated Use {@link android.security.keystore.recovery.LockScreenRequiredException}.
* @hide
*/
public class LockScreenRequiredException extends RecoveryControllerException {
diff --git a/core/java/android/security/keystore/RecoveryClaim.java b/core/java/android/security/keystore/RecoveryClaim.java
index 6f566af1dc7d..12be607a23d4 100644
--- a/core/java/android/security/keystore/RecoveryClaim.java
+++ b/core/java/android/security/keystore/RecoveryClaim.java
@@ -17,8 +17,7 @@
package android.security.keystore;
/**
- * An attempt to recover a keychain protected by remote secure hardware.
- *
+ * @deprecated Use {@link android.security.keystore.recovery.RecoverySession}.
* @hide
*/
public class RecoveryClaim {
diff --git a/core/java/android/security/keystore/RecoveryController.java b/core/java/android/security/keystore/RecoveryController.java
index 4a0de5f2c7f0..145261e3b71d 100644
--- a/core/java/android/security/keystore/RecoveryController.java
+++ b/core/java/android/security/keystore/RecoveryController.java
@@ -31,22 +31,6 @@ import java.util.List;
import java.util.Map;
/**
- * An assistant for generating {@link javax.crypto.SecretKey} instances that can be recovered by
- * other Android devices belonging to the user. The exported keychain is protected by the user's
- * lock screen.
- *
- * <p>The RecoveryController must be paired with a recovery agent. The recovery agent is responsible
- * for transporting the keychain to remote trusted hardware. This hardware must prevent brute force
- * attempts against the user's lock screen by limiting the number of allowed guesses (to, e.g., 10).
- * After that number of incorrect guesses, the trusted hardware no longer allows access to the
- * key chain.
- *
- * <p>For now only the recovery agent itself is able to create keys, so it is expected that the
- * recovery agent is itself the system app.
- *
- * <p>A recovery agent requires the privileged permission
- * {@code android.Manifest.permission#RECOVER_KEYSTORE}.
- *
* @deprecated Use {@link android.security.keystore.recovery.RecoveryController}.
* @hide
*/
diff --git a/core/java/android/security/keystore/RecoveryControllerException.java b/core/java/android/security/keystore/RecoveryControllerException.java
index 5b806b75ebab..f990c236c9d3 100644
--- a/core/java/android/security/keystore/RecoveryControllerException.java
+++ b/core/java/android/security/keystore/RecoveryControllerException.java
@@ -19,8 +19,7 @@ package android.security.keystore;
import java.security.GeneralSecurityException;
/**
- * Base exception for errors thrown by {@link RecoveryController}.
- *
+ * @deprecated Use {@link android.security.keystore.recovery.RecoveryController}.
* @hide
*/
public abstract class RecoveryControllerException extends GeneralSecurityException {
diff --git a/core/java/android/security/keystore/RecoverySession.java b/core/java/android/security/keystore/RecoverySession.java
index ae8d91af3230..8a3e06b7deb1 100644
--- a/core/java/android/security/keystore/RecoverySession.java
+++ b/core/java/android/security/keystore/RecoverySession.java
@@ -19,9 +19,7 @@ package android.security.keystore;
import java.security.SecureRandom;
/**
- * Session to recover a {@link KeychainSnapshot} from the remote trusted hardware, initiated by a
- * recovery agent.
- *
+ * @deprecated Use {@link android.security.keystore.recovery.RecoverySession}.
* @hide
*/
public class RecoverySession implements AutoCloseable {
diff --git a/core/java/android/security/keystore/SessionExpiredException.java b/core/java/android/security/keystore/SessionExpiredException.java
index f13e20602625..7c8d5e4f52f9 100644
--- a/core/java/android/security/keystore/SessionExpiredException.java
+++ b/core/java/android/security/keystore/SessionExpiredException.java
@@ -17,8 +17,7 @@
package android.security.keystore;
/**
- * Error thrown when attempting to use a {@link RecoverySession} that has since expired.
- *
+ * @deprecated Use {@link android.security.keystore.recovery.SessionExpiredException}.
* @hide
*/
public class SessionExpiredException extends RecoveryControllerException {
diff --git a/core/java/android/security/keystore/WrappedApplicationKey.java b/core/java/android/security/keystore/WrappedApplicationKey.java
index 522bb9557b8d..2ce8c7d395d5 100644
--- a/core/java/android/security/keystore/WrappedApplicationKey.java
+++ b/core/java/android/security/keystore/WrappedApplicationKey.java
@@ -23,16 +23,7 @@ import android.os.Parcelable;
import com.android.internal.util.Preconditions;
/**
- * Helper class with data necessary recover a single application key, given a recovery key.
- *
- * <ul>
- * <li>Alias - Keystore alias of the key.
- * <li>Encrypted key material.
- * </ul>
- *
- * Note that Application info is not included. Recovery Agent can only make its own keys
- * recoverable.
- *
+ * @deprecated Use {@link android.security.keystore.recovery.WrappedApplicationKey}.
* @hide
*/
public final class WrappedApplicationKey implements Parcelable {
diff --git a/core/java/android/security/keystore/recovery/RecoveryClaim.java b/core/java/android/security/keystore/recovery/RecoveryClaim.java
deleted file mode 100644
index 45c6b4ff6758..000000000000
--- a/core/java/android/security/keystore/recovery/RecoveryClaim.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.security.keystore.recovery;
-
-/**
- * An attempt to recover a keychain protected by remote secure hardware.
- *
- * @hide
- * Deprecated
- */
-public class RecoveryClaim {
-
- private final RecoverySession mRecoverySession;
- private final byte[] mClaimBytes;
-
- RecoveryClaim(RecoverySession recoverySession, byte[] claimBytes) {
- mRecoverySession = recoverySession;
- mClaimBytes = claimBytes;
- }
-
- /**
- * Returns the session associated with the recovery attempt. This is used to match the symmetric
- * key, which remains internal to the framework, for decrypting the claim response.
- *
- * @return The session data.
- */
- public RecoverySession getRecoverySession() {
- return mRecoverySession;
- }
-
- /**
- * Returns the encrypted claim's bytes.
- *
- * <p>This should be sent by the recovery agent to the remote secure hardware, which will use
- * it to decrypt the keychain, before sending it re-encrypted with the session's symmetric key
- * to the device.
- */
- public byte[] getClaimBytes() {
- return mClaimBytes;
- }
-}
diff --git a/core/java/android/security/keystore/recovery/RecoveryController.java b/core/java/android/security/keystore/recovery/RecoveryController.java
index 0683e02b66a7..426ca5c472b9 100644
--- a/core/java/android/security/keystore/recovery/RecoveryController.java
+++ b/core/java/android/security/keystore/recovery/RecoveryController.java
@@ -113,6 +113,14 @@ public class RecoveryController {
*/
public static final int ERROR_DECRYPTION_FAILED = 26;
+ /**
+ * Error thrown if the format of a given key is invalid. This might be because the key has a
+ * wrong length, invalid content, etc.
+ *
+ * @hide
+ */
+ public static final int ERROR_INVALID_KEY_FORMAT = 27;
+
private final ILockSettings mBinder;
private final KeyStore mKeyStore;
@@ -461,6 +469,7 @@ public class RecoveryController {
}
}
+ // TODO: Unhide the following APIs, generateKey(), importKey(), and getKey()
/**
* @deprecated Use {@link #generateKey(String)}.
* @removed
@@ -503,6 +512,40 @@ public class RecoveryController {
}
/**
+ * Imports a 256-bit recoverable AES key with the given {@code alias} and the raw bytes {@code
+ * keyBytes}.
+ *
+ * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery
+ * service.
+ * @throws LockScreenRequiredException if the user does not have a lock screen set. A lock
+ * screen is required to generate recoverable keys.
+ *
+ * @hide
+ */
+ public Key importKey(@NonNull String alias, byte[] keyBytes)
+ throws InternalRecoveryServiceException, LockScreenRequiredException {
+ try {
+ String grantAlias = mBinder.importKey(alias, keyBytes);
+ if (grantAlias == null) {
+ throw new InternalRecoveryServiceException("Null grant alias");
+ }
+ return AndroidKeyStoreProvider.loadAndroidKeyStoreKeyFromKeystore(
+ mKeyStore,
+ grantAlias,
+ KeyStore.UID_SELF);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ } catch (UnrecoverableKeyException e) {
+ throw new InternalRecoveryServiceException("Failed to get key from keystore", e);
+ } catch (ServiceSpecificException e) {
+ if (e.errorCode == ERROR_INSECURE_USER) {
+ throw new LockScreenRequiredException(e.getMessage());
+ }
+ throw wrapUnexpectedServiceSpecificException(e);
+ }
+ }
+
+ /**
* Gets a key called {@code alias} from the recoverable key store.
*
* @param alias The key alias.
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index 0fac58a55aca..eebd22ae64bb 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -1220,6 +1220,7 @@ public abstract class NotificationListenerService extends Service {
// convert icon metadata to legacy format for older clients
createLegacyIconExtras(sbn.getNotification());
maybePopulateRemoteViews(sbn.getNotification());
+ maybePopulatePeople(sbn.getNotification());
} catch (IllegalArgumentException e) {
// warn and drop corrupt notification
Log.w(TAG, "onNotificationPosted: can't rebuild notification from " +
diff --git a/core/java/android/util/StatsLog.java b/core/java/android/util/StatsLog.java
index 3350f3e164bc..517b13b2122c 100644
--- a/core/java/android/util/StatsLog.java
+++ b/core/java/android/util/StatsLog.java
@@ -18,8 +18,7 @@ package android.util;
/**
* StatsLog provides an API for developers to send events to statsd. The events can be used to
- * define custom metrics inside statsd. We will rate-limit how often the calls can be made inside
- * statsd.
+ * define custom metrics inside statsd.
*/
public final class StatsLog extends StatsLogInternal {
private static final String TAG = "StatsManager";
diff --git a/core/java/android/widget/Magnifier.java b/core/java/android/widget/Magnifier.java
index 6e87e2356f65..85f68d7c29f4 100644
--- a/core/java/android/widget/Magnifier.java
+++ b/core/java/android/widget/Magnifier.java
@@ -24,6 +24,7 @@ import android.annotation.UiThread;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
+import android.graphics.Color;
import android.graphics.Outline;
import android.graphics.Paint;
import android.graphics.PixelFormat;
@@ -319,6 +320,10 @@ public final class Magnifier {
* producing a shakiness effect for the magnifier content.
*/
private static class InternalPopupWindow {
+ // The alpha set on the magnifier's content, which defines how
+ // prominent the white background is.
+ private static final int CONTENT_BITMAP_ALPHA = 242;
+
// Display associated to the view the magnifier is attached to.
private final Display mDisplay;
// The size of the content of the magnifier.
@@ -511,10 +516,13 @@ public final class Magnifier {
final DisplayListCanvas canvas =
mBitmapRenderNode.start(mContentWidth, mContentHeight);
try {
+ canvas.drawColor(Color.WHITE);
+
final Rect srcRect = new Rect(0, 0, mBitmap.getWidth(), mBitmap.getHeight());
final Rect dstRect = new Rect(0, 0, mContentWidth, mContentHeight);
final Paint paint = new Paint();
paint.setFilterBitmap(true);
+ paint.setAlpha(CONTENT_BITMAP_ALPHA);
canvas.drawBitmap(mBitmap, srcRect, dstRect, paint);
} finally {
mBitmapRenderNode.end(canvas);
diff --git a/core/java/com/android/internal/widget/ILockSettings.aidl b/core/java/com/android/internal/widget/ILockSettings.aidl
index d3fc644c2341..7c9cf7a183cd 100644
--- a/core/java/com/android/internal/widget/ILockSettings.aidl
+++ b/core/java/com/android/internal/widget/ILockSettings.aidl
@@ -68,6 +68,7 @@ interface ILockSettings {
KeyChainSnapshot getKeyChainSnapshot();
byte[] generateAndStoreKey(String alias);
String generateKey(String alias);
+ String importKey(String alias, in byte[] keyBytes);
String getKey(String alias);
void removeKey(String alias);
void setSnapshotCreatedPendingIntent(in PendingIntent intent);
diff --git a/core/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp
index 61a22c144f1b..6456fe622f98 100644
--- a/core/jni/android_media_AudioTrack.cpp
+++ b/core/jni/android_media_AudioTrack.cpp
@@ -282,7 +282,7 @@ android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject weak_this, job
// compute the frame count
size_t frameCount;
- if (audio_is_linear_pcm(format)) {
+ if (audio_has_proportional_frames(format)) {
const size_t bytesPerSample = audio_bytes_per_sample(format);
frameCount = buffSizeInBytes / (channelCount * bytesPerSample);
} else {
diff --git a/core/proto/android/server/alarmmanagerservice.proto b/core/proto/android/server/alarmmanagerservice.proto
index d1c5db66a841..b288c1149731 100644
--- a/core/proto/android/server/alarmmanagerservice.proto
+++ b/core/proto/android/server/alarmmanagerservice.proto
@@ -220,6 +220,8 @@ message ConstantsProto {
optional int64 allow_while_idle_long_duration_ms = 5;
// BroadcastOptions.setTemporaryAppWhitelistDuration() to use for FLAG_ALLOW_WHILE_IDLE.
optional int64 allow_while_idle_whitelist_duration_ms = 6;
+ // Maximum alarm recurrence interval.
+ optional int64 max_interval_duration_ms = 7;
}
// A com.android.server.AlarmManagerService.FilterStats object.
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 2ce08ebb882f..291826025cc1 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -541,7 +541,7 @@
<!-- Magnifier dimensions -->
<dimen name="magnifier_width">100dp</dimen>
<dimen name="magnifier_height">48dp</dimen>
- <dimen name="magnifier_elevation">2dp</dimen>
+ <dimen name="magnifier_elevation">4dp</dimen>
<dimen name="magnifier_offset">42dp</dimen>
<item type="dimen" format="float" name="magnifier_zoom_scale">1.25</item>
diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java
index ded427eb244a..1924bbe764c7 100644
--- a/keystore/java/android/security/KeyStore.java
+++ b/keystore/java/android/security/KeyStore.java
@@ -784,6 +784,20 @@ public class KeyStore {
}
/**
+ * Requests keystore to check if the confirmationui HAL is available.
+ *
+ * @return whether the confirmationUI HAL is available.
+ */
+ public boolean isConfirmationPromptSupported() {
+ try {
+ return mBinder.isConfirmationPromptSupported();
+ } catch (RemoteException e) {
+ Log.w(TAG, "Cannot connect to keystore", e);
+ return false;
+ }
+ }
+
+ /**
* Returns a {@link KeyStoreException} corresponding to the provided keystore/keymaster error
* code.
*/
diff --git a/libs/hwui/JankTracker.cpp b/libs/hwui/JankTracker.cpp
index ab27a0d00246..cf29e434a351 100644
--- a/libs/hwui/JankTracker.cpp
+++ b/libs/hwui/JankTracker.cpp
@@ -165,7 +165,7 @@ void JankTracker::finishFrame(const FrameInfo& frame) {
ALOGI("%s", ss.str().c_str());
// Just so we have something that counts up, the value is largely irrelevant
ATRACE_INT(ss.str().c_str(), ++sDaveyCount);
- android::util::stats_write(android::util::DAVEY_OCCURRED, ns2ms(totalDuration));
+ android::util::stats_write(android::util::DAVEY_OCCURRED, getuid(), ns2ms(totalDuration));
}
}
diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp
index 022198beae45..62030bba44f5 100644
--- a/media/jni/android_media_MediaCodec.cpp
+++ b/media/jni/android_media_MediaCodec.cpp
@@ -36,7 +36,6 @@
#include <gui/Surface.h>
-#include <media/ICrypto.h>
#include <media/MediaCodecBuffer.h>
#include <media/stagefright/MediaCodec.h>
#include <media/stagefright/foundation/ABuffer.h>
@@ -46,6 +45,7 @@
#include <media/stagefright/foundation/AString.h>
#include <media/stagefright/MediaErrors.h>
#include <media/stagefright/PersistentSurface.h>
+#include <mediadrm/ICrypto.h>
#include <nativehelper/ScopedLocalRef.h>
#include <system/window.h>
diff --git a/media/jni/android_media_MediaCrypto.cpp b/media/jni/android_media_MediaCrypto.cpp
index 1b3c24fcde49..2d9051f5230d 100644
--- a/media/jni/android_media_MediaCrypto.cpp
+++ b/media/jni/android_media_MediaCrypto.cpp
@@ -26,9 +26,9 @@
#include <binder/IServiceManager.h>
#include <cutils/properties.h>
-#include <media/ICrypto.h>
-#include <media/IMediaDrmService.h>
#include <media/stagefright/foundation/ADebug.h>
+#include <mediadrm/ICrypto.h>
+#include <mediadrm/IMediaDrmService.h>
namespace android {
diff --git a/media/jni/android_media_MediaDrm.cpp b/media/jni/android_media_MediaDrm.cpp
index 3518392d30b6..4c20f050087d 100644
--- a/media/jni/android_media_MediaDrm.cpp
+++ b/media/jni/android_media_MediaDrm.cpp
@@ -31,10 +31,10 @@
#include <binder/Parcel.h>
#include <binder/PersistableBundle.h>
#include <cutils/properties.h>
-#include <media/IDrm.h>
-#include <media/IMediaDrmService.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/MediaErrors.h>
+#include <mediadrm/IDrm.h>
+#include <mediadrm/IMediaDrmService.h>
using ::android::os::PersistableBundle;
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index cf0659aef31c..a444ff9eb20d 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -124,7 +124,7 @@
<!-- Tiles native to System UI. Order should match "quick_settings_tiles_default" -->
<string name="quick_settings_tiles_stock" translatable="false">
- wifi,cell,battery,dnd,flashlight,rotation,bt,airplane,location,hotspot,inversion,saver,work,cast,night,alarm
+ wifi,cell,battery,dnd,flashlight,rotation,bt,airplane,location,hotspot,inversion,saver,work,cast,night
</string>
<!-- The tiles to display in QuickSettings -->
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 3e13b6735f64..3483e65fa0da 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -743,8 +743,6 @@
<string name="quick_settings_wifi_on_label">Wi-Fi On</string>
<!-- QuickSettings: Wifi detail panel, text when there are no items [CHAR LIMIT=NONE] -->
<string name="quick_settings_wifi_detail_empty_text">No Wi-Fi networks available</string>
- <!-- QuickSettings: Alarm title [CHAR LIMIT=NONE] -->
- <string name="quick_settings_alarm_title">Alarm</string>
<!-- QuickSettings: Cast title [CHAR LIMIT=NONE] -->
<string name="quick_settings_cast_title">Cast</string>
<!-- QuickSettings: Cast detail panel, status text when casting [CHAR LIMIT=NONE] -->
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
index b54d09a66535..cecaaa9b660e 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
@@ -162,8 +162,8 @@ public class KeyguardSliceView extends LinearLayout implements View.OnClickListe
mRow.addView(button);
PendingIntent pendingIntent = null;
- if (rc.getPrimaryAction() != null) {
- pendingIntent = rc.getPrimaryAction().getAction();
+ if (rc.getContentIntent() != null) {
+ pendingIntent = rc.getContentIntent().getAction();
}
mClickActions.put(button, pendingIntent);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/PageIndicator.java b/packages/SystemUI/src/com/android/systemui/qs/PageIndicator.java
index b22ea4c81f17..2629f30f40e2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/PageIndicator.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/PageIndicator.java
@@ -24,7 +24,7 @@ public class PageIndicator extends ViewGroup {
// The size of a single dot in relation to the whole animation.
private static final float SINGLE_SCALE = .4f;
- private static final float MINOR_ALPHA = .3f;
+ private static final float MINOR_ALPHA = .42f;
private final ArrayList<Integer> mQueuedPositions = new ArrayList<>();
@@ -53,7 +53,7 @@ public class PageIndicator extends ViewGroup {
removeViewAt(getChildCount() - 1);
}
TypedArray array = getContext().obtainStyledAttributes(
- new int[]{android.R.attr.colorForeground});
+ new int[]{android.R.attr.colorControlActivated});
int color = array.getColor(0, 0);
array.recycle();
while (numPages > getChildCount()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
index b220686b3056..446a1d4c3c7b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
@@ -14,7 +14,6 @@
package com.android.systemui.statusbar.phone;
-import android.app.AlarmManager.AlarmClockInfo;
import android.content.Context;
import android.os.Handler;
import android.provider.Settings.Secure;
@@ -28,8 +27,6 @@ import com.android.systemui.statusbar.policy.DataSaverController;
import com.android.systemui.statusbar.policy.DataSaverController.Listener;
import com.android.systemui.statusbar.policy.HotspotController;
import com.android.systemui.statusbar.policy.HotspotController.Callback;
-import com.android.systemui.statusbar.policy.NextAlarmController;
-import com.android.systemui.statusbar.policy.NextAlarmController.NextAlarmChangeCallback;
/**
* Manages which tiles should be automatically added to QS.
@@ -40,7 +37,6 @@ public class AutoTileManager {
public static final String INVERSION = "inversion";
public static final String WORK = "work";
public static final String NIGHT = "night";
- public static final String ALARM = "alarm";
private final Context mContext;
private final QSTileHost mHost;
@@ -87,9 +83,6 @@ public class AutoTileManager {
&& ColorDisplayController.isAvailable(mContext)) {
Dependency.get(ColorDisplayController.class).setListener(mColorDisplayCallback);
}
- if (!mAutoTracker.isAdded(ALARM)) {
- Dependency.get(NextAlarmController.class).addCallback(mNextAlarmChangeCallback);
- }
}
public void destroy() {
@@ -101,7 +94,6 @@ public class AutoTileManager {
Dependency.get(DataSaverController.class).removeCallback(mDataSaverListener);
Dependency.get(ManagedProfileController.class).removeCallback(mProfileCallback);
Dependency.get(ColorDisplayController.class).setListener(null);
- Dependency.get(NextAlarmController.class).removeCallback(mNextAlarmChangeCallback);
}
private final ManagedProfileController.Callback mProfileCallback =
@@ -150,19 +142,6 @@ public class AutoTileManager {
}
};
- private final NextAlarmChangeCallback mNextAlarmChangeCallback = new NextAlarmChangeCallback() {
- @Override
- public void onNextAlarmChanged(AlarmClockInfo nextAlarm) {
- if (mAutoTracker.isAdded(ALARM)) return;
- if (nextAlarm != null) {
- mHost.addTile(ALARM);
- mAutoTracker.setTileAdded(ALARM);
- mHandler.post(() -> Dependency.get(NextAlarmController.class)
- .removeCallback(mNextAlarmChangeCallback));
- }
- }
- };
-
@VisibleForTesting
final ColorDisplayController.Callback mColorDisplayCallback =
new ColorDisplayController.Callback() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
index 2d2db1bba735..a80b04576715 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
@@ -18,28 +18,21 @@ package com.android.systemui.statusbar.phone;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-import android.app.AlarmManager.AlarmClockInfo;
-import android.os.Handler;
import android.support.test.filters.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
import com.android.internal.app.ColorDisplayController;
import com.android.systemui.Dependency;
+import com.android.systemui.Prefs;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.qs.AutoAddTracker;
import com.android.systemui.qs.QSTileHost;
-import com.android.systemui.statusbar.policy.NextAlarmController;
-import com.android.systemui.statusbar.policy.NextAlarmController.NextAlarmChangeCallback;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Captor;
import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
+import org.mockito.Mockito;
@RunWith(AndroidTestingRunner.class)
@RunWithLooper
@@ -47,19 +40,16 @@ import org.mockito.MockitoAnnotations;
public class AutoTileManagerTest extends SysuiTestCase {
@Mock private QSTileHost mQsTileHost;
- @Mock private AutoAddTracker mAutoAddTracker;
- @Captor private ArgumentCaptor<NextAlarmChangeCallback> mAlarmCallback;
private AutoTileManager mAutoTileManager;
@Before
public void setUp() throws Exception {
- MockitoAnnotations.initMocks(this);
- mDependency.injectMockDependency(NextAlarmController.class);
- mAutoTileManager = new AutoTileManager(mContext, mAutoAddTracker,
- mQsTileHost, new Handler(TestableLooper.get(this).getLooper()));
- verify(Dependency.get(NextAlarmController.class))
- .addCallback(mAlarmCallback.capture());
+ mDependency.injectTestDependency(Dependency.BG_LOOPER,
+ TestableLooper.get(this).getLooper());
+ Prefs.putBoolean(mContext, Prefs.Key.QS_NIGHTDISPLAY_ADDED, false);
+ mQsTileHost = Mockito.mock(QSTileHost.class);
+ mAutoTileManager = new AutoTileManager(mContext, mQsTileHost);
}
@Test
@@ -109,30 +99,4 @@ public class AutoTileManagerTest extends SysuiTestCase {
ColorDisplayController.AUTO_MODE_DISABLED);
verify(mQsTileHost, never()).addTile("night");
}
-
- @Test
- public void alarmTileAdded_whenAlarmSet() {
- mAlarmCallback.getValue().onNextAlarmChanged(new AlarmClockInfo(0, null));
-
- verify(mQsTileHost).addTile("alarm");
- verify(mAutoAddTracker).setTileAdded("alarm");
- }
-
- @Test
- public void alarmTileNotAdded_whenAlarmNotSet() {
- mAlarmCallback.getValue().onNextAlarmChanged(null);
-
- verify(mQsTileHost, never()).addTile("alarm");
- verify(mAutoAddTracker, never()).setTileAdded("alarm");
- }
-
- @Test
- public void alarmTileNotAdded_whenAlreadyAdded() {
- when(mAutoAddTracker.isAdded("alarm")).thenReturn(true);
-
- mAlarmCallback.getValue().onNextAlarmChanged(new AlarmClockInfo(0, null));
-
- verify(mQsTileHost, never()).addTile("alarm");
- verify(mAutoAddTracker, never()).setTileAdded("alarm");
- }
}
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index c56002e88406..b897c7cc8873 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -5328,6 +5328,21 @@ message MetricsEvent {
// OS: P
PACKAGE_OPTIMIZATION_COMPILATION_REASON = 1321;
+ // FIELD: The camera API level used.
+ // CATEGORY: CAMERA
+ // OS: P
+ FIELD_CAMERA_API_LEVEL = 1322;
+
+ // OPEN: Settings > Battery > Battery tip > Battery tip Dialog
+ // CATEGORY: SETTINGS
+ // OS: P
+ FUELGAUGE_BATTERY_TIP_DIALOG = 1323;
+
+ // OPEN: Settings > Battery > Battery tip
+ // CATEGORY: SETTINGS
+ // OS: P
+ ACTION_BATTERY_TIP_SHOWN = 1324;
+
// ---- End P Constants, all P constants go above this line ----
// Add new aosp constants above this line.
// END OF AOSP CONSTANTS
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index c93f405012ca..62a7b8feb19e 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -66,6 +66,7 @@ import android.provider.Settings;
import android.system.Os;
import android.text.TextUtils;
import android.text.format.DateFormat;
+import android.text.format.DateUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.KeyValueListParser;
@@ -268,6 +269,7 @@ class AlarmManagerService extends SystemService {
// Key names stored in the settings value.
private static final String KEY_MIN_FUTURITY = "min_futurity";
private static final String KEY_MIN_INTERVAL = "min_interval";
+ private static final String KEY_MAX_INTERVAL = "max_interval";
private static final String KEY_ALLOW_WHILE_IDLE_SHORT_TIME = "allow_while_idle_short_time";
private static final String KEY_ALLOW_WHILE_IDLE_LONG_TIME = "allow_while_idle_long_time";
private static final String KEY_ALLOW_WHILE_IDLE_WHITELIST_DURATION
@@ -285,6 +287,7 @@ class AlarmManagerService extends SystemService {
private static final long DEFAULT_MIN_FUTURITY = 5 * 1000;
private static final long DEFAULT_MIN_INTERVAL = 60 * 1000;
+ private static final long DEFAULT_MAX_INTERVAL = 365 * DateUtils.DAY_IN_MILLIS;
private static final long DEFAULT_ALLOW_WHILE_IDLE_SHORT_TIME = DEFAULT_MIN_FUTURITY;
private static final long DEFAULT_ALLOW_WHILE_IDLE_LONG_TIME = 9*60*1000;
private static final long DEFAULT_ALLOW_WHILE_IDLE_WHITELIST_DURATION = 10*1000;
@@ -303,6 +306,9 @@ class AlarmManagerService extends SystemService {
// Minimum alarm recurrence interval
public long MIN_INTERVAL = DEFAULT_MIN_INTERVAL;
+ // Maximum alarm recurrence interval
+ public long MAX_INTERVAL = DEFAULT_MAX_INTERVAL;
+
// Minimum time between ALLOW_WHILE_IDLE alarms when system is not idle.
public long ALLOW_WHILE_IDLE_SHORT_TIME = DEFAULT_ALLOW_WHILE_IDLE_SHORT_TIME;
@@ -361,6 +367,7 @@ class AlarmManagerService extends SystemService {
MIN_FUTURITY = mParser.getLong(KEY_MIN_FUTURITY, DEFAULT_MIN_FUTURITY);
MIN_INTERVAL = mParser.getLong(KEY_MIN_INTERVAL, DEFAULT_MIN_INTERVAL);
+ MAX_INTERVAL = mParser.getLong(KEY_MAX_INTERVAL, DEFAULT_MAX_INTERVAL);
ALLOW_WHILE_IDLE_SHORT_TIME = mParser.getLong(KEY_ALLOW_WHILE_IDLE_SHORT_TIME,
DEFAULT_ALLOW_WHILE_IDLE_SHORT_TIME);
ALLOW_WHILE_IDLE_LONG_TIME = mParser.getLong(KEY_ALLOW_WHILE_IDLE_LONG_TIME,
@@ -391,6 +398,10 @@ class AlarmManagerService extends SystemService {
TimeUtils.formatDuration(MIN_INTERVAL, pw);
pw.println();
+ pw.print(" "); pw.print(KEY_MAX_INTERVAL); pw.print("=");
+ TimeUtils.formatDuration(MAX_INTERVAL, pw);
+ pw.println();
+
pw.print(" "); pw.print(KEY_LISTENER_TIMEOUT); pw.print("=");
TimeUtils.formatDuration(LISTENER_TIMEOUT, pw);
pw.println();
@@ -419,6 +430,7 @@ class AlarmManagerService extends SystemService {
proto.write(ConstantsProto.MIN_FUTURITY_DURATION_MS, MIN_FUTURITY);
proto.write(ConstantsProto.MIN_INTERVAL_DURATION_MS, MIN_INTERVAL);
+ proto.write(ConstantsProto.MAX_INTERVAL_DURATION_MS, MAX_INTERVAL);
proto.write(ConstantsProto.LISTENER_TIMEOUT_DURATION_MS, LISTENER_TIMEOUT);
proto.write(ConstantsProto.ALLOW_WHILE_IDLE_SHORT_DURATION_MS,
ALLOW_WHILE_IDLE_SHORT_TIME);
@@ -481,7 +493,7 @@ class AlarmManagerService extends SystemService {
Batch(Alarm seed) {
start = seed.whenElapsed;
- end = seed.maxWhenElapsed;
+ end = clampPositive(seed.maxWhenElapsed);
flags = seed.flags;
alarms.add(seed);
if (seed.operation == mTimeTickSender) {
@@ -737,7 +749,7 @@ class AlarmManagerService extends SystemService {
if (futurity < MIN_FUZZABLE_INTERVAL) {
futurity = 0;
}
- return triggerAtTime + (long)(.75 * futurity);
+ return clampPositive(triggerAtTime + (long)(.75 * futurity));
}
// returns true if the batch was added at the head
@@ -913,7 +925,7 @@ class AlarmManagerService extends SystemService {
// the window based on the alarm's new futurity. Note that this
// reflects a policy of preferring timely to deferred delivery.
maxElapsed = (a.windowLength > 0)
- ? (whenElapsed + a.windowLength)
+ ? clampPositive(whenElapsed + a.windowLength)
: maxTriggerTime(nowElapsed, whenElapsed, a.repeatInterval);
}
a.whenElapsed = whenElapsed;
@@ -921,6 +933,10 @@ class AlarmManagerService extends SystemService {
setImplLocked(a, true, doValidate);
}
+ static long clampPositive(long val) {
+ return (val >= 0) ? val : Long.MAX_VALUE;
+ }
+
/**
* Sends alarms that were blocked due to user applied background restrictions - either because
* the user lifted those or the uid came to foreground.
@@ -1421,13 +1437,18 @@ class AlarmManagerService extends SystemService {
}
// Sanity check the recurrence interval. This will catch people who supply
- // seconds when the API expects milliseconds.
+ // seconds when the API expects milliseconds, or apps trying shenanigans
+ // around intentional period overflow, etc.
final long minInterval = mConstants.MIN_INTERVAL;
if (interval > 0 && interval < minInterval) {
Slog.w(TAG, "Suspiciously short interval " + interval
+ " millis; expanding to " + (minInterval/1000)
+ " seconds");
interval = minInterval;
+ } else if (interval > mConstants.MAX_INTERVAL) {
+ Slog.w(TAG, "Suspiciously long interval " + interval
+ + " millis; clamping");
+ interval = mConstants.MAX_INTERVAL;
}
if (type < RTC_WAKEUP || type > ELAPSED_REALTIME) {
@@ -3175,8 +3196,7 @@ class AlarmManagerService extends SystemService {
whenElapsed = _whenElapsed;
expectedWhenElapsed = _whenElapsed;
windowLength = _windowLength;
- maxWhenElapsed = _maxWhen;
- expectedMaxWhenElapsed = _maxWhen;
+ maxWhenElapsed = expectedMaxWhenElapsed = clampPositive(_maxWhen);
repeatInterval = _interval;
operation = _op;
listener = _rec;
diff --git a/services/core/java/com/android/server/am/ActivityDisplay.java b/services/core/java/com/android/server/am/ActivityDisplay.java
index 56ed6c8a3f56..bac81e7cd4a2 100644
--- a/services/core/java/com/android/server/am/ActivityDisplay.java
+++ b/services/core/java/com/android/server/am/ActivityDisplay.java
@@ -158,6 +158,8 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack>
}
private void positionChildAt(ActivityStack stack, int position) {
+ // TODO: Keep in sync with WindowContainer.positionChildAt(), once we change that to adjust
+ // the position internally, also update the logic here
mStacks.remove(stack);
final int insertPosition = getTopInsertPosition(stack, position);
mStacks.add(insertPosition, stack);
@@ -750,7 +752,15 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack>
return;
}
- positionChildAt(mHomeStack, Math.max(0, mStacks.indexOf(behindStack) - 1));
+ // Note that positionChildAt will first remove the given stack before inserting into the
+ // list, so we need to adjust the insertion index to account for the removed index
+ // TODO: Remove this logic when WindowContainer.positionChildAt() is updated to adjust the
+ // position internally
+ final int homeStackIndex = mStacks.indexOf(mHomeStack);
+ final int behindStackIndex = mStacks.indexOf(behindStack);
+ final int insertIndex = homeStackIndex <= behindStackIndex
+ ? behindStackIndex - 1 : behindStackIndex;
+ positionChildAt(mHomeStack, Math.max(0, insertIndex));
}
boolean isSleeping() {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index da886677cfd4..e1c70f9e6c08 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -9410,6 +9410,25 @@ public class ActivityManagerService extends IActivityManager.Stub
allowed = false;
}
}
+ if (pi.pathPermissions != null) {
+ final int N = pi.pathPermissions.length;
+ for (int i=0; i<N; i++) {
+ if (pi.pathPermissions[i] != null
+ && pi.pathPermissions[i].match(grantUri.uri.getPath())) {
+ if ((modeFlags&Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0) {
+ if (pi.pathPermissions[i].getReadPermission() != null) {
+ allowed = false;
+ }
+ }
+ if ((modeFlags&Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0) {
+ if (pi.pathPermissions[i].getWritePermission() != null) {
+ allowed = false;
+ }
+ }
+ break;
+ }
+ }
+ }
if (allowed) {
return -1;
}
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 8cc927346500..274a4b068fa5 100644
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -1581,25 +1581,7 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo
void setState(ActivityState state, String reason) {
if (DEBUG_STATES) Slog.v(TAG_STATES, "State movement: " + this + " from:" + getState()
+ " to:" + state + " reason:" + reason);
- final boolean stateChanged = mState != state;
mState = state;
-
- if (stateChanged && isState(DESTROYING, DESTROYED)) {
- makeFinishingLocked();
-
- // When moving to the destroyed state, immediately destroy the activity in the
- // associated stack. Most paths for finishing an activity will handle an activity's path
- // to destroy through mechanisms such as ActivityStackSupervisor#mFinishingActivities.
- // However, moving to the destroyed state directly (as in the case of an app dying) and
- // marking it as finished will lead to cleanup steps that will prevent later handling
- // from happening.
- if (isState(DESTROYED)) {
- final ActivityStack stack = getStack();
- if (stack != null) {
- stack.activityDestroyedLocked(this, reason);
- }
- }
- }
}
ActivityState getState() {
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 8afa54008baa..ca228201f15d 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -1538,17 +1538,6 @@ public class AudioService extends IAudioService.Stub
if (adjustVolume && (direction != AudioManager.ADJUST_SAME)) {
mAudioHandler.removeMessages(MSG_UNMUTE_STREAM);
- // Check if volume update should be send to AVRCP
- if (streamTypeAlias == AudioSystem.STREAM_MUSIC &&
- (device & AudioSystem.DEVICE_OUT_ALL_A2DP) != 0 &&
- (flags & AudioManager.FLAG_BLUETOOTH_ABS_VOLUME) == 0) {
- synchronized (mA2dpAvrcpLock) {
- if (mA2dp != null && mAvrcpAbsVolSupported) {
- mA2dp.adjustAvrcpAbsoluteVolume(direction);
- }
- }
- }
-
if (isMuteAdjust) {
boolean state;
if (direction == AudioManager.ADJUST_TOGGLE_MUTE) {
@@ -1597,8 +1586,20 @@ public class AudioService extends IAudioService.Stub
0);
}
- // Check if volume update should be sent to Hdmi system audio.
int newIndex = mStreamStates[streamType].getIndex(device);
+
+ // Check if volume update should be send to AVRCP
+ if (streamTypeAlias == AudioSystem.STREAM_MUSIC &&
+ (device & AudioSystem.DEVICE_OUT_ALL_A2DP) != 0 &&
+ (flags & AudioManager.FLAG_BLUETOOTH_ABS_VOLUME) == 0) {
+ synchronized (mA2dpAvrcpLock) {
+ if (mA2dp != null && mAvrcpAbsVolSupported) {
+ mA2dp.setAvrcpAbsoluteVolume(newIndex / 10);
+ }
+ }
+ }
+
+ // Check if volume update should be sent to Hdmi system audio.
if (streamTypeAlias == AudioSystem.STREAM_MUSIC) {
setSystemAudioVolume(oldIndex, newIndex, getStreamMaxVolume(streamType), flags);
}
diff --git a/services/core/java/com/android/server/camera/CameraServiceProxy.java b/services/core/java/com/android/server/camera/CameraServiceProxy.java
index 3133a51a5fb5..ca8823f61ee2 100644
--- a/services/core/java/com/android/server/camera/CameraServiceProxy.java
+++ b/services/core/java/com/android/server/camera/CameraServiceProxy.java
@@ -103,13 +103,15 @@ public class CameraServiceProxy extends SystemService
private static class CameraUsageEvent {
public final int mCameraFacing;
public final String mClientName;
+ public final int mAPILevel;
private boolean mCompleted;
private long mDurationOrStartTimeMs; // Either start time, or duration once completed
- public CameraUsageEvent(int facing, String clientName) {
+ public CameraUsageEvent(int facing, String clientName, int apiLevel) {
mCameraFacing = facing;
mClientName = clientName;
+ mAPILevel = apiLevel;
mDurationOrStartTimeMs = SystemClock.elapsedRealtime();
mCompleted = false;
}
@@ -168,13 +170,13 @@ public class CameraServiceProxy extends SystemService
@Override
public void notifyCameraState(String cameraId, int newCameraState, int facing,
- String clientName) {
+ String clientName, int apiLevel) {
String state = cameraStateToString(newCameraState);
String facingStr = cameraFacingToString(facing);
if (DEBUG) Slog.v(TAG, "Camera " + cameraId + " facing " + facingStr + " state now " +
- state + " for client " + clientName);
+ state + " for client " + clientName + " API Level " + apiLevel);
- updateActivityCount(cameraId, newCameraState, facing, clientName);
+ updateActivityCount(cameraId, newCameraState, facing, clientName, apiLevel);
}
};
@@ -293,6 +295,7 @@ public class CameraServiceProxy extends SystemService
.setType(MetricsEvent.TYPE_ACTION)
.setSubtype(subtype)
.setLatency(e.getDuration())
+ .addTaggedData(MetricsEvent.FIELD_CAMERA_API_LEVEL, e.mAPILevel)
.setPackageName(e.mClientName);
mLogger.write(l);
}
@@ -368,7 +371,8 @@ public class CameraServiceProxy extends SystemService
return true;
}
- private void updateActivityCount(String cameraId, int newCameraState, int facing, String clientName) {
+ private void updateActivityCount(String cameraId, int newCameraState, int facing,
+ String clientName, int apiLevel) {
synchronized(mLock) {
// Update active camera list and notify NFC if necessary
boolean wasEmpty = mActiveCameraUsage.isEmpty();
@@ -376,7 +380,7 @@ public class CameraServiceProxy extends SystemService
case ICameraServiceProxy.CAMERA_STATE_OPEN:
break;
case ICameraServiceProxy.CAMERA_STATE_ACTIVE:
- CameraUsageEvent newEvent = new CameraUsageEvent(facing, clientName);
+ CameraUsageEvent newEvent = new CameraUsageEvent(facing, clientName, apiLevel);
CameraUsageEvent oldEvent = mActiveCameraUsage.put(cameraId, newEvent);
if (oldEvent != null) {
Slog.w(TAG, "Camera " + cameraId + " was already marked as active");
diff --git a/services/core/java/com/android/server/clipboard/ClipboardService.java b/services/core/java/com/android/server/clipboard/ClipboardService.java
index 0c9d70a95ab9..776e93dd053f 100644
--- a/services/core/java/com/android/server/clipboard/ClipboardService.java
+++ b/services/core/java/com/android/server/clipboard/ClipboardService.java
@@ -16,6 +16,7 @@
package com.android.server.clipboard;
+import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.AppGlobals;
import android.app.AppOpsManager;
@@ -24,9 +25,9 @@ import android.app.KeyguardManager;
import android.content.ClipData;
import android.content.ClipDescription;
import android.content.ContentProvider;
+import android.content.Context;
import android.content.IClipboard;
import android.content.IOnPrimaryClipChangedListener;
-import android.content.Context;
import android.content.Intent;
import android.content.pm.IPackageManager;
import android.content.pm.PackageInfo;
@@ -37,7 +38,6 @@ import android.os.Binder;
import android.os.IBinder;
import android.os.IUserManager;
import android.os.Parcel;
-import android.os.Process;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -49,14 +49,10 @@ import android.util.SparseArray;
import com.android.server.SystemService;
-import java.util.HashSet;
-import java.util.List;
-
-import java.lang.Thread;
-import java.lang.Runnable;
-import java.lang.InterruptedException;
import java.io.IOException;
import java.io.RandomAccessFile;
+import java.util.HashSet;
+import java.util.List;
// The following class is Android Emulator specific. It is used to read and
// write contents of the host system's clipboard.
@@ -182,7 +178,8 @@ public class ClipboardService extends SystemService {
new String[]{"text/plain"},
new ClipData.Item(contents));
synchronized(mClipboards) {
- setPrimaryClipInternal(getClipboard(0), clip);
+ setPrimaryClipInternal(getClipboard(0), clip,
+ android.os.Process.SYSTEM_UID);
}
}
});
@@ -218,7 +215,10 @@ public class ClipboardService extends SystemService {
final RemoteCallbackList<IOnPrimaryClipChangedListener> primaryClipListeners
= new RemoteCallbackList<IOnPrimaryClipChangedListener>();
+ /** Current primary clip. */
ClipData primaryClip;
+ /** UID that set {@link #primaryClip}. */
+ int primaryClipUid = android.os.Process.NOBODY_UID;
final HashSet<String> activePermissionOwners
= new HashSet<String>();
@@ -246,58 +246,28 @@ public class ClipboardService extends SystemService {
@Override
public void setPrimaryClip(ClipData clip, String callingPackage) {
synchronized (this) {
- if (clip != null && clip.getItemCount() <= 0) {
+ if (clip == null || clip.getItemCount() <= 0) {
throw new IllegalArgumentException("No items");
}
- if (clip.getItemAt(0).getText() != null &&
- mHostClipboardMonitor != null) {
- mHostClipboardMonitor.setHostClipboard(
- clip.getItemAt(0).getText().toString());
- }
final int callingUid = Binder.getCallingUid();
if (!clipboardAccessAllowed(AppOpsManager.OP_WRITE_CLIPBOARD, callingPackage,
callingUid)) {
return;
}
checkDataOwnerLocked(clip, callingUid);
- final int userId = UserHandle.getUserId(callingUid);
- PerUserClipboard clipboard = getClipboard(userId);
- revokeUris(clipboard);
- setPrimaryClipInternal(clipboard, clip);
- List<UserInfo> related = getRelatedProfiles(userId);
- if (related != null) {
- int size = related.size();
- if (size > 1) { // Related profiles list include the current profile.
- boolean canCopy = false;
- try {
- canCopy = !mUm.getUserRestrictions(userId).getBoolean(
- UserManager.DISALLOW_CROSS_PROFILE_COPY_PASTE);
- } catch (RemoteException e) {
- Slog.e(TAG, "Remote Exception calling UserManager: " + e);
- }
- // Copy clip data to related users if allowed. If disallowed, then remove
- // primary clip in related users to prevent pasting stale content.
- if (!canCopy) {
- clip = null;
- } else {
- // We want to fix the uris of the related user's clip without changing the
- // uris of the current user's clip.
- // So, copy the ClipData, and then copy all the items, so that nothing
- // is shared in memmory.
- clip = new ClipData(clip);
- for (int i = clip.getItemCount() - 1; i >= 0; i--) {
- clip.setItemAt(i, new ClipData.Item(clip.getItemAt(i)));
- }
- clip.fixUrisLight(userId);
- }
- for (int i = 0; i < size; i++) {
- int id = related.get(i).id;
- if (id != userId) {
- setPrimaryClipInternal(getClipboard(id), clip);
- }
- }
- }
+ setPrimaryClipInternal(clip, callingUid);
+ }
+ }
+
+ @Override
+ public void clearPrimaryClip(String callingPackage) {
+ synchronized (this) {
+ final int callingUid = Binder.getCallingUid();
+ if (!clipboardAccessAllowed(AppOpsManager.OP_WRITE_CLIPBOARD, callingPackage,
+ callingUid)) {
+ return;
}
+ setPrimaryClipInternal(null, callingUid);
}
}
@@ -398,13 +368,75 @@ public class ClipboardService extends SystemService {
return related;
}
- void setPrimaryClipInternal(PerUserClipboard clipboard, ClipData clip) {
+ void setPrimaryClipInternal(@Nullable ClipData clip, int callingUid) {
+ // Push clipboard to host, if any
+ if (mHostClipboardMonitor != null) {
+ if (clip == null) {
+ // Someone really wants the clipboard cleared, so push empty
+ mHostClipboardMonitor.setHostClipboard("");
+ } else if (clip.getItemCount() > 0) {
+ final CharSequence text = clip.getItemAt(0).getText();
+ if (text != null) {
+ mHostClipboardMonitor.setHostClipboard(text.toString());
+ }
+ }
+ }
+
+ // Update this user
+ final int userId = UserHandle.getUserId(callingUid);
+ setPrimaryClipInternal(getClipboard(userId), clip, callingUid);
+
+ // Update related users
+ List<UserInfo> related = getRelatedProfiles(userId);
+ if (related != null) {
+ int size = related.size();
+ if (size > 1) { // Related profiles list include the current profile.
+ boolean canCopy = false;
+ try {
+ canCopy = !mUm.getUserRestrictions(userId).getBoolean(
+ UserManager.DISALLOW_CROSS_PROFILE_COPY_PASTE);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote Exception calling UserManager: " + e);
+ }
+ // Copy clip data to related users if allowed. If disallowed, then remove
+ // primary clip in related users to prevent pasting stale content.
+ if (!canCopy) {
+ clip = null;
+ } else {
+ // We want to fix the uris of the related user's clip without changing the
+ // uris of the current user's clip.
+ // So, copy the ClipData, and then copy all the items, so that nothing
+ // is shared in memmory.
+ clip = new ClipData(clip);
+ for (int i = clip.getItemCount() - 1; i >= 0; i--) {
+ clip.setItemAt(i, new ClipData.Item(clip.getItemAt(i)));
+ }
+ clip.fixUrisLight(userId);
+ }
+ for (int i = 0; i < size; i++) {
+ int id = related.get(i).id;
+ if (id != userId) {
+ setPrimaryClipInternal(getClipboard(id), clip, callingUid);
+ }
+ }
+ }
+ }
+ }
+
+ void setPrimaryClipInternal(PerUserClipboard clipboard, @Nullable ClipData clip,
+ int callingUid) {
+ revokeUris(clipboard);
clipboard.activePermissionOwners.clear();
if (clip == null && clipboard.primaryClip == null) {
return;
}
clipboard.primaryClip = clip;
if (clip != null) {
+ clipboard.primaryClipUid = callingUid;
+ } else {
+ clipboard.primaryClipUid = android.os.Process.NOBODY_UID;
+ }
+ if (clip != null) {
final ClipDescription description = clip.getDescription();
if (description != null) {
description.setTimestamp(System.currentTimeMillis());
@@ -479,12 +511,12 @@ public class ClipboardService extends SystemService {
}
}
- private final void grantUriLocked(Uri uri, String pkg, int userId) {
+ private final void grantUriLocked(Uri uri, int primaryClipUid, String pkg, int userId) {
long ident = Binder.clearCallingIdentity();
try {
int sourceUserId = ContentProvider.getUserIdFromUri(uri, userId);
uri = ContentProvider.getUriWithoutUserId(uri);
- mAm.grantUriPermissionFromOwner(mPermissionOwner, Process.myUid(), pkg,
+ mAm.grantUriPermissionFromOwner(mPermissionOwner, primaryClipUid, pkg,
uri, Intent.FLAG_GRANT_READ_URI_PERMISSION, sourceUserId, userId);
} catch (RemoteException e) {
} finally {
@@ -492,13 +524,14 @@ public class ClipboardService extends SystemService {
}
}
- private final void grantItemLocked(ClipData.Item item, String pkg, int userId) {
+ private final void grantItemLocked(ClipData.Item item, int primaryClipUid, String pkg,
+ int userId) {
if (item.getUri() != null) {
- grantUriLocked(item.getUri(), pkg, userId);
+ grantUriLocked(item.getUri(), primaryClipUid, pkg, userId);
}
Intent intent = item.getIntent();
if (intent != null && intent.getData() != null) {
- grantUriLocked(intent.getData(), pkg, userId);
+ grantUriLocked(intent.getData(), primaryClipUid, pkg, userId);
}
}
@@ -524,7 +557,8 @@ public class ClipboardService extends SystemService {
if (clipboard.primaryClip != null && !clipboard.activePermissionOwners.contains(pkg)) {
final int N = clipboard.primaryClip.getItemCount();
for (int i=0; i<N; i++) {
- grantItemLocked(clipboard.primaryClip.getItemAt(i), pkg, UserHandle.getUserId(uid));
+ grantItemLocked(clipboard.primaryClip.getItemAt(i), clipboard.primaryClipUid, pkg,
+ UserHandle.getUserId(uid));
}
clipboard.activePermissionOwners.add(pkg);
}
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index 9e00819d4eee..752ab8f4128d 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -2079,6 +2079,11 @@ public class LockSettingsService extends ILockSettings.Stub {
}
@Override
+ public String importKey(@NonNull String alias, byte[] keyBytes) throws RemoteException {
+ return mRecoverableKeyStoreManager.importKey(alias, keyBytes);
+ }
+
+ @Override
public String getKey(@NonNull String alias) throws RemoteException {
return mRecoverableKeyStoreManager.getKey(alias);
}
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyGenerator.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyGenerator.java
index 2fe3f4e943b3..7ebe8bf20d62 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyGenerator.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyGenerator.java
@@ -16,6 +16,8 @@
package com.android.server.locksettings.recoverablekeystore;
+import android.annotation.NonNull;
+
import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDb;
import java.security.InvalidKeyException;
@@ -25,20 +27,24 @@ import java.util.Locale;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
+import javax.crypto.spec.SecretKeySpec;
+// TODO: Rename RecoverableKeyGenerator to RecoverableKeyManager as it can import a key too now
/**
- * Generates keys and stores them both in AndroidKeyStore and on disk, in wrapped form.
+ * Generates/imports keys and stores them both in AndroidKeyStore and on disk, in wrapped form.
*
- * <p>Generates 256-bit AES keys, which can be used for encrypt / decrypt with AES/GCM/NoPadding.
+ * <p>Generates/imports 256-bit AES keys, which can be used for encrypt and decrypt with AES-GCM.
* They are synced to disk wrapped by a platform key. This allows them to be exported to a remote
* service.
*
* @hide
*/
public class RecoverableKeyGenerator {
+
private static final int RESULT_CANNOT_INSERT_ROW = -1;
- private static final String KEY_GENERATOR_ALGORITHM = "AES";
- private static final int KEY_SIZE_BITS = 256;
+ private static final String SECRET_KEY_ALGORITHM = "AES";
+
+ static final int KEY_SIZE_BITS = 256;
/**
* A new {@link RecoverableKeyGenerator} instance.
@@ -52,7 +58,7 @@ public class RecoverableKeyGenerator {
throws NoSuchAlgorithmException {
// NB: This cannot use AndroidKeyStore as the provider, as we need access to the raw key
// material, so that it can be synced to disk in encrypted form.
- KeyGenerator keyGenerator = KeyGenerator.getInstance(KEY_GENERATOR_ALGORITHM);
+ KeyGenerator keyGenerator = KeyGenerator.getInstance(SECRET_KEY_ALGORITHM);
return new RecoverableKeyGenerator(keyGenerator, database);
}
@@ -102,4 +108,41 @@ public class RecoverableKeyGenerator {
mDatabase.setShouldCreateSnapshot(userId, uid, true);
return key.getEncoded();
}
+
+ /**
+ * Imports an AES key with the given alias.
+ *
+ * <p>Stores in the AndroidKeyStore, as well as persisting in wrapped form to disk. It is
+ * persisted to disk so that it can be synced remotely, and then recovered on another device.
+ * The generated key allows encrypt/decrypt only using AES/GCM/NoPadding.
+ *
+ * @param platformKey The user's platform key, with which to wrap the generated key.
+ * @param userId The user ID of the profile to which the calling app belongs.
+ * @param uid The uid of the application that will own the key.
+ * @param alias The alias by which the key will be known in the recoverable key store.
+ * @param keyBytes The raw bytes of the AES key to be imported.
+ * @throws RecoverableKeyStorageException if there is some error persisting the key either to
+ * the database.
+ * @throws KeyStoreException if there is a KeyStore error wrapping the generated key.
+ * @throws InvalidKeyException if the platform key cannot be used to wrap keys.
+ *
+ * @hide
+ */
+ public void importKey(
+ @NonNull PlatformEncryptionKey platformKey, int userId, int uid, @NonNull String alias,
+ @NonNull byte[] keyBytes)
+ throws RecoverableKeyStorageException, KeyStoreException, InvalidKeyException {
+ SecretKey key = new SecretKeySpec(keyBytes, SECRET_KEY_ALGORITHM);
+
+ WrappedKey wrappedKey = WrappedKey.fromSecretKey(platformKey, key);
+ long result = mDatabase.insertKey(userId, uid, alias, wrappedKey);
+
+ if (result == RESULT_CANNOT_INSERT_ROW) {
+ throw new RecoverableKeyStorageException(
+ String.format(
+ Locale.US, "Failed writing (%d, %s) to database.", uid, alias));
+ }
+
+ mDatabase.setShouldCreateSnapshot(userId, uid, true);
+ }
}
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
index 72f72eb82b93..da0b0d03b54d 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
@@ -19,6 +19,7 @@ package com.android.server.locksettings.recoverablekeystore;
import static android.security.keystore.recovery.RecoveryController.ERROR_BAD_CERTIFICATE_FORMAT;
import static android.security.keystore.recovery.RecoveryController.ERROR_DECRYPTION_FAILED;
import static android.security.keystore.recovery.RecoveryController.ERROR_INSECURE_USER;
+import static android.security.keystore.recovery.RecoveryController.ERROR_INVALID_KEY_FORMAT;
import static android.security.keystore.recovery.RecoveryController.ERROR_NO_SNAPSHOT_PENDING;
import static android.security.keystore.recovery.RecoveryController.ERROR_SERVICE_INTERNAL_ERROR;
import static android.security.keystore.recovery.RecoveryController.ERROR_SESSION_EXPIRED;
@@ -505,6 +506,7 @@ public class RecoverableKeyStoreManager {
*
* <p>TODO: Once AndroidKeyStore has added move api, do not return raw bytes.
*
+ * @deprecated
* @hide
*/
public byte[] generateAndStoreKey(@NonNull String alias) throws RemoteException {
@@ -581,6 +583,57 @@ public class RecoverableKeyStoreManager {
}
/**
+ * Imports a 256-bit AES-GCM key named {@code alias}. The key is stored in system service
+ * keystore namespace.
+ *
+ * @param alias the alias provided by caller as a reference to the key.
+ * @param keyBytes the raw bytes of the 256-bit AES key.
+ * @return grant alias, which caller can use to access the key.
+ * @throws RemoteException if the given key is invalid or some internal errors occur.
+ *
+ * @hide
+ */
+ public String importKey(@NonNull String alias, @NonNull byte[] keyBytes)
+ throws RemoteException {
+ if (keyBytes == null ||
+ keyBytes.length != RecoverableKeyGenerator.KEY_SIZE_BITS / Byte.SIZE) {
+ Log.e(TAG, "The given key for import doesn't have the required length "
+ + RecoverableKeyGenerator.KEY_SIZE_BITS);
+ throw new ServiceSpecificException(ERROR_INVALID_KEY_FORMAT,
+ "The given key does not contain " + RecoverableKeyGenerator.KEY_SIZE_BITS
+ + " bits.");
+ }
+
+ int uid = Binder.getCallingUid();
+ int userId = UserHandle.getCallingUserId();
+
+ // TODO: Refactor RecoverableKeyGenerator to wrap the PlatformKey logic
+
+ PlatformEncryptionKey encryptionKey;
+ try {
+ encryptionKey = mPlatformKeyManager.getEncryptKey(userId);
+ } catch (NoSuchAlgorithmException e) {
+ // Impossible: all algorithms must be supported by AOSP
+ throw new RuntimeException(e);
+ } catch (KeyStoreException | UnrecoverableKeyException e) {
+ throw new ServiceSpecificException(ERROR_SERVICE_INTERNAL_ERROR, e.getMessage());
+ } catch (InsecureUserException e) {
+ throw new ServiceSpecificException(ERROR_INSECURE_USER, e.getMessage());
+ }
+
+ try {
+ // Wrap the key by the platform key and store the wrapped key locally
+ mRecoverableKeyGenerator.importKey(encryptionKey, userId, uid, alias, keyBytes);
+
+ // Import the key to Android KeyStore and get grant
+ mApplicationKeyStorage.setSymmetricKeyEntry(userId, uid, alias, keyBytes);
+ return mApplicationKeyStorage.getGrantAlias(userId, uid, alias);
+ } catch (KeyStoreException | InvalidKeyException | RecoverableKeyStorageException e) {
+ throw new ServiceSpecificException(ERROR_SERVICE_INTERNAL_ERROR, e.getMessage());
+ }
+ }
+
+ /**
* Gets a key named {@code alias} in caller's namespace.
*
* @return grant alias, which caller can use to access the key.
@@ -630,14 +683,6 @@ public class RecoverableKeyStoreManager {
}
}
- private String constructLoggingMessage(String key, byte[] value) {
- if (value == null) {
- return key + " is null";
- } else {
- return key + ": " + HexDump.toHexString(value);
- }
- }
-
/**
* Uses {@code recoveryKey} to decrypt {@code applicationKeys}.
*
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbContract.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbContract.java
index 1cb5d91be3ba..8983ec369f55 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbContract.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbContract.java
@@ -70,6 +70,122 @@ class RecoverableKeyStoreDbContract {
}
/**
+ * Table holding encrypted snapshots of the recoverable key store.
+ */
+ static class SnapshotsEntry implements BaseColumns {
+ static final String TABLE_NAME = "snapshots";
+
+ /**
+ * The version number of the snapshot.
+ */
+ static final String COLUMN_NAME_VERSION = "version";
+
+ /**
+ * The ID of the user whose keystore was snapshotted.
+ */
+ static final String COLUMN_NAME_USER_ID = "user_id";
+
+ /**
+ * The UID of the app that owns the snapshot (i.e., the recovery agent).
+ */
+ static final String COLUMN_NAME_UID = "uid";
+
+ /**
+ * The maximum number of attempts allowed to attempt to decrypt the recovery key.
+ */
+ static final String COLUMN_NAME_MAX_ATTEMPTS = "max_attempts";
+
+ /**
+ * The ID of the counter in the trusted hardware module.
+ */
+ static final String COLUMN_NAME_COUNTER_ID = "counter_id";
+
+ /**
+ * Server parameters used to help identify the device (during recovery).
+ */
+ static final String SERVER_PARAMS = "server_params";
+
+ /**
+ * The public key of the trusted hardware module. This key has been used to encrypt the
+ * snapshot, to ensure that it can only be read by the trusted module.
+ */
+ static final String TRUSTED_HARDWARE_PUBLIC_KEY = "thm_public_key";
+
+ /**
+ * {@link java.security.cert.CertPath} signing the trusted hardware module to whose public
+ * key this snapshot is encrypted.
+ */
+ static final String CERT_PATH = "cert_path";
+
+ /**
+ * The recovery key, encrypted with the user's lock screen and the trusted hardware module's
+ * public key.
+ */
+ static final String ENCRYPTED_RECOVERY_KEY = "encrypted_recovery_key";
+ }
+
+ /**
+ * Table holding encrypted keys belonging to a particular snapshot.
+ */
+ static class SnapshotKeysEntry implements BaseColumns {
+ static final String TABLE_NAME = "snapshot_keys";
+
+ /**
+ * ID of the associated snapshot entry in {@link SnapshotsEntry}.
+ */
+ static final String COLUMN_NAME_SNAPSHOT_ID = "snapshot_id";
+
+ /**
+ * Alias of the key.
+ */
+ static final String COLUMN_NAME_ALIAS = "alias";
+
+ /**
+ * Key material, encrypted with the recovery key from the snapshot.
+ */
+ static final String COLUMN_NAME_ENCRYPTED_BYTES = "encrypted_key_bytes";
+ }
+
+ /**
+ * A layer of protection associated with a snapshot.
+ */
+ static class SnapshotProtectionParams implements BaseColumns {
+ static final String TABLE_NAME = "snapshot_protection_params";
+
+ /**
+ * ID of the associated snapshot entry in {@link SnapshotsEntry}.
+ */
+ static final String COLUMN_NAME_SNAPSHOT_ID = "snapshot_id";
+
+ /**
+ * Type of secret used to generate recovery key. One of
+ * {@link android.security.keystore.recovery.KeyChainProtectionParams#TYPE_LOCKSCREEN} or
+ * {@link android.security.keystore.recovery.KeyChainProtectionParams#TYPE_CUSTOM_PASSWORD}.
+ */
+ static final String COLUMN_NAME_SECRET_TYPE = "secret_type";
+
+ /**
+ * If a lock screen, the type of UI used. One of
+ * {@link android.security.keystore.recovery.KeyChainProtectionParams#UI_FORMAT_PATTERN},
+ * {@link android.security.keystore.recovery.KeyChainProtectionParams#UI_FORMAT_PIN}, or
+ * {@link android.security.keystore.recovery.KeyChainProtectionParams#UI_FORMAT_PASSWORD}.
+ */
+ static final String COLUMN_NAME_LOCKSCREEN_UI_TYPE = "lock_screen_ui_type";
+
+ /**
+ * The algorithm used to derive cryptographic material from the key and salt. One of
+ * {@link android.security.keystore.recovery.KeyDerivationParams#ALGORITHM_SHA256} or
+ * {@link android.security.keystore.recovery.KeyDerivationParams#ALGORITHM_ARGON2ID}.
+ */
+ static final String COLUMN_NAME_KEY_DERIVATION_ALGORITHM = "key_derivation_algorithm";
+
+ /**
+ * The salt used along with the secret to generate cryptographic material.
+ */
+ static final String COLUMN_NAME_KEY_DERIVATION_SALT = "key_derivation_salt";
+ }
+
+ /**
* Recoverable KeyStore metadata for a specific user profile.
*/
static class UserMetadataEntry implements BaseColumns {
diff --git a/services/core/java/com/android/server/notification/ValidateNotificationPeople.java b/services/core/java/com/android/server/notification/ValidateNotificationPeople.java
index 896480ffb560..c0c66b248ea5 100644
--- a/services/core/java/com/android/server/notification/ValidateNotificationPeople.java
+++ b/services/core/java/com/android/server/notification/ValidateNotificationPeople.java
@@ -16,6 +16,7 @@
package com.android.server.notification;
+import android.annotation.Nullable;
import android.app.Notification;
import android.content.Context;
import android.content.pm.PackageManager;
@@ -45,8 +46,6 @@ import java.util.Set;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
-import android.os.SystemClock;
-
/**
* This {@link NotificationSignalExtractor} attempts to validate
* people references. Also elevates the priority of real people.
@@ -231,7 +230,6 @@ public class ValidateNotificationPeople implements NotificationSignalExtractor {
private PeopleRankingReconsideration validatePeople(Context context, String key, Bundle extras,
List<String> peopleOverride, float[] affinityOut) {
- long start = SystemClock.elapsedRealtime();
float affinity = NONE;
if (extras == null) {
return null;
@@ -239,7 +237,7 @@ public class ValidateNotificationPeople implements NotificationSignalExtractor {
final Set<String> people = new ArraySet<>(peopleOverride);
final String[] notificationPeople = getExtraPeople(extras);
if (notificationPeople != null ) {
- people.addAll(Arrays.asList(getExtraPeople(extras)));
+ people.addAll(Arrays.asList(notificationPeople));
}
if (VERBOSE) Slog.i(TAG, "Validating: " + key + " for " + context.getUserId());
@@ -283,7 +281,31 @@ public class ValidateNotificationPeople implements NotificationSignalExtractor {
// VisibleForTesting
public static String[] getExtraPeople(Bundle extras) {
- Object people = extras.get(Notification.EXTRA_PEOPLE_LIST);
+ String[] peopleList = getExtraPeopleForKey(extras, Notification.EXTRA_PEOPLE_LIST);
+ String[] legacyPeople = getExtraPeopleForKey(extras, Notification.EXTRA_PEOPLE);
+ return combineLists(legacyPeople, peopleList);
+ }
+
+ private static String[] combineLists(String[] first, String[] second) {
+ if (first == null) {
+ return second;
+ }
+ if (second == null) {
+ return first;
+ }
+ ArraySet<String> people = new ArraySet<>(first.length + second.length);
+ for (String person: first) {
+ people.add(person);
+ }
+ for (String person: second) {
+ people.add(person);
+ }
+ return (String[]) people.toArray();
+ }
+
+ @Nullable
+ private static String[] getExtraPeopleForKey(Bundle extras, String key) {
+ Object people = extras.get(key);
if (people instanceof String[]) {
return (String[]) people;
}
@@ -458,7 +480,6 @@ public class ValidateNotificationPeople implements NotificationSignalExtractor {
@Override
public void work() {
- long start = SystemClock.elapsedRealtime();
if (VERBOSE) Slog.i(TAG, "Executing: validation for: " + mKey);
long timeStartMs = System.currentTimeMillis();
for (final String handle: mPendingLookups) {
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index 95c30d10341f..6e017cd0a3d1 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -100,6 +100,7 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
private final PendingIntent mAnomalyAlarmIntent;
private final PendingIntent mPullingAlarmIntent;
+ private final PendingIntent mPeriodicAlarmIntent;
private final BroadcastReceiver mAppUpdateReceiver;
private final BroadcastReceiver mUserUpdateReceiver;
private final ShutdownEventReceiver mShutdownEventReceiver;
@@ -123,6 +124,8 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
new Intent(mContext, AnomalyAlarmReceiver.class), 0);
mPullingAlarmIntent = PendingIntent.getBroadcast(
mContext, 0, new Intent(mContext, PullingAlarmReceiver.class), 0);
+ mPeriodicAlarmIntent = PendingIntent.getBroadcast(
+ mContext, 0, new Intent(mContext, PeriodicAlarmReceiver.class), 0);
mAppUpdateReceiver = new AppUpdateReceiver();
mUserUpdateReceiver = new BroadcastReceiver() {
@Override
@@ -329,7 +332,28 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
}
}
- private final static class ShutdownEventReceiver extends BroadcastReceiver {
+ public final static class PeriodicAlarmReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (DEBUG)
+ Slog.d(TAG, "Time to poll something.");
+ synchronized (sStatsdLock) {
+ if (sStatsd == null) {
+ Slog.w(TAG, "Could not access statsd to inform it of periodic alarm firing.");
+ return;
+ }
+ try {
+ // Two-way call to statsd to retain AlarmManager wakelock
+ sStatsd.informAlarmForSubscriberTriggeringFired();
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to inform statsd of periodic alarm firing.", e);
+ }
+ }
+ // AlarmManager releases its own wakelock here.
+ }
+ }
+
+ public final static class ShutdownEventReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
/**
@@ -385,6 +409,35 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
}
@Override // Binder call
+ public void setAlarmForSubscriberTriggering(long timestampMs) {
+ enforceCallingPermission();
+ if (DEBUG)
+ Slog.d(TAG, "Setting periodic alarm at " + timestampMs);
+ final long callingToken = Binder.clearCallingIdentity();
+ try {
+ // using ELAPSED_REALTIME, not ELAPSED_REALTIME_WAKEUP, so if device is asleep, will
+ // only fire when it awakens.
+ // This alarm is inexact, leaving its exactness completely up to the OS optimizations.
+ mAlarmManager.set(AlarmManager.ELAPSED_REALTIME, timestampMs, mPeriodicAlarmIntent);
+ } finally {
+ Binder.restoreCallingIdentity(callingToken);
+ }
+ }
+
+ @Override // Binder call
+ public void cancelAlarmForSubscriberTriggering() {
+ enforceCallingPermission();
+ if (DEBUG)
+ Slog.d(TAG, "Cancelling periodic alarm");
+ final long callingToken = Binder.clearCallingIdentity();
+ try {
+ mAlarmManager.cancel(mPeriodicAlarmIntent);
+ } finally {
+ Binder.restoreCallingIdentity(callingToken);
+ }
+ }
+
+ @Override // Binder call
public void setPullingAlarms(long timestampMs, long intervalMs) {
enforceCallingPermission();
if (DEBUG)
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 277a04b6b201..84c885e4fba7 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -1620,7 +1620,15 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
@Override
public SurfaceControl getAnimationLeashParent() {
- return getAppAnimationLayer();
+ // All normal app transitions take place in an animation layer which is below the pinned
+ // stack but may be above the parent stacks of the given animating apps.
+ // For transitions in the pinned stack (menu activity) we just let them occur as a child
+ // of the pinned stack.
+ if (!inPinnedWindowingMode()) {
+ return getAppAnimationLayer();
+ } else {
+ return getStack().getSurfaceControl();
+ }
}
boolean applyAnimationLocked(WindowManager.LayoutParams lp, int transit, boolean enter,
@@ -1763,10 +1771,18 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
@Override
public void onAnimationLeashCreated(Transaction t, SurfaceControl leash) {
-
// The leash is parented to the animation layer. We need to preserve the z-order by using
// the prefix order index, but we boost if necessary.
- int layer = getPrefixOrderIndex();
+ int layer = 0;
+ if (!inPinnedWindowingMode()) {
+ layer = getPrefixOrderIndex();
+ } else {
+ // Pinned stacks have animations take place within themselves rather than an animation
+ // layer so we need to preserve the order relative to the stack (e.g. the order of our
+ // task/parent).
+ layer = getParent().getPrefixOrderIndex();
+ }
+
if (mNeedsZBoost) {
layer += Z_BOOST_BASE;
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 75a633816f03..19c634a55d5a 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -3583,7 +3583,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
if (s.inSplitScreenWindowingMode() && mSplitScreenDividerAnchor != null) {
t.setLayer(mSplitScreenDividerAnchor, layer++);
}
- if (s.isSelfOrChildAnimating()) {
+ if (s.isAppAnimating() && state != ALWAYS_ON_TOP_STATE) {
// Ensure the animation layer ends up above the
// highest animating stack and no higher.
layerForAnimationLayer = layer++;
@@ -3632,6 +3632,11 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
super(name, service);
}
+ @Override
+ void assignChildLayers(SurfaceControl.Transaction t) {
+ assignChildLayers(t, null /* imeContainer */);
+ }
+
void assignChildLayers(SurfaceControl.Transaction t, WindowContainer imeContainer) {
boolean needAssignIme = imeContainer != null
&& imeContainer.getSurfaceControl() != null;
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 1f7caffd1916..42f606531cc2 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -406,6 +406,10 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
}
break;
default:
+ // TODO: Removing the child before reinserting requires the caller to provide a
+ // position that takes into account the removed child (if the index of the
+ // child < position, then the position should be adjusted). We should consider
+ // doing this adjustment here and remove any adjustments in the callers.
mChildren.remove(child);
mChildren.add(position, child);
}
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java
index bfc31337134e..fb1595e1eb49 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java
@@ -204,20 +204,4 @@ public class ActivityRecordTests extends ActivityTestsBase {
verify(mService.mStackSupervisor, times(1)).canPlaceEntityOnDisplay(anyInt(), eq(expected),
anyInt(), anyInt(), eq(record.info));
}
-
- @Test
- public void testFinishingAfterDestroying() throws Exception {
- assertFalse(mActivity.finishing);
- mActivity.setState(DESTROYING, "testFinishingAfterDestroying");
- assertTrue(mActivity.isState(DESTROYING));
- assertTrue(mActivity.finishing);
- }
-
- @Test
- public void testFinishingAfterDestroyed() throws Exception {
- assertFalse(mActivity.finishing);
- mActivity.setState(DESTROYED, "testFinishingAfterDestroyed");
- assertTrue(mActivity.isState(DESTROYED));
- assertTrue(mActivity.finishing);
- }
}
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
index ce3528b57d27..c62820e8e3c3 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
@@ -408,6 +408,10 @@ public class ActivityStackTests extends ActivityTestsBase {
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
final TestActivityStack fullscreenStack2 = createStackForShouldBeVisibleTest(display,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+ final TestActivityStack fullscreenStack3 = createStackForShouldBeVisibleTest(display,
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+ final TestActivityStack fullscreenStack4 = createStackForShouldBeVisibleTest(display,
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
final TestActivityStack homeStack = createStackForShouldBeVisibleTest(display,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
@@ -415,6 +419,10 @@ public class ActivityStackTests extends ActivityTestsBase {
assertTrue(display.getStackAboveHome() == fullscreenStack1);
display.moveHomeStackBehindStack(fullscreenStack2);
assertTrue(display.getStackAboveHome() == fullscreenStack2);
+ display.moveHomeStackBehindStack(fullscreenStack4);
+ assertTrue(display.getStackAboveHome() == fullscreenStack4);
+ display.moveHomeStackBehindStack(fullscreenStack2);
+ assertTrue(display.getStackAboveHome() == fullscreenStack2);
}
private <T extends ActivityStack> T createStackForShouldBeVisibleTest(
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyGeneratorTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyGeneratorTest.java
index 8a461ac508fa..fd8b319b74ca 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyGeneratorTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyGeneratorTest.java
@@ -39,6 +39,7 @@ import org.junit.runner.RunWith;
import java.io.File;
import java.nio.charset.StandardCharsets;
import java.security.KeyStore;
+import java.util.Random;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
@@ -51,7 +52,7 @@ public class RecoverableKeyGeneratorTest {
private static final int TEST_GENERATION_ID = 3;
private static final String ANDROID_KEY_STORE_PROVIDER = "AndroidKeyStore";
private static final String KEY_ALGORITHM = "AES";
- private static final int KEY_SIZE_BYTES = 32;
+ private static final int KEY_SIZE_BYTES = RecoverableKeyGenerator.KEY_SIZE_BITS / Byte.SIZE;
private static final String KEY_WRAP_ALGORITHM = "AES/GCM/NoPadding";
private static final String TEST_ALIAS = "karlin";
private static final String WRAPPING_KEY_ALIAS = "RecoverableKeyGeneratorTestWrappingKey";
@@ -71,7 +72,7 @@ public class RecoverableKeyGeneratorTest {
mDatabaseFile = context.getDatabasePath(DATABASE_FILE_NAME);
mRecoverableKeyStoreDb = RecoverableKeyStoreDb.newInstance(context);
- AndroidKeyStoreSecretKey platformKey = generateAndroidKeyStoreKey();
+ AndroidKeyStoreSecretKey platformKey = generatePlatformKey();
mPlatformKey = new PlatformEncryptionKey(TEST_GENERATION_ID, platformKey);
mDecryptKey = new PlatformDecryptionKey(TEST_GENERATION_ID, platformKey);
mRecoverableKeyGenerator = RecoverableKeyGenerator.newInstance(mRecoverableKeyStoreDb);
@@ -117,7 +118,21 @@ public class RecoverableKeyGeneratorTest {
assertArrayEquals(rawMaterial, unwrappedMaterial);
}
- private AndroidKeyStoreSecretKey generateAndroidKeyStoreKey() throws Exception {
+ @Test
+ public void importKey_storesTheWrappedVersionOfTheRawMaterial() throws Exception {
+ byte[] rawMaterial = randomBytes(KEY_SIZE_BYTES);
+ mRecoverableKeyGenerator.importKey(
+ mPlatformKey, TEST_USER_ID, KEYSTORE_UID_SELF, TEST_ALIAS, rawMaterial);
+
+ WrappedKey wrappedKey = mRecoverableKeyStoreDb.getKey(KEYSTORE_UID_SELF, TEST_ALIAS);
+ Cipher cipher = Cipher.getInstance(KEY_WRAP_ALGORITHM);
+ cipher.init(Cipher.DECRYPT_MODE, mDecryptKey.getKey(),
+ new GCMParameterSpec(GCM_TAG_LENGTH_BITS, wrappedKey.getNonce()));
+ byte[] unwrappedMaterial = cipher.doFinal(wrappedKey.getKeyMaterial());
+ assertArrayEquals(rawMaterial, unwrappedMaterial);
+ }
+
+ private AndroidKeyStoreSecretKey generatePlatformKey() throws Exception {
KeyGenerator keyGenerator = KeyGenerator.getInstance(
KEY_ALGORITHM,
ANDROID_KEY_STORE_PROVIDER);
@@ -132,4 +147,10 @@ public class RecoverableKeyGeneratorTest {
private static byte[] getUtf8Bytes(String s) {
return s.getBytes(StandardCharsets.UTF_8);
}
+
+ private static byte[] randomBytes(int n) {
+ byte[] bytes = new byte[n];
+ new Random().nextBytes(bytes);
+ return bytes;
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
index b67659debee1..199410c42b0e 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
@@ -132,6 +132,7 @@ public class RecoverableKeyStoreManagerTest {
private static final String TEST_ALIAS = "nick";
private static final String TEST_ALIAS2 = "bob";
private static final int RECOVERABLE_KEY_SIZE_BYTES = 32;
+ private static final int APPLICATION_KEY_SIZE_BYTES = 32;
private static final int GENERATION_ID = 1;
private static final byte[] NONCE = getUtf8Bytes("nonce");
private static final byte[] KEY_MATERIAL = getUtf8Bytes("keymaterial");
@@ -209,6 +210,39 @@ public class RecoverableKeyStoreManagerTest {
}
@Test
+ public void importKey_storesTheKey() throws Exception {
+ int uid = Binder.getCallingUid();
+ int userId = UserHandle.getCallingUserId();
+ byte[] keyMaterial = randomBytes(APPLICATION_KEY_SIZE_BYTES);
+
+ mRecoverableKeyStoreManager.importKey(TEST_ALIAS, keyMaterial);
+
+ assertThat(mRecoverableKeyStoreDb.getKey(uid, TEST_ALIAS)).isNotNull();
+ assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isTrue();
+ }
+
+ @Test
+ public void importKey_throwsIfInvalidLength() throws Exception {
+ byte[] keyMaterial = randomBytes(APPLICATION_KEY_SIZE_BYTES - 1);
+ try {
+ mRecoverableKeyStoreManager.importKey(TEST_ALIAS, keyMaterial);
+ fail("should have thrown");
+ } catch (ServiceSpecificException e) {
+ assertThat(e.getMessage()).contains("not contain 256 bits");
+ }
+ }
+
+ @Test
+ public void importKey_throwsIfNullKey() throws Exception {
+ try {
+ mRecoverableKeyStoreManager.importKey(TEST_ALIAS, /*keyBytes=*/ null);
+ fail("should have thrown");
+ } catch (ServiceSpecificException e) {
+ assertThat(e.getMessage()).contains("not contain 256 bits");
+ }
+ }
+
+ @Test
public void removeKey_removesAKey() throws Exception {
int uid = Binder.getCallingUid();
mRecoverableKeyStoreManager.generateAndStoreKey(TEST_ALIAS);
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java b/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java
index 297a6eab4a78..956efc075d0a 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java
@@ -380,11 +380,10 @@ public final class UsbDescriptorParser {
if (DEBUG) {
Log.d(TAG, " type:0x" + Integer.toHexString(type));
}
- if ((type >= UsbTerminalTypes.TERMINAL_IN_UNDEFINED
- && type <= UsbTerminalTypes.TERMINAL_IN_PROC_MIC_ARRAY)
- || (type >= UsbTerminalTypes.TERMINAL_BIDIR_UNDEFINED
- && type <= UsbTerminalTypes.TERMINAL_BIDIR_SKRPHONE_CANCEL)
- || (type == UsbTerminalTypes.TERMINAL_USB_STREAMING)) {
+ int terminalCategory = type & ~0xFF;
+ if (terminalCategory != UsbTerminalTypes.TERMINAL_USB_UNDEFINED
+ && terminalCategory != UsbTerminalTypes.TERMINAL_OUT_UNDEFINED) {
+ // If not explicitly a USB connection or output, it could be an input.
hasInput = true;
break;
}
@@ -419,10 +418,10 @@ public final class UsbDescriptorParser {
if (DEBUG) {
Log.d(TAG, " type:0x" + Integer.toHexString(type));
}
- if ((type >= UsbTerminalTypes.TERMINAL_OUT_UNDEFINED
- && type <= UsbTerminalTypes.TERMINAL_OUT_LFSPEAKER)
- || (type >= UsbTerminalTypes.TERMINAL_BIDIR_UNDEFINED
- && type <= UsbTerminalTypes.TERMINAL_BIDIR_SKRPHONE_CANCEL)) {
+ int terminalCategory = type & ~0xFF;
+ if (terminalCategory != UsbTerminalTypes.TERMINAL_USB_UNDEFINED
+ && terminalCategory != UsbTerminalTypes.TERMINAL_IN_UNDEFINED) {
+ // If not explicitly a USB connection or input, it could be an output.
hasOutput = true;
break;
}
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbTerminalTypes.java b/services/usb/java/com/android/server/usb/descriptors/UsbTerminalTypes.java
index 9bd6cb942888..cbb899ea4c32 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbTerminalTypes.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbTerminalTypes.java
@@ -24,6 +24,7 @@ public final class UsbTerminalTypes {
private static final String TAG = "UsbTerminalTypes";
// USB
+ public static final int TERMINAL_USB_UNDEFINED = 0x0100;
public static final int TERMINAL_USB_STREAMING = 0x0101;
// Inputs
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 6798a83142dc..96eb23d88b1f 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -1815,7 +1815,14 @@ public class CarrierConfigManager {
"check_pricing_with_carrier_data_roaming_bool";
/**
- * List of thresholds of RSRP for determining the display level of LTE signal bar.
+ * A list of 4 LTE RSRP thresholds above which a signal level is considered POOR,
+ * MODERATE, GOOD, or EXCELLENT, to be used in SignalStrength reporting.
+ *
+ * Note that the min and max thresholds are fixed at -140 and -44, as explained in
+ * TS 136.133 9.1.4 - RSRP Measurement Report Mapping.
+ * <p>
+ * See SignalStrength#MAX_LTE_RSRP and SignalStrength#MIN_LTE_RSRP. Any signal level outside
+ * these boundaries is considered invalid.
* @hide
*/
public static final String KEY_LTE_RSRP_THRESHOLDS_INT_ARRAY =
@@ -2136,12 +2143,10 @@ public class CarrierConfigManager {
sDefaults.putBoolean(KEY_CHECK_PRICING_WITH_CARRIER_FOR_DATA_ROAMING_BOOL, false);
sDefaults.putIntArray(KEY_LTE_RSRP_THRESHOLDS_INT_ARRAY,
new int[] {
- -140, /* SIGNAL_STRENGTH_NONE_OR_UNKNOWN */
-128, /* SIGNAL_STRENGTH_POOR */
-118, /* SIGNAL_STRENGTH_MODERATE */
-108, /* SIGNAL_STRENGTH_GOOD */
-98, /* SIGNAL_STRENGTH_GREAT */
- -44
});
}
diff --git a/telephony/java/android/telephony/SignalStrength.java b/telephony/java/android/telephony/SignalStrength.java
index 778ca77662ab..47a7d5f35388 100644
--- a/telephony/java/android/telephony/SignalStrength.java
+++ b/telephony/java/android/telephony/SignalStrength.java
@@ -62,7 +62,9 @@ public class SignalStrength implements Parcelable {
*/
public static final int INVALID = Integer.MAX_VALUE;
- private static final int LTE_RSRP_THRESHOLDS_NUM = 6;
+ private static final int LTE_RSRP_THRESHOLDS_NUM = 4;
+ private static final int MAX_LTE_RSRP = -44;
+ private static final int MIN_LTE_RSRP = -140;
/** Parameters reported by the Radio */
private int mGsmSignalStrength; // Valid values are (0-31, 99) as defined in TS 27.007 8.5
@@ -86,7 +88,8 @@ public class SignalStrength implements Parcelable {
// onSignalStrengthResult.
private boolean mUseOnlyRsrpForLteLevel; // Use only RSRP for the number of LTE signal bar.
- // The threshold of LTE RSRP for determining the display level of LTE signal bar.
+ // The threshold of LTE RSRP for determining the display level of LTE signal bar. Note that the
+ // min and max are fixed at MIN_LTE_RSRP (-140) and MAX_LTE_RSRP (-44).
private int mLteRsrpThresholds[] = new int[LTE_RSRP_THRESHOLDS_NUM];
/**
@@ -324,7 +327,8 @@ public class SignalStrength implements Parcelable {
// TS 36.214 Physical Layer Section 5.1.3, TS 36.331 RRC
mLteSignalStrength = (mLteSignalStrength >= 0) ? mLteSignalStrength : 99;
- mLteRsrp = ((mLteRsrp >= 44) && (mLteRsrp <= 140)) ? -mLteRsrp : SignalStrength.INVALID;
+ mLteRsrp = ((-mLteRsrp >= MIN_LTE_RSRP) && (-mLteRsrp <= MAX_LTE_RSRP)) ? -mLteRsrp
+ : SignalStrength.INVALID;
mLteRsrq = ((mLteRsrq >= 3) && (mLteRsrq <= 20)) ? -mLteRsrq : SignalStrength.INVALID;
mLteRssnr = ((mLteRssnr >= -200) && (mLteRssnr <= 300)) ? mLteRssnr
: SignalStrength.INVALID;
@@ -740,24 +744,29 @@ public class SignalStrength implements Parcelable {
*/
public int getLteLevel() {
/*
- * TS 36.214 Physical Layer Section 5.1.3 TS 36.331 RRC RSSI = received
- * signal + noise RSRP = reference signal dBm RSRQ = quality of signal
- * dB= Number of Resource blocksxRSRP/RSSI SNR = gain=signal/noise ratio
- * = -10log P1/P2 dB
+ * TS 36.214 Physical Layer Section 5.1.3
+ * TS 36.331 RRC
+ *
+ * RSSI = received signal + noise
+ * RSRP = reference signal dBm
+ * RSRQ = quality of signal dB = Number of Resource blocks*RSRP/RSSI
+ * SNR = gain = signal/noise ratio = -10log P1/P2 dB
*/
int rssiIconLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN, rsrpIconLevel = -1, snrIconLevel = -1;
- if (mLteRsrp > mLteRsrpThresholds[5]) {
- rsrpIconLevel = -1;
- } else if (mLteRsrp >= (mLteRsrpThresholds[4] - mLteRsrpBoost)) {
- rsrpIconLevel = SIGNAL_STRENGTH_GREAT;
+ if (mLteRsrp > MAX_LTE_RSRP || mLteRsrp < MIN_LTE_RSRP) {
+ if (mLteRsrp != INVALID) {
+ Log.wtf(LOG_TAG, "getLteLevel - invalid lte rsrp: mLteRsrp=" + mLteRsrp);
+ }
} else if (mLteRsrp >= (mLteRsrpThresholds[3] - mLteRsrpBoost)) {
- rsrpIconLevel = SIGNAL_STRENGTH_GOOD;
+ rsrpIconLevel = SIGNAL_STRENGTH_GREAT;
} else if (mLteRsrp >= (mLteRsrpThresholds[2] - mLteRsrpBoost)) {
- rsrpIconLevel = SIGNAL_STRENGTH_MODERATE;
+ rsrpIconLevel = SIGNAL_STRENGTH_GOOD;
} else if (mLteRsrp >= (mLteRsrpThresholds[1] - mLteRsrpBoost)) {
+ rsrpIconLevel = SIGNAL_STRENGTH_MODERATE;
+ } else if (mLteRsrp >= (mLteRsrpThresholds[0] - mLteRsrpBoost)) {
rsrpIconLevel = SIGNAL_STRENGTH_POOR;
- } else if (mLteRsrp >= mLteRsrpThresholds[0]) {
+ } else {
rsrpIconLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
}
diff --git a/tests/UsbTests/res/raw/readme.txt b/tests/UsbTests/res/raw/readme.txt
new file mode 100644
index 000000000000..62b673c2f079
--- /dev/null
+++ b/tests/UsbTests/res/raw/readme.txt
@@ -0,0 +1,35 @@
+The usbdescriptors_ files contain raw USB descriptors from the Google
+USB-C to 3.5mm adapter, with different loads connected to the 3.5mm
+jack.
+
+usbdescriptors_nothing.bin:
+ - The descriptors when the jack is disconnected.
+
+usbdescriptors_headphones.bin:
+ - The descriptors when the jack is connected to 32-ohm headphones,
+ no microphone.
+ The relevant output terminal is:
+ bDescriptorSubtype 3 (OUTPUT_TERMINAL)
+ bTerminalID 15
+ wTerminalType 0x0302 Headphones
+
+usbdescriptors_lineout.bin:
+ - The descriptors when the jack is connected to a PC line-in jack.
+ The relevant output terminal is:
+ bDescriptorSubtype 3 (OUTPUT_TERMINAL)
+ bTerminalID 15
+ wTerminalType 0x0603 Line Connector
+
+usbdescriptors_headset.bin:
+ - The descriptors when a headset with microphone and low-impedance
+ headphones are connected.
+ The relevant input terminal is:
+ bDescriptorSubtype 2 (INPUT_TERMINAL)
+ bTerminalID 1
+ wTerminalType 0x0201 Microphone
+ The relevant output terminal is:
+ bDescriptorSubtype 3 (OUTPUT_TERMINAL)
+ bTerminalID 15
+ wTerminalType 0x0302 Headphones
+
+
diff --git a/tests/UsbTests/res/raw/usbdescriptors_headphones.bin b/tests/UsbTests/res/raw/usbdescriptors_headphones.bin
new file mode 100644
index 000000000000..e8f2932d7b5b
--- /dev/null
+++ b/tests/UsbTests/res/raw/usbdescriptors_headphones.bin
Binary files differ
diff --git a/tests/UsbTests/res/raw/usbdescriptors_headset.bin b/tests/UsbTests/res/raw/usbdescriptors_headset.bin
new file mode 100644
index 000000000000..30eef2aae787
--- /dev/null
+++ b/tests/UsbTests/res/raw/usbdescriptors_headset.bin
Binary files differ
diff --git a/tests/UsbTests/res/raw/usbdescriptors_lineout.bin b/tests/UsbTests/res/raw/usbdescriptors_lineout.bin
new file mode 100644
index 000000000000..d540d33d15f3
--- /dev/null
+++ b/tests/UsbTests/res/raw/usbdescriptors_lineout.bin
Binary files differ
diff --git a/tests/UsbTests/res/raw/usbdescriptors_nothing.bin b/tests/UsbTests/res/raw/usbdescriptors_nothing.bin
new file mode 100644
index 000000000000..c318abf93afb
--- /dev/null
+++ b/tests/UsbTests/res/raw/usbdescriptors_nothing.bin
Binary files differ
diff --git a/tests/UsbTests/src/com/android/server/usb/UsbDescriptorParserTests.java b/tests/UsbTests/src/com/android/server/usb/UsbDescriptorParserTests.java
new file mode 100644
index 000000000000..f32395226f4a
--- /dev/null
+++ b/tests/UsbTests/src/com/android/server/usb/UsbDescriptorParserTests.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.usb;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.fail;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.Resources.NotFoundException;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import com.android.server.usb.descriptors.UsbDescriptorParser;
+import com.google.common.io.ByteStreams;
+
+import java.io.InputStream;
+import java.io.IOException;
+import java.lang.Exception;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests for {@link com.android.server.usb.descriptors.UsbDescriptorParser}
+ */
+@RunWith(AndroidJUnit4.class)
+public class UsbDescriptorParserTests {
+
+ public UsbDescriptorParser loadParser(int resource) {
+ Context c = InstrumentationRegistry.getContext();
+ Resources res = c.getResources();
+ InputStream is = null;
+ try {
+ is = res.openRawResource(resource);
+ } catch (NotFoundException e) {
+ fail("Failed to load resource.");
+ }
+
+ byte[] descriptors = null;
+ try {
+ descriptors = ByteStreams.toByteArray(is);
+ } catch (IOException e) {
+ fail("Failed to convert descriptor strema to bytearray.");
+ }
+
+ // Testing same codepath as UsbHostManager.java:usbDeviceAdded
+ UsbDescriptorParser parser = new UsbDescriptorParser("test-usb-addr");
+ if (!parser.parseDescriptors(descriptors)) {
+ fail("failed to parse descriptors.");
+ }
+ return parser;
+ }
+
+ // A Headset has a microphone and a speaker and is a headset.
+ @Test
+ @SmallTest
+ public void testHeadsetDescriptorParser() {
+ UsbDescriptorParser parser = loadParser(R.raw.usbdescriptors_headset);
+ assertTrue(parser.hasInput());
+ assertTrue(parser.hasOutput());
+ assertTrue(parser.isInputHeadset());
+ assertTrue(parser.isOutputHeadset());
+ }
+
+ // Headphones have no microphones but are considered a headset.
+ @Test
+ @SmallTest
+ public void testHeadphoneDescriptorParser() {
+ UsbDescriptorParser parser = loadParser(R.raw.usbdescriptors_headphones);
+ assertFalse(parser.hasInput());
+ assertTrue(parser.hasOutput());
+ assertFalse(parser.isInputHeadset());
+ assertTrue(parser.isOutputHeadset());
+ }
+
+ // Line out has no microphones and aren't considered a headset.
+ @Test
+ @SmallTest
+ public void testLineoutDescriptorParser() {
+ UsbDescriptorParser parser = loadParser(R.raw.usbdescriptors_lineout);
+ assertFalse(parser.hasInput());
+ assertTrue(parser.hasOutput());
+ assertFalse(parser.isInputHeadset());
+ assertFalse(parser.isOutputHeadset());
+ }
+
+ // An HID-only device shouldn't be considered anything at all.
+ @Test
+ @SmallTest
+ public void testNothingDescriptorParser() {
+ UsbDescriptorParser parser = loadParser(R.raw.usbdescriptors_nothing);
+ assertFalse(parser.hasInput());
+ assertFalse(parser.hasOutput());
+ assertFalse(parser.isInputHeadset());
+ assertFalse(parser.isOutputHeadset());
+ }
+
+}
diff --git a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
index 66e0955b04c3..3e1ff6dd5f32 100644
--- a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
+++ b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
@@ -281,6 +281,7 @@ public class IpSecServiceParameterizedTest {
anyInt());
}
+ @Test
public void testCreateTwoTransformsWithSameSpis() throws Exception {
IpSecConfig ipSecConfig = new IpSecConfig();
addDefaultSpisAndRemoteAddrToIpSecConfig(ipSecConfig);
diff --git a/tools/aapt2/format/binary/XmlFlattener.cpp b/tools/aapt2/format/binary/XmlFlattener.cpp
index 345cc95cfb29..067372b99b5c 100644
--- a/tools/aapt2/format/binary/XmlFlattener.cpp
+++ b/tools/aapt2/format/binary/XmlFlattener.cpp
@@ -26,6 +26,7 @@
#include "utils/misc.h"
#include "SdkConstants.h"
+#include "ValueVisitor.h"
#include "format/binary/ChunkWriter.h"
#include "format/binary/ResourceTypeExtensions.h"
#include "xml/XmlDom.h"
@@ -153,6 +154,9 @@ class XmlFlattenerVisitor : public xml::ConstVisitor {
private:
DISALLOW_COPY_AND_ASSIGN(XmlFlattenerVisitor);
+ // We are adding strings to a StringPool whose strings will be sorted and merged with other
+ // string pools. That means we can't encode the ID of a string directly. Instead, we defer the
+ // writing of the ID here, until after the StringPool is merged and sorted.
void AddString(const StringPiece& str, uint32_t priority, android::ResStringPool_ref* dest,
bool treat_empty_string_as_null = false) {
if (str.empty() && treat_empty_string_as_null) {
@@ -164,6 +168,9 @@ class XmlFlattenerVisitor : public xml::ConstVisitor {
}
}
+ // We are adding strings to a StringPool whose strings will be sorted and merged with other
+ // string pools. That means we can't encode the ID of a string directly. Instead, we defer the
+ // writing of the ID here, until after the StringPool is merged and sorted.
void AddString(const StringPool::Ref& ref, android::ResStringPool_ref* dest) {
string_refs.push_back(StringFlattenDest{ref, dest});
}
@@ -248,30 +255,39 @@ class XmlFlattenerVisitor : public xml::ConstVisitor {
AddString(name_ref, &flat_attr->name);
}
- // Process plain strings to make sure they get properly escaped.
- StringPiece raw_value = xml_attr->value;
-
- util::StringBuilder str_builder(true /*preserve_spaces*/);
- str_builder.Append(xml_attr->value);
-
- if (!options_.keep_raw_values) {
- raw_value = str_builder.ToString();
- }
-
- if (options_.keep_raw_values || !xml_attr->compiled_value) {
- // Keep raw values if the value is not compiled or
- // if we're building a static library (need symbols).
- AddString(raw_value, kLowPriority, &flat_attr->rawValue);
+ std::string processed_str;
+ Maybe<StringPiece> compiled_text;
+ if (xml_attr->compiled_value != nullptr) {
+ // Make sure we're not flattening a String. A String can be referencing a string from
+ // a different StringPool than we're using here to build the binary XML.
+ String* string_value = ValueCast<String>(xml_attr->compiled_value.get());
+ if (string_value != nullptr) {
+ // Mark the String's text as needing to be serialized.
+ compiled_text = StringPiece(*string_value->value);
+ } else {
+ // Serialize this compiled value safely.
+ CHECK(xml_attr->compiled_value->Flatten(&flat_attr->typedValue));
+ }
+ } else {
+ // There is no compiled value, so treat the raw string as compiled, once it is processed to
+ // make sure escape sequences are properly interpreted.
+ processed_str =
+ util::StringBuilder(true /*preserve_spaces*/).Append(xml_attr->value).ToString();
+ compiled_text = StringPiece(processed_str);
}
- if (xml_attr->compiled_value) {
- CHECK(xml_attr->compiled_value->Flatten(&flat_attr->typedValue));
- } else {
- // Flatten as a regular string type.
+ if (compiled_text) {
+ // Write out the compiled text and raw_text.
flat_attr->typedValue.dataType = android::Res_value::TYPE_STRING;
-
- AddString(str_builder.ToString(), kLowPriority,
- (ResStringPool_ref*)&flat_attr->typedValue.data);
+ AddString(compiled_text.value(), kLowPriority,
+ reinterpret_cast<ResStringPool_ref*>(&flat_attr->typedValue.data));
+ if (options_.keep_raw_values) {
+ AddString(xml_attr->value, kLowPriority, &flat_attr->rawValue);
+ } else {
+ AddString(compiled_text.value(), kLowPriority, &flat_attr->rawValue);
+ }
+ } else if (options_.keep_raw_values && !xml_attr->value.empty()) {
+ AddString(xml_attr->value, kLowPriority, &flat_attr->rawValue);
}
flat_attr->typedValue.size = util::HostToDevice16(sizeof(flat_attr->typedValue));
diff --git a/tools/aapt2/format/binary/XmlFlattener_test.cpp b/tools/aapt2/format/binary/XmlFlattener_test.cpp
index 0450f6c16de5..08243feb3769 100644
--- a/tools/aapt2/format/binary/XmlFlattener_test.cpp
+++ b/tools/aapt2/format/binary/XmlFlattener_test.cpp
@@ -286,4 +286,92 @@ TEST_F(XmlFlattenerTest, ProcessEscapedStrings) {
EXPECT_THAT(tree.getText(&len), StrEq(u"\\d{5}"));
}
+TEST_F(XmlFlattenerTest, FlattenRawValueOnlyMakesCompiledValueToo) {
+ std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(R"(<element foo="bar" />)");
+
+ // Raw values are kept when encoding an attribute with no compiled value, regardless of option.
+ XmlFlattenerOptions options;
+ options.keep_raw_values = false;
+
+ android::ResXMLTree tree;
+ ASSERT_TRUE(Flatten(doc.get(), &tree, options));
+
+ while (tree.next() != android::ResXMLTree::START_TAG) {
+ ASSERT_THAT(tree.getEventType(), Ne(android::ResXMLTree::BAD_DOCUMENT));
+ ASSERT_THAT(tree.getEventType(), Ne(android::ResXMLTree::END_DOCUMENT));
+ }
+
+ ASSERT_THAT(tree.getAttributeCount(), Eq(1u));
+ EXPECT_THAT(tree.getAttributeValueStringID(0), Ge(0));
+ EXPECT_THAT(tree.getAttributeDataType(0), Eq(android::Res_value::TYPE_STRING));
+ EXPECT_THAT(tree.getAttributeValueStringID(0), Eq(tree.getAttributeData(0)));
+}
+
+TEST_F(XmlFlattenerTest, FlattenCompiledStringValuePreservesRawValue) {
+ std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(R"(<element foo="bar" />)");
+ doc->root->attributes[0].compiled_value =
+ util::make_unique<String>(doc->string_pool.MakeRef("bar"));
+
+ // Raw values are kept when encoding a string anyways.
+ XmlFlattenerOptions options;
+ options.keep_raw_values = false;
+
+ android::ResXMLTree tree;
+ ASSERT_TRUE(Flatten(doc.get(), &tree, options));
+
+ while (tree.next() != android::ResXMLTree::START_TAG) {
+ ASSERT_THAT(tree.getEventType(), Ne(android::ResXMLTree::BAD_DOCUMENT));
+ ASSERT_THAT(tree.getEventType(), Ne(android::ResXMLTree::END_DOCUMENT));
+ }
+
+ ASSERT_THAT(tree.getAttributeCount(), Eq(1u));
+ EXPECT_THAT(tree.getAttributeValueStringID(0), Ge(0));
+ EXPECT_THAT(tree.getAttributeDataType(0), Eq(android::Res_value::TYPE_STRING));
+ EXPECT_THAT(tree.getAttributeValueStringID(0), Eq(tree.getAttributeData(0)));
+}
+
+TEST_F(XmlFlattenerTest, FlattenCompiledValueExcludesRawValueWithKeepRawOptionFalse) {
+ std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(R"(<element foo="true" />)");
+ doc->root->attributes[0].compiled_value = ResourceUtils::MakeBool(true);
+
+ XmlFlattenerOptions options;
+ options.keep_raw_values = false;
+
+ android::ResXMLTree tree;
+ ASSERT_TRUE(Flatten(doc.get(), &tree, options));
+
+ while (tree.next() != android::ResXMLTree::START_TAG) {
+ ASSERT_THAT(tree.getEventType(), Ne(android::ResXMLTree::BAD_DOCUMENT));
+ ASSERT_THAT(tree.getEventType(), Ne(android::ResXMLTree::END_DOCUMENT));
+ }
+
+ ASSERT_THAT(tree.getAttributeCount(), Eq(1u));
+ EXPECT_THAT(tree.getAttributeValueStringID(0), Eq(-1));
+ EXPECT_THAT(tree.getAttributeDataType(0), Eq(android::Res_value::TYPE_INT_BOOLEAN));
+}
+
+TEST_F(XmlFlattenerTest, FlattenCompiledValueExcludesRawValueWithKeepRawOptionTrue) {
+ std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(R"(<element foo="true" />)");
+ doc->root->attributes[0].compiled_value = ResourceUtils::MakeBool(true);
+
+ XmlFlattenerOptions options;
+ options.keep_raw_values = true;
+
+ android::ResXMLTree tree;
+ ASSERT_TRUE(Flatten(doc.get(), &tree, options));
+
+ while (tree.next() != android::ResXMLTree::START_TAG) {
+ ASSERT_THAT(tree.getEventType(), Ne(android::ResXMLTree::BAD_DOCUMENT));
+ ASSERT_THAT(tree.getEventType(), Ne(android::ResXMLTree::END_DOCUMENT));
+ }
+
+ ASSERT_THAT(tree.getAttributeCount(), Eq(1u));
+ EXPECT_THAT(tree.getAttributeValueStringID(0), Ge(0));
+
+ size_t len;
+ EXPECT_THAT(tree.getAttributeStringValue(0, &len), StrEq(u"true"));
+
+ EXPECT_THAT(tree.getAttributeDataType(0), Eq(android::Res_value::TYPE_INT_BOOLEAN));
+}
+
} // namespace aapt
diff --git a/tools/aapt2/xml/XmlDom.cpp b/tools/aapt2/xml/XmlDom.cpp
index 7b748ce78cbc..b6cd08697545 100644
--- a/tools/aapt2/xml/XmlDom.cpp
+++ b/tools/aapt2/xml/XmlDom.cpp
@@ -248,8 +248,14 @@ static void CopyAttributes(Element* el, android::ResXMLParser* parser, StringPoo
android::Res_value res_value;
if (parser->getAttributeValue(i, &res_value) > 0) {
- attr.compiled_value = ResourceUtils::ParseBinaryResValue(
- ResourceType::kAnim, {}, parser->getStrings(), res_value, out_pool);
+ // Only compile the value if it is not a string, or it is a string that differs from
+ // the raw attribute value.
+ int32_t raw_value_idx = parser->getAttributeValueStringID(i);
+ if (res_value.dataType != android::Res_value::TYPE_STRING || raw_value_idx < 0 ||
+ static_cast<uint32_t>(raw_value_idx) != res_value.data) {
+ attr.compiled_value = ResourceUtils::ParseBinaryResValue(
+ ResourceType::kAnim, {}, parser->getStrings(), res_value, out_pool);
+ }
}
el->attributes.push_back(std::move(attr));
@@ -262,8 +268,8 @@ std::unique_ptr<XmlResource> Inflate(const void* data, size_t len, std::string*
// an enum, which causes errors when qualifying it with android::
using namespace android;
- StringPool string_pool;
- std::unique_ptr<Element> root;
+ std::unique_ptr<XmlResource> xml_resource = util::make_unique<XmlResource>();
+
std::stack<Element*> node_stack;
std::unique_ptr<Element> pending_element;
@@ -322,12 +328,12 @@ std::unique_ptr<XmlResource> Inflate(const void* data, size_t len, std::string*
}
Element* this_el = el.get();
- CopyAttributes(el.get(), &tree, &string_pool);
+ CopyAttributes(el.get(), &tree, &xml_resource->string_pool);
if (!node_stack.empty()) {
node_stack.top()->AppendChild(std::move(el));
} else {
- root = std::move(el);
+ xml_resource->root = std::move(el);
}
node_stack.push(this_el);
break;
@@ -359,7 +365,7 @@ std::unique_ptr<XmlResource> Inflate(const void* data, size_t len, std::string*
break;
}
}
- return util::make_unique<XmlResource>(ResourceFile{}, std::move(string_pool), std::move(root));
+ return xml_resource;
}
std::unique_ptr<XmlResource> XmlResource::Clone() const {