diff options
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 Binary files differnew file mode 100644 index 000000000000..e8f2932d7b5b --- /dev/null +++ b/tests/UsbTests/res/raw/usbdescriptors_headphones.bin diff --git a/tests/UsbTests/res/raw/usbdescriptors_headset.bin b/tests/UsbTests/res/raw/usbdescriptors_headset.bin Binary files differnew file mode 100644 index 000000000000..30eef2aae787 --- /dev/null +++ b/tests/UsbTests/res/raw/usbdescriptors_headset.bin diff --git a/tests/UsbTests/res/raw/usbdescriptors_lineout.bin b/tests/UsbTests/res/raw/usbdescriptors_lineout.bin Binary files differnew file mode 100644 index 000000000000..d540d33d15f3 --- /dev/null +++ b/tests/UsbTests/res/raw/usbdescriptors_lineout.bin diff --git a/tests/UsbTests/res/raw/usbdescriptors_nothing.bin b/tests/UsbTests/res/raw/usbdescriptors_nothing.bin Binary files differnew file mode 100644 index 000000000000..c318abf93afb --- /dev/null +++ b/tests/UsbTests/res/raw/usbdescriptors_nothing.bin 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 { |