Merge "Adb command for binary push logging" into qt-dev
diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp
index a9f5208e..ec02b12 100644
--- a/cmds/statsd/src/StatsLogProcessor.cpp
+++ b/cmds/statsd/src/StatsLogProcessor.cpp
@@ -68,12 +68,8 @@
const int FIELD_ID_DUMP_REPORT_REASON = 8;
const int FIELD_ID_STRINGS = 9;
-const int FIELD_ID_ACTIVE_CONFIG_LIST = 1;
-const int FIELD_ID_CONFIG_ID = 1;
-const int FIELD_ID_CONFIG_UID = 2;
-const int FIELD_ID_ACTIVE_METRIC = 3;
-const int FIELD_ID_METRIC_ID = 1;
-const int FIELD_ID_TIME_TO_LIVE_NANOS = 2;
+// for ActiveConfigList
+const int FIELD_ID_ACTIVE_CONFIG_LIST_CONFIG = 1;
#define NS_PER_HOUR 3600 * NS_PER_SEC
@@ -523,7 +519,7 @@
std::lock_guard<std::mutex> lock(mMetricsMutex);
auto it = mMetricsManagers.find(key);
if (it != mMetricsManagers.end()) {
- WriteDataToDiskLocked(key, getElapsedRealtimeNs(), CONFIG_REMOVED,
+ WriteDataToDiskLocked(key, getElapsedRealtimeNs(), CONFIG_REMOVED,
NO_TIME_CONSTRAINTS);
mMetricsManagers.erase(it);
mUidMap->OnConfigRemoved(key);
@@ -613,7 +609,7 @@
mOnDiskDataConfigs.insert(key);
}
-void StatsLogProcessor::WriteMetricsActivationToDisk(int64_t currentTimeNs) {
+void StatsLogProcessor::SaveActiveConfigsToDisk(int64_t currentTimeNs) {
std::lock_guard<std::mutex> lock(mMetricsMutex);
const int64_t timeNs = getElapsedRealtimeNs();
@@ -629,28 +625,12 @@
mLastActiveMetricsWriteNs = timeNs;
ProtoOutputStream proto;
-
for (const auto& pair : mMetricsManagers) {
- uint64_t activeConfigListToken = proto.start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED |
- FIELD_ID_ACTIVE_CONFIG_LIST);
- proto.write(FIELD_TYPE_INT64 | FIELD_ID_CONFIG_ID, (long long)pair.first.GetId());
- proto.write(FIELD_TYPE_INT32 | FIELD_ID_CONFIG_UID, pair.first.GetUid());
-
- vector<MetricProducer*> activeMetrics;
- pair.second->prepForShutDown(currentTimeNs);
- pair.second->getActiveMetrics(activeMetrics);
- for (MetricProducer* metric : activeMetrics) {
- if (metric->isActive()) {
- uint64_t metricToken = proto.start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED |
- FIELD_ID_ACTIVE_METRIC);
- proto.write(FIELD_TYPE_INT64 | FIELD_ID_METRIC_ID,
- (long long)metric->getMetricId());
- proto.write(FIELD_TYPE_INT64 | FIELD_ID_TIME_TO_LIVE_NANOS,
- (long long)metric->getRemainingTtlNs(currentTimeNs));
- proto.end(metricToken);
- }
- }
- proto.end(activeConfigListToken);
+ const sp<MetricsManager>& metricsManager = pair.second;
+ uint64_t configToken = proto.start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED |
+ FIELD_ID_ACTIVE_CONFIG_LIST_CONFIG);
+ metricsManager->writeActiveConfigToProtoOutputStream(currentTimeNs, &proto);
+ proto.end(configToken);
}
string file_name = StringPrintf("%s/active_metrics", STATS_ACTIVE_METRIC_DIR);
@@ -664,30 +644,45 @@
proto.flush(fd.get());
}
-void StatsLogProcessor::LoadMetricsActivationFromDisk() {
+void StatsLogProcessor::LoadActiveConfigsFromDisk() {
+ std::lock_guard<std::mutex> lock(mMetricsMutex);
+
string file_name = StringPrintf("%s/active_metrics", STATS_ACTIVE_METRIC_DIR);
int fd = open(file_name.c_str(), O_RDONLY | O_CLOEXEC);
- if (fd != -1) {
- string content;
- if (android::base::ReadFdToString(fd, &content)) {
- ActiveConfigList activeConfigList;
- if (activeConfigList.ParseFromString(content)) {
- for (int i = 0; i < activeConfigList.active_config_size(); i++) {
- const auto& config = activeConfigList.active_config(i);
- ConfigKey key(config.uid(), config.config_id());
- auto it = mMetricsManagers.find(key);
- if (it == mMetricsManagers.end()) {
- ALOGE("No config found for config %s", key.ToString().c_str());
- continue;
- }
- VLOG("Setting active config %s", key.ToString().c_str());
- it->second->setActiveMetrics(config, mTimeBaseNs);
- }
- }
- VLOG("Successfully loaded %d active configs.", activeConfigList.active_config_size());
- }
- close(fd);
+ if (-1 == fd) {
+ VLOG("Attempt to read %s but failed", file_name.c_str());
+ StorageManager::deleteFile(file_name.c_str());
+ return;
}
+ string content;
+ if (!android::base::ReadFdToString(fd, &content)) {
+ ALOGE("Attempt to read %s but failed", file_name.c_str());
+ close(fd);
+ StorageManager::deleteFile(file_name.c_str());
+ return;
+ }
+
+ close(fd);
+
+ ActiveConfigList activeConfigList;
+ if (!activeConfigList.ParseFromString(content)) {
+ ALOGE("Attempt to read %s but failed; failed to load active configs", file_name.c_str());
+ StorageManager::deleteFile(file_name.c_str());
+ return;
+ }
+ for (int i = 0; i < activeConfigList.config_size(); i++) {
+ const auto& config = activeConfigList.config(i);
+ ConfigKey key(config.uid(), config.id());
+ auto it = mMetricsManagers.find(key);
+ if (it == mMetricsManagers.end()) {
+ ALOGE("No config found for config %s", key.ToString().c_str());
+ continue;
+ }
+ VLOG("Setting active config %s", key.ToString().c_str());
+ it->second->loadActiveConfig(config, mTimeBaseNs);
+ }
+ VLOG("Successfully loaded %d active configs.", activeConfigList.config_size());
+
StorageManager::deleteFile(file_name.c_str());
}
@@ -709,7 +704,7 @@
}
}
-void StatsLogProcessor::WriteDataToDisk(const DumpReportReason dumpReportReason,
+void StatsLogProcessor::WriteDataToDisk(const DumpReportReason dumpReportReason,
const DumpLatency dumpLatency) {
std::lock_guard<std::mutex> lock(mMetricsMutex);
WriteDataToDiskLocked(dumpReportReason, dumpLatency);
diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h
index 6178a4b..0dc597b 100644
--- a/cmds/statsd/src/StatsLogProcessor.h
+++ b/cmds/statsd/src/StatsLogProcessor.h
@@ -89,11 +89,11 @@
void WriteDataToDisk(const DumpReportReason dumpReportReason,
const DumpLatency dumpLatency);
- /* Persist metric activation status onto disk. */
- void WriteMetricsActivationToDisk(int64_t currentTimeNs);
+ /* Persist configs containing metrics with active activations to disk. */
+ void SaveActiveConfigsToDisk(int64_t currentTimeNs);
- /* Load metric activation status from disk. */
- void LoadMetricsActivationFromDisk();
+ /* Load configs containing metrics with active activations from disk. */
+ void LoadActiveConfigsFromDisk();
// Reset all configs.
void resetConfigs();
@@ -221,6 +221,9 @@
FRIEND_TEST(StatsLogProcessorTest, TestDropWhenByteSizeTooLarge);
FRIEND_TEST(StatsLogProcessorTest, TestActiveConfigMetricDiskWriteRead);
FRIEND_TEST(StatsLogProcessorTest, TestActivationOnBoot);
+ FRIEND_TEST(StatsLogProcessorTest, TestActivationOnBootMultipleActivations);
+ FRIEND_TEST(StatsLogProcessorTest,
+ TestActivationOnBootMultipleActivationsDifferentActivationTypes);
FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForSumDuration1);
FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForSumDuration2);
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index 4063c85..623a1f2 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -1038,7 +1038,7 @@
ENFORCE_UID(AID_SYSTEM);
VLOG("StatsService::informDeviceShutdown");
mProcessor->WriteDataToDisk(DEVICE_SHUTDOWN, FAST);
- mProcessor->WriteMetricsActivationToDisk(getElapsedRealtimeNs());
+ mProcessor->SaveActiveConfigsToDisk(getElapsedRealtimeNs());
return Status::ok();
}
@@ -1073,14 +1073,14 @@
void StatsService::Startup() {
mConfigManager->Startup();
- mProcessor->LoadMetricsActivationFromDisk();
+ mProcessor->LoadActiveConfigsFromDisk();
}
void StatsService::Terminate() {
ALOGI("StatsService::Terminating");
if (mProcessor != nullptr) {
mProcessor->WriteDataToDisk(TERMINATION_SIGNAL_RECEIVED, FAST);
- mProcessor->WriteMetricsActivationToDisk(getElapsedRealtimeNs());
+ mProcessor->SaveActiveConfigsToDisk(getElapsedRealtimeNs());
}
}
diff --git a/cmds/statsd/src/active_config_list.proto b/cmds/statsd/src/active_config_list.proto
index 0e9ee03..ef8e50b 100644
--- a/cmds/statsd/src/active_config_list.proto
+++ b/cmds/statsd/src/active_config_list.proto
@@ -21,23 +21,26 @@
option java_multiple_files = true;
option java_outer_classname = "ActiveConfigProto";
+message ActiveEventActivation {
+ optional int32 atom_matcher_index = 1;
+
+ // Time left in activation. When this proto is loaded after device boot,
+ // the activation should be set to active for this duration.
+ optional int64 remaining_ttl_nanos = 2;
+}
+
message ActiveMetric {
- // metric id
- optional int64 metric_id = 1;
- // Remaining time to live in nano seconds. -1 for infinity.
- optional int64 time_to_live_nanos = 2;
+ optional int64 id = 1;
+ repeated ActiveEventActivation activation = 2;
}
message ActiveConfig {
- // config id
- optional int64 config_id = 1;
- // config uid
+ optional int64 id = 1;
optional int32 uid = 2;
- // metrics
- repeated ActiveMetric active_metric = 3;
+ repeated ActiveMetric metric = 3;
}
// all configs and their metrics on device.
message ActiveConfigList {
- repeated ActiveConfig active_config = 1;
-}
\ No newline at end of file
+ repeated ActiveConfig config = 1;
+}
diff --git a/cmds/statsd/src/metrics/MetricProducer.cpp b/cmds/statsd/src/metrics/MetricProducer.cpp
index e22b853..9ad7f09 100644
--- a/cmds/statsd/src/metrics/MetricProducer.cpp
+++ b/cmds/statsd/src/metrics/MetricProducer.cpp
@@ -18,12 +18,26 @@
#include "Log.h"
#include "MetricProducer.h"
+using android::util::FIELD_COUNT_REPEATED;
+using android::util::FIELD_TYPE_INT32;
+using android::util::FIELD_TYPE_INT64;
+using android::util::FIELD_TYPE_MESSAGE;
+using android::util::ProtoOutputStream;
+
namespace android {
namespace os {
namespace statsd {
using std::map;
+// for ActiveMetric
+const int FIELD_ID_ACTIVE_METRIC_ID = 1;
+const int FIELD_ID_ACTIVE_METRIC_ACTIVATION = 2;
+
+// for ActiveEventActivation
+const int FIELD_ID_ACTIVE_EVENT_ACTIVATION_ATOM_MATCHER_INDEX = 1;
+const int FIELD_ID_ACTIVE_EVENT_ACTIVATION_REMAINING_TTL_NANOS = 2;
+
void MetricProducer::onMatchedLogEventLocked(const size_t matcherIndex, const LogEvent& event) {
if (!mIsActive) {
return;
@@ -74,7 +88,7 @@
bool isActive = mEventActivationMap.empty();
for (auto& it : mEventActivationMap) {
if (it.second->state == ActivationState::kActive &&
- elapsedTimestampNs > it.second->ttl_ns + it.second->activation_ns) {
+ elapsedTimestampNs > it.second->ttl_ns + it.second->start_ns) {
it.second->state = ActivationState::kNotActive;
}
if (it.second->state == ActivationState::kActive) {
@@ -95,8 +109,8 @@
}
}
-void MetricProducer::addActivation(int activationTrackerIndex, int64_t ttl_seconds,
- int deactivationTrackerIndex) {
+void MetricProducer::addActivation(int activationTrackerIndex, const ActivationType& activationType,
+ int64_t ttl_seconds, int deactivationTrackerIndex) {
std::lock_guard<std::mutex> lock(mMutex);
// When a metric producer does not depend on any activation, its mIsActive is true.
// Therefore, if this is the 1st activation, mIsActive will turn to false. Otherwise it does not
@@ -104,8 +118,8 @@
if (mEventActivationMap.empty()) {
mIsActive = false;
}
- std::shared_ptr<Activation> activation = std::make_shared<Activation>();
- activation->ttl_ns = ttl_seconds * NS_PER_SEC;
+ std::shared_ptr<Activation> activation =
+ std::make_shared<Activation>(activationType, ttl_seconds * NS_PER_SEC);
mEventActivationMap.emplace(activationTrackerIndex, activation);
if (-1 != deactivationTrackerIndex) {
mEventDeactivationMap.emplace(deactivationTrackerIndex, activation);
@@ -117,13 +131,16 @@
if (it == mEventActivationMap.end()) {
return;
}
- if (mActivationType == MetricActivation::ACTIVATE_ON_BOOT &&
- it->second->state == ActivationState::kNotActive) {
- it->second->state = ActivationState::kActiveOnBoot;
+ auto& activation = it->second;
+ if (ACTIVATE_ON_BOOT == activation->activationType) {
+ if (ActivationState::kNotActive == activation->state) {
+ activation->state = ActivationState::kActiveOnBoot;
+ }
+ // If the Activation is already active or set to kActiveOnBoot, do nothing.
return;
}
- it->second->activation_ns = elapsedTimestampNs;
- it->second->state = ActivationState::kActive;
+ activation->start_ns = elapsedTimestampNs;
+ activation->state = ActivationState::kActive;
mIsActive = true;
}
@@ -135,46 +152,55 @@
it->second->state = ActivationState::kNotActive;
}
-void MetricProducer::setActiveLocked(int64_t currentTimeNs, int64_t remainingTtlNs) {
+void MetricProducer::loadActiveMetricLocked(const ActiveMetric& activeMetric,
+ int64_t currentTimeNs) {
if (mEventActivationMap.size() == 0) {
return;
}
- for (auto& pair : mEventActivationMap) {
- auto& activation = pair.second;
- if (activation->ttl_ns >= remainingTtlNs) {
- activation->activation_ns = currentTimeNs + remainingTtlNs - activation->ttl_ns;
- activation->state = kActive;
- mIsActive = true;
- VLOG("setting new activation->time to %lld, %lld, %lld",
- (long long)activation->activation_ns, (long long)currentTimeNs,
- (long long)remainingTtlNs);
- return;
+ for (int i = 0; i < activeMetric.activation_size(); i++) {
+ const auto& activeEventActivation = activeMetric.activation(i);
+ auto it = mEventActivationMap.find(activeEventActivation.atom_matcher_index());
+ if (it == mEventActivationMap.end()) {
+ ALOGE("Saved event activation not found");
+ continue;
}
+ auto& activation = it->second;
+ // We don't want to change the ttl for future activations, so we set the start_ns
+ // such that start_ns + ttl_ns == currentTimeNs + remaining_ttl_nanos
+ activation->start_ns =
+ currentTimeNs + activeEventActivation.remaining_ttl_nanos() - activation->ttl_ns;
+ activation->state = ActivationState::kActive;
+ mIsActive = true;
}
- ALOGE("Required ttl is longer than all possible activations.");
}
-int64_t MetricProducer::getRemainingTtlNsLocked(int64_t currentTimeNs) const {
- int64_t maxTtl = 0;
- for (const auto& activation : mEventActivationMap) {
- if (activation.second->state == kActive) {
- maxTtl = std::max(maxTtl, activation.second->ttl_ns + activation.second->activation_ns -
- currentTimeNs);
- }
- }
- return maxTtl;
-}
+void MetricProducer::writeActiveMetricToProtoOutputStream(
+ int64_t currentTimeNs, ProtoOutputStream* proto) {
+ proto->write(FIELD_TYPE_INT64 | FIELD_ID_ACTIVE_METRIC_ID, (long long)mMetricId);
+ for (auto& it : mEventActivationMap) {
+ const int atom_matcher_index = it.first;
+ const std::shared_ptr<Activation>& activation = it.second;
-void MetricProducer::prepActiveForBootIfNecessaryLocked(int64_t currentTimeNs) {
- if (mActivationType != MetricActivation::ACTIVATE_ON_BOOT) {
- return;
- }
- for (auto& activation : mEventActivationMap) {
- if (activation.second->state == kActiveOnBoot) {
- activation.second->state = kActive;
- activation.second->activation_ns = currentTimeNs;
- mIsActive = true;
+ if (ActivationState::kNotActive == activation->state ||
+ (ActivationState::kActive == activation->state &&
+ activation->start_ns + activation->ttl_ns < currentTimeNs)) {
+ continue;
}
+
+ const uint64_t activationToken = proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED |
+ FIELD_ID_ACTIVE_METRIC_ACTIVATION);
+ proto->write(FIELD_TYPE_INT32 | FIELD_ID_ACTIVE_EVENT_ACTIVATION_ATOM_MATCHER_INDEX,
+ atom_matcher_index);
+ if (ActivationState::kActive == activation->state) {
+ const int64_t remainingTtlNs =
+ activation->start_ns + activation->ttl_ns - currentTimeNs;
+ proto->write(FIELD_TYPE_INT64 | FIELD_ID_ACTIVE_EVENT_ACTIVATION_REMAINING_TTL_NANOS,
+ (long long)remainingTtlNs);
+ } else if (ActivationState::kActiveOnBoot == activation->state) {
+ proto->write(FIELD_TYPE_INT64 | FIELD_ID_ACTIVE_EVENT_ACTIVATION_REMAINING_TTL_NANOS,
+ (long long)activation->ttl_ns);
+ }
+ proto->end(activationToken);
}
}
diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h
index 750566d..7676f59 100644
--- a/cmds/statsd/src/metrics/MetricProducer.h
+++ b/cmds/statsd/src/metrics/MetricProducer.h
@@ -19,6 +19,7 @@
#include <shared_mutex>
+#include <frameworks/base/cmds/statsd/src/active_config_list.pb.h>
#include "HashableDimensionKey.h"
#include "anomaly/AnomalyTracker.h"
#include "condition/ConditionWizard.h"
@@ -198,15 +199,9 @@
return mMetricId;
}
- int64_t getRemainingTtlNs(int64_t currentTimeNs) const {
+ void loadActiveMetric(const ActiveMetric& activeMetric, int64_t currentTimeNs) {
std::lock_guard<std::mutex> lock(mMutex);
- return getRemainingTtlNsLocked(currentTimeNs);
- }
-
- // Set metric to active for ttlNs.
- void setActive(int64_t currentTimeNs, int64_t remainingTtlNs) {
- std::lock_guard<std::mutex> lock(mMutex);
- setActiveLocked(currentTimeNs, remainingTtlNs);
+ loadActiveMetricLocked(activeMetric, currentTimeNs);
}
// Let MetricProducer drop in-memory data to save memory.
@@ -238,17 +233,8 @@
return isActiveLocked();
}
- void prepActiveForBootIfNecessary(int64_t currentTimeNs) {
- std::lock_guard<std::mutex> lock(mMutex);
- prepActiveForBootIfNecessaryLocked(currentTimeNs);
- }
-
- void addActivation(int activationTrackerIndex, int64_t ttl_seconds,
- int deactivationTrackerIndex = -1);
-
- inline void setActivationType(const MetricActivation::ActivationType& activationType) {
- mActivationType = activationType;
- }
+ void addActivation(int activationTrackerIndex, const ActivationType& activationType,
+ int64_t ttl_seconds, int deactivationTrackerIndex = -1);
void prepareFistBucket() {
std::lock_guard<std::mutex> lock(mMutex);
@@ -257,6 +243,8 @@
void flushIfExpire(int64_t elapsedTimestampNs);
+ void writeActiveMetricToProtoOutputStream(
+ int64_t currentTimeNs, ProtoOutputStream* proto);
protected:
virtual void onConditionChangedLocked(const bool condition, const int64_t eventTime) = 0;
virtual void onSlicedConditionMayChangeLocked(bool overallCondition,
@@ -282,9 +270,7 @@
void prepActiveForBootIfNecessaryLocked(int64_t currentTimeNs);
- int64_t getRemainingTtlNsLocked(int64_t currentTimeNs) const;
-
- void setActiveLocked(int64_t currentTimeNs, int64_t remainingTtlNs);
+ void loadActiveMetricLocked(const ActiveMetric& activeMetric, int64_t currentTimeNs);
virtual void prepareFistBucketLocked() {};
/**
@@ -396,11 +382,16 @@
mutable std::mutex mMutex;
struct Activation {
- Activation() : ttl_ns(0), activation_ns(0), state(ActivationState::kNotActive) {}
+ Activation(const ActivationType& activationType, const int64_t ttlNs)
+ : ttl_ns(ttlNs),
+ start_ns(0),
+ state(ActivationState::kNotActive),
+ activationType(activationType) {}
- int64_t ttl_ns;
- int64_t activation_ns;
+ const int64_t ttl_ns;
+ int64_t start_ns;
ActivationState state;
+ const ActivationType activationType;
};
// When the metric producer has multiple activations, these activations are ORed to determine
// whether the metric producer is ready to generate metrics.
@@ -411,8 +402,6 @@
bool mIsActive;
- MetricActivation::ActivationType mActivationType;
-
FRIEND_TEST(MetricActivationE2eTest, TestCountMetric);
FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithOneDeactivation);
FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithTwoDeactivations);
@@ -420,6 +409,9 @@
FRIEND_TEST(StatsLogProcessorTest, TestActiveConfigMetricDiskWriteRead);
FRIEND_TEST(StatsLogProcessorTest, TestActivationOnBoot);
+ FRIEND_TEST(StatsLogProcessorTest, TestActivationOnBootMultipleActivations);
+ FRIEND_TEST(StatsLogProcessorTest,
+ TestActivationOnBootMultipleActivationsDifferentActivationTypes);
};
} // namespace statsd
diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp
index 6a55289..947f377 100644
--- a/cmds/statsd/src/metrics/MetricsManager.cpp
+++ b/cmds/statsd/src/metrics/MetricsManager.cpp
@@ -54,6 +54,11 @@
const int FIELD_ID_ANNOTATIONS_INT64 = 1;
const int FIELD_ID_ANNOTATIONS_INT32 = 2;
+// for ActiveConfig
+const int FIELD_ID_ACTIVE_CONFIG_ID = 1;
+const int FIELD_ID_ACTIVE_CONFIG_UID = 2;
+const int FIELD_ID_ACTIVE_CONFIG_METRIC = 3;
+
MetricsManager::MetricsManager(const ConfigKey& key, const StatsdConfig& config,
const int64_t timeBaseNs, const int64_t currentTimeNs,
const sp<UidMap>& uidMap,
@@ -503,25 +508,41 @@
return totalSize;
}
-void MetricsManager::setActiveMetrics(ActiveConfig config, int64_t currentTimeNs) {
- if (config.active_metric_size() == 0) {
+void MetricsManager::loadActiveConfig(const ActiveConfig& config, int64_t currentTimeNs) {
+ if (config.metric_size() == 0) {
ALOGW("No active metric for config %s", mConfigKey.ToString().c_str());
return;
}
- for (int i = 0; i < config.active_metric_size(); i++) {
- for (int metric : mMetricIndexesWithActivation) {
- if (mAllMetricProducers[metric]->getMetricId() == config.active_metric(i).metric_id()) {
- VLOG("Setting active metric: %lld",
- (long long)mAllMetricProducers[metric]->getMetricId());
- mAllMetricProducers[metric]->setActive(
- currentTimeNs, config.active_metric(i).time_to_live_nanos());
- mIsActive = true;
+ for (int i = 0; i < config.metric_size(); i++) {
+ const auto& activeMetric = config.metric(i);
+ for (int metricIndex : mMetricIndexesWithActivation) {
+ const auto& metric = mAllMetricProducers[metricIndex];
+ if (metric->getMetricId() == activeMetric.id()) {
+ VLOG("Setting active metric: %lld", (long long)metric->getMetricId());
+ metric->loadActiveMetric(activeMetric, currentTimeNs);
+ mIsActive |= metric->isActive();
}
}
}
}
+void MetricsManager::writeActiveConfigToProtoOutputStream(
+ int64_t currentTimeNs, ProtoOutputStream* proto) {
+ proto->write(FIELD_TYPE_INT64 | FIELD_ID_ACTIVE_CONFIG_ID, (long long)mConfigKey.GetId());
+ proto->write(FIELD_TYPE_INT32 | FIELD_ID_ACTIVE_CONFIG_UID, mConfigKey.GetUid());
+ for (int metricIndex : mMetricIndexesWithActivation) {
+ const auto& metric = mAllMetricProducers[metricIndex];
+ const uint64_t metricToken = proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED |
+ FIELD_ID_ACTIVE_CONFIG_METRIC);
+ metric->writeActiveMetricToProtoOutputStream(currentTimeNs, proto);
+ proto->end(metricToken);
+ }
+}
+
+
+
+
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h
index d05bb8b..818131e 100644
--- a/cmds/statsd/src/metrics/MetricsManager.h
+++ b/cmds/statsd/src/metrics/MetricsManager.h
@@ -16,7 +16,6 @@
#pragma once
-#include <frameworks/base/cmds/statsd/src/active_config_list.pb.h>
#include "anomaly/AlarmMonitor.h"
#include "anomaly/AlarmTracker.h"
#include "anomaly/AnomalyTracker.h"
@@ -139,21 +138,10 @@
return mIsActive;
}
- inline void getActiveMetrics(std::vector<MetricProducer*>& metrics) const {
- for (const auto& metric : mAllMetricProducers) {
- if (metric->isActive()) {
- metrics.push_back(metric.get());
- }
- }
- }
+ void loadActiveConfig(const ActiveConfig& config, int64_t currentTimeNs);
- inline void prepForShutDown(int64_t currentTimeNs) {
- for (const auto& metric : mAllMetricProducers) {
- metric->prepActiveForBootIfNecessary(currentTimeNs);
- }
- }
-
- void setActiveMetrics(ActiveConfig config, int64_t currentTimeNs);
+ void writeActiveConfigToProtoOutputStream(
+ int64_t currentTimeNs, ProtoOutputStream* proto);
private:
// For test only.
@@ -299,6 +287,9 @@
FRIEND_TEST(StatsLogProcessorTest, TestActiveConfigMetricDiskWriteRead);
FRIEND_TEST(StatsLogProcessorTest, TestActivationOnBoot);
+ FRIEND_TEST(StatsLogProcessorTest, TestActivationOnBootMultipleActivations);
+ FRIEND_TEST(StatsLogProcessorTest,
+ TestActivationOnBootMultipleActivationsDifferentActivationTypes);
};
} // namespace statsd
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.cpp b/cmds/statsd/src/metrics/metrics_manager_util.cpp
index 31b424e..b027fa0 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.cpp
+++ b/cmds/statsd/src/metrics/metrics_manager_util.cpp
@@ -727,7 +727,6 @@
return false;
}
const sp<MetricProducer>& metric = allMetricProducers[metricTrackerIndex];
- metric->setActivationType(metric_activation.activation_type());
metricsWithActivation.push_back(metricTrackerIndex);
for (int j = 0; j < metric_activation.event_activation_size(); ++j) {
const EventActivation& activation = metric_activation.event_activation(j);
@@ -740,6 +739,13 @@
activationAtomTrackerToMetricMap[atomMatcherIndex].push_back(
metricTrackerIndex);
+ ActivationType activationType;
+ if (activation.has_activation_type()) {
+ activationType = activation.activation_type();
+ } else {
+ activationType = metric_activation.activation_type();
+ }
+
if (activation.has_deactivation_atom_matcher_id()) {
auto deactivationAtomMatcherIt =
logEventTrackerMap.find(activation.deactivation_atom_matcher_id());
@@ -750,10 +756,10 @@
const int deactivationMatcherIndex = deactivationAtomMatcherIt->second;
deactivationAtomTrackerToMetricMap[deactivationMatcherIndex]
.push_back(metricTrackerIndex);
- metric->addActivation(atomMatcherIndex, activation.ttl_seconds(),
+ metric->addActivation(atomMatcherIndex, activationType, activation.ttl_seconds(),
deactivationMatcherIndex);
} else {
- metric->addActivation(atomMatcherIndex, activation.ttl_seconds());
+ metric->addActivation(atomMatcherIndex, activationType, activation.ttl_seconds());
}
}
}
diff --git a/cmds/statsd/src/statsd_config.proto b/cmds/statsd/src/statsd_config.proto
index 2260b9b..4e419b6 100644
--- a/cmds/statsd/src/statsd_config.proto
+++ b/cmds/statsd/src/statsd_config.proto
@@ -377,21 +377,23 @@
optional float probability_of_informing = 7 [default = 1.1];
}
+enum ActivationType {
+ ACTIVATION_TYPE_UNKNOWN = 0;
+ ACTIVATE_IMMEDIATELY = 1;
+ ACTIVATE_ON_BOOT = 2;
+}
+
message EventActivation {
optional int64 atom_matcher_id = 1;
optional int64 ttl_seconds = 2;
optional int64 deactivation_atom_matcher_id = 3;
+ optional ActivationType activation_type = 4;
}
message MetricActivation {
optional int64 metric_id = 1;
- enum ActivationType {
- UNKNOWN = 0;
- ACTIVATE_IMMEDIATELY = 1;
- ACTIVATE_ON_BOOT = 2;
- }
- optional ActivationType activation_type = 3;
+ optional ActivationType activation_type = 3 [deprecated = true];
repeated EventActivation event_activation = 2;
}
diff --git a/cmds/statsd/tests/StatsLogProcessor_test.cpp b/cmds/statsd/tests/StatsLogProcessor_test.cpp
index 91e282a..49b4e90 100644
--- a/cmds/statsd/tests/StatsLogProcessor_test.cpp
+++ b/cmds/statsd/tests/StatsLogProcessor_test.cpp
@@ -339,6 +339,7 @@
auto metric3Activation = config2.add_metric_activation();
metric3Activation->set_metric_id(metricId3);
+ metric3Activation->set_activation_type(ACTIVATE_IMMEDIATELY);
auto metric3ActivationTrigger = metric3Activation->add_event_activation();
metric3ActivationTrigger->set_atom_matcher_id(wakelockAcquireMatcher.id());
metric3ActivationTrigger->set_ttl_seconds(100);
@@ -366,12 +367,14 @@
auto metric5Activation = config3.add_metric_activation();
metric5Activation->set_metric_id(metricId5);
+ metric5Activation->set_activation_type(ACTIVATE_IMMEDIATELY);
auto metric5ActivationTrigger = metric5Activation->add_event_activation();
metric5ActivationTrigger->set_atom_matcher_id(wakelockAcquireMatcher.id());
metric5ActivationTrigger->set_ttl_seconds(100);
auto metric6Activation = config3.add_metric_activation();
metric6Activation->set_metric_id(metricId6);
+ metric6Activation->set_activation_type(ACTIVATE_IMMEDIATELY);
auto metric6ActivationTrigger = metric6Activation->add_event_activation();
metric6ActivationTrigger->set_atom_matcher_id(wakelockAcquireMatcher.id());
metric6ActivationTrigger->set_ttl_seconds(200);
@@ -507,17 +510,13 @@
// When we shut down, metrics 3 & 5 have 100ns remaining, metric 6 has 100s + 100ns.
int64_t shutDownTime = timeBase1 + 100 * NS_PER_SEC;
- EXPECT_TRUE(metricProducer3->isActive());
- int64_t ttl3 = metricProducer3->getRemainingTtlNs(shutDownTime);
- EXPECT_EQ(100, ttl3);
- EXPECT_TRUE(metricProducer5->isActive());
- int64_t ttl5 = metricProducer5->getRemainingTtlNs(shutDownTime);
- EXPECT_EQ(100, ttl5);
- EXPECT_TRUE(metricProducer6->isActive());
- int64_t ttl6 = metricProducer6->getRemainingTtlNs(shutDownTime);
- EXPECT_EQ(100 + 100 * NS_PER_SEC, ttl6);
-
- processor.WriteMetricsActivationToDisk(shutDownTime);
+ processor.SaveActiveConfigsToDisk(shutDownTime);
+ const int64_t ttl3 = event->GetElapsedTimestampNs() +
+ metric3ActivationTrigger->ttl_seconds() * NS_PER_SEC - shutDownTime;
+ const int64_t ttl5 = event->GetElapsedTimestampNs() +
+ metric5ActivationTrigger->ttl_seconds() * NS_PER_SEC - shutDownTime;
+ const int64_t ttl6 = event->GetElapsedTimestampNs() +
+ metric6ActivationTrigger->ttl_seconds() * NS_PER_SEC - shutDownTime;
// Create a second StatsLogProcessor and push the same 3 configs.
long timeBase2 = 1000;
@@ -611,25 +610,25 @@
EXPECT_FALSE(metricProducer1003->isActive());
const auto& activation1003 = metricProducer1003->mEventActivationMap.begin()->second;
EXPECT_EQ(100 * NS_PER_SEC, activation1003->ttl_ns);
- EXPECT_EQ(0, activation1003->activation_ns);
+ EXPECT_EQ(0, activation1003->start_ns);
EXPECT_FALSE(metricProducer1005->isActive());
const auto& activation1005 = metricProducer1005->mEventActivationMap.begin()->second;
EXPECT_EQ(100 * NS_PER_SEC, activation1005->ttl_ns);
- EXPECT_EQ(0, activation1005->activation_ns);
+ EXPECT_EQ(0, activation1005->start_ns);
EXPECT_FALSE(metricProducer1006->isActive());
const auto& activation1006 = metricProducer1006->mEventActivationMap.begin()->second;
EXPECT_EQ(200 * NS_PER_SEC, activation1006->ttl_ns);
- EXPECT_EQ(0, activation1006->activation_ns);
+ EXPECT_EQ(0, activation1006->start_ns);
- processor2->LoadMetricsActivationFromDisk();
+ processor2->LoadActiveConfigsFromDisk();
// After loading activations from disk, assert that all 3 metrics are active.
EXPECT_TRUE(metricProducer1003->isActive());
- EXPECT_EQ(timeBase2 + ttl3 - activation1003->ttl_ns, activation1003->activation_ns);
+ EXPECT_EQ(timeBase2 + ttl3 - activation1003->ttl_ns, activation1003->start_ns);
EXPECT_TRUE(metricProducer1005->isActive());
- EXPECT_EQ(timeBase2 + ttl5 - activation1005->ttl_ns, activation1005->activation_ns);
+ EXPECT_EQ(timeBase2 + ttl5 - activation1005->ttl_ns, activation1005->start_ns);
EXPECT_TRUE(metricProducer1006->isActive());
- EXPECT_EQ(timeBase2 + ttl6 - activation1006->ttl_ns, activation1003->activation_ns);
+ EXPECT_EQ(timeBase2 + ttl6 - activation1006->ttl_ns, activation1003->start_ns);
// Make sure no more broadcasts have happened.
EXPECT_EQ(broadcastCount, 1);
@@ -638,7 +637,6 @@
TEST(StatsLogProcessorTest, TestActivationOnBoot) {
int uid = 1111;
- // Setup a simple config, no activation
StatsdConfig config1;
config1.set_id(12341);
config1.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
@@ -659,7 +657,7 @@
auto metric1Activation = config1.add_metric_activation();
metric1Activation->set_metric_id(metricId1);
- metric1Activation->set_activation_type(MetricActivation::ACTIVATE_ON_BOOT);
+ metric1Activation->set_activation_type(ACTIVATE_ON_BOOT);
auto metric1ActivationTrigger = metric1Activation->add_event_activation();
metric1ActivationTrigger->set_atom_matcher_id(wakelockAcquireMatcher.id());
metric1ActivationTrigger->set_ttl_seconds(100);
@@ -697,7 +695,7 @@
const auto& activation1 = metricProducer1->mEventActivationMap.begin()->second;
EXPECT_EQ(100 * NS_PER_SEC, activation1->ttl_ns);
- EXPECT_EQ(0, activation1->activation_ns);
+ EXPECT_EQ(0, activation1->start_ns);
EXPECT_EQ(kNotActive, activation1->state);
std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(111, "App1")};
@@ -705,15 +703,13 @@
processor->OnLogEvent(event.get());
EXPECT_FALSE(metricProducer1->isActive());
- EXPECT_EQ(0, activation1->activation_ns);
+ EXPECT_EQ(0, activation1->start_ns);
EXPECT_EQ(kActiveOnBoot, activation1->state);
int64_t shutDownTime = timeBase1 + 100 * NS_PER_SEC;
-
- processor->WriteMetricsActivationToDisk(shutDownTime);
- EXPECT_TRUE(metricProducer1->isActive());
- int64_t ttl1 = metricProducer1->getRemainingTtlNs(shutDownTime);
- EXPECT_EQ(100 * NS_PER_SEC, ttl1);
+ processor->SaveActiveConfigsToDisk(shutDownTime);
+ EXPECT_FALSE(metricProducer1->isActive());
+ const int64_t ttl1 = metric1ActivationTrigger->ttl_seconds() * NS_PER_SEC;
long timeBase2 = 1000;
sp<StatsLogProcessor> processor2 =
@@ -747,13 +743,743 @@
const auto& activation1001 = metricProducer1001->mEventActivationMap.begin()->second;
EXPECT_EQ(100 * NS_PER_SEC, activation1001->ttl_ns);
- EXPECT_EQ(0, activation1001->activation_ns);
+ EXPECT_EQ(0, activation1001->start_ns);
EXPECT_EQ(kNotActive, activation1001->state);
- processor2->LoadMetricsActivationFromDisk();
+ processor2->LoadActiveConfigsFromDisk();
EXPECT_TRUE(metricProducer1001->isActive());
- EXPECT_EQ(timeBase2 + ttl1 - activation1001->ttl_ns, activation1001->activation_ns);
+ EXPECT_EQ(timeBase2 + ttl1 - activation1001->ttl_ns, activation1001->start_ns);
+ EXPECT_EQ(kActive, activation1001->state);
+}
+
+TEST(StatsLogProcessorTest, TestActivationOnBootMultipleActivations) {
+ int uid = 1111;
+
+ // Create config with 2 metrics:
+ // Metric 1: Activate on boot with 2 activations
+ // Metric 2: Always active
+ StatsdConfig config1;
+ config1.set_id(12341);
+ config1.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
+ auto wakelockAcquireMatcher = CreateAcquireWakelockAtomMatcher();
+ auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher();
+ *config1.add_atom_matcher() = wakelockAcquireMatcher;
+ *config1.add_atom_matcher() = screenOnMatcher;
+
+ long metricId1 = 1234561;
+ long metricId2 = 1234562;
+
+ auto countMetric1 = config1.add_count_metric();
+ countMetric1->set_id(metricId1);
+ countMetric1->set_what(wakelockAcquireMatcher.id());
+ countMetric1->set_bucket(FIVE_MINUTES);
+
+ auto countMetric2 = config1.add_count_metric();
+ countMetric2->set_id(metricId2);
+ countMetric2->set_what(wakelockAcquireMatcher.id());
+ countMetric2->set_bucket(FIVE_MINUTES);
+
+ auto metric1Activation = config1.add_metric_activation();
+ metric1Activation->set_metric_id(metricId1);
+ metric1Activation->set_activation_type(ACTIVATE_ON_BOOT);
+ auto metric1ActivationTrigger1 = metric1Activation->add_event_activation();
+ metric1ActivationTrigger1->set_atom_matcher_id(wakelockAcquireMatcher.id());
+ metric1ActivationTrigger1->set_ttl_seconds(100);
+ auto metric1ActivationTrigger2 = metric1Activation->add_event_activation();
+ metric1ActivationTrigger2->set_atom_matcher_id(screenOnMatcher.id());
+ metric1ActivationTrigger2->set_ttl_seconds(200);
+
+ ConfigKey cfgKey1(uid, 12341);
+ long timeBase1 = 1;
+ sp<StatsLogProcessor> processor =
+ CreateStatsLogProcessor(timeBase1, timeBase1, config1, cfgKey1);
+
+ // Metric 1 is not active.
+ // Metric 2 is active.
+ // {{{---------------------------------------------------------------------------
+ EXPECT_EQ(1, processor->mMetricsManagers.size());
+ auto it = processor->mMetricsManagers.find(cfgKey1);
+ EXPECT_TRUE(it != processor->mMetricsManagers.end());
+ auto& metricsManager1 = it->second;
+ EXPECT_TRUE(metricsManager1->isActive());
+
+ auto metricIt = metricsManager1->mAllMetricProducers.begin();
+ for (; metricIt != metricsManager1->mAllMetricProducers.end(); metricIt++) {
+ if ((*metricIt)->getMetricId() == metricId1) {
+ break;
+ }
+ }
+ EXPECT_TRUE(metricIt != metricsManager1->mAllMetricProducers.end());
+ auto& metricProducer1 = *metricIt;
+ EXPECT_FALSE(metricProducer1->isActive());
+
+ metricIt = metricsManager1->mAllMetricProducers.begin();
+ for (; metricIt != metricsManager1->mAllMetricProducers.end(); metricIt++) {
+ if ((*metricIt)->getMetricId() == metricId2) {
+ break;
+ }
+ }
+ EXPECT_TRUE(metricIt != metricsManager1->mAllMetricProducers.end());
+ auto& metricProducer2 = *metricIt;
+ EXPECT_TRUE(metricProducer2->isActive());
+
+ int i = 0;
+ for (; i < metricsManager1->mAllAtomMatchers.size(); i++) {
+ if (metricsManager1->mAllAtomMatchers[i]->getId() ==
+ metric1ActivationTrigger1->atom_matcher_id()) {
+ break;
+ }
+ }
+ const auto& activation1 = metricProducer1->mEventActivationMap.at(i);
+ EXPECT_EQ(100 * NS_PER_SEC, activation1->ttl_ns);
+ EXPECT_EQ(0, activation1->start_ns);
+ EXPECT_EQ(kNotActive, activation1->state);
+
+ i = 0;
+ for (; i < metricsManager1->mAllAtomMatchers.size(); i++) {
+ if (metricsManager1->mAllAtomMatchers[i]->getId() ==
+ metric1ActivationTrigger2->atom_matcher_id()) {
+ break;
+ }
+ }
+ const auto& activation2 = metricProducer1->mEventActivationMap.at(i);
+ EXPECT_EQ(200 * NS_PER_SEC, activation2->ttl_ns);
+ EXPECT_EQ(0, activation2->start_ns);
+ EXPECT_EQ(kNotActive, activation2->state);
+ // }}}------------------------------------------------------------------------------
+
+ // Trigger Activation 1 for Metric 1
+ std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(111, "App1")};
+ auto event = CreateAcquireWakelockEvent(attributions1, "wl1", 100 + timeBase1);
+ processor->OnLogEvent(event.get());
+
+ // Metric 1 is not active; Activation 1 set to kActiveOnBoot
+ // Metric 2 is active.
+ // {{{---------------------------------------------------------------------------
+ EXPECT_FALSE(metricProducer1->isActive());
+ EXPECT_EQ(0, activation1->start_ns);
+ EXPECT_EQ(kActiveOnBoot, activation1->state);
+ EXPECT_EQ(0, activation2->start_ns);
+ EXPECT_EQ(kNotActive, activation2->state);
+
+ EXPECT_TRUE(metricProducer2->isActive());
+ // }}}-----------------------------------------------------------------------------
+
+ // Simulate shutdown by saving state to disk
+ int64_t shutDownTime = timeBase1 + 100 * NS_PER_SEC;
+ processor->SaveActiveConfigsToDisk(shutDownTime);
+ EXPECT_FALSE(metricProducer1->isActive());
+ int64_t ttl1 = metric1ActivationTrigger1->ttl_seconds() * NS_PER_SEC;
+
+ // Simulate device restarted state by creating new instance of StatsLogProcessor with the
+ // same config.
+ long timeBase2 = 1000;
+ sp<StatsLogProcessor> processor2 =
+ CreateStatsLogProcessor(timeBase2, timeBase2, config1, cfgKey1);
+
+ // Metric 1 is not active.
+ // Metric 2 is active.
+ // {{{---------------------------------------------------------------------------
+ EXPECT_EQ(1, processor2->mMetricsManagers.size());
+ it = processor2->mMetricsManagers.find(cfgKey1);
+ EXPECT_TRUE(it != processor2->mMetricsManagers.end());
+ auto& metricsManager1001 = it->second;
+ EXPECT_TRUE(metricsManager1001->isActive());
+
+ metricIt = metricsManager1001->mAllMetricProducers.begin();
+ for (; metricIt != metricsManager1001->mAllMetricProducers.end(); metricIt++) {
+ if ((*metricIt)->getMetricId() == metricId1) {
+ break;
+ }
+ }
+ EXPECT_TRUE(metricIt != metricsManager1001->mAllMetricProducers.end());
+ auto& metricProducer1001 = *metricIt;
+ EXPECT_FALSE(metricProducer1001->isActive());
+
+ metricIt = metricsManager1001->mAllMetricProducers.begin();
+ for (; metricIt != metricsManager1001->mAllMetricProducers.end(); metricIt++) {
+ if ((*metricIt)->getMetricId() == metricId2) {
+ break;
+ }
+ }
+ EXPECT_TRUE(metricIt != metricsManager1001->mAllMetricProducers.end());
+ auto& metricProducer1002 = *metricIt;
+ EXPECT_TRUE(metricProducer1002->isActive());
+
+ i = 0;
+ for (; i < metricsManager1001->mAllAtomMatchers.size(); i++) {
+ if (metricsManager1001->mAllAtomMatchers[i]->getId() ==
+ metric1ActivationTrigger1->atom_matcher_id()) {
+ break;
+ }
+ }
+ const auto& activation1001_1 = metricProducer1001->mEventActivationMap.at(i);
+ EXPECT_EQ(100 * NS_PER_SEC, activation1001_1->ttl_ns);
+ EXPECT_EQ(0, activation1001_1->start_ns);
+ EXPECT_EQ(kNotActive, activation1001_1->state);
+
+ i = 0;
+ for (; i < metricsManager1001->mAllAtomMatchers.size(); i++) {
+ if (metricsManager1001->mAllAtomMatchers[i]->getId() ==
+ metric1ActivationTrigger2->atom_matcher_id()) {
+ break;
+ }
+ }
+
+ const auto& activation1001_2 = metricProducer1001->mEventActivationMap.at(i);
+ EXPECT_EQ(200 * NS_PER_SEC, activation1001_2->ttl_ns);
+ EXPECT_EQ(0, activation1001_2->start_ns);
+ EXPECT_EQ(kNotActive, activation1001_2->state);
+ // }}}-----------------------------------------------------------------------------------
+
+ // Load saved state from disk.
+ processor2->LoadActiveConfigsFromDisk();
+
+ // Metric 1 active; Activation 1 is active, Activation 2 is not active
+ // Metric 2 is active.
+ // {{{---------------------------------------------------------------------------
+ EXPECT_TRUE(metricProducer1001->isActive());
+ EXPECT_EQ(timeBase2 + ttl1 - activation1001_1->ttl_ns, activation1001_1->start_ns);
+ EXPECT_EQ(kActive, activation1001_1->state);
+ EXPECT_EQ(0, activation1001_2->start_ns);
+ EXPECT_EQ(kNotActive, activation1001_2->state);
+
+ EXPECT_TRUE(metricProducer1002->isActive());
+ // }}}--------------------------------------------------------------------------------
+
+ // Trigger Activation 2 for Metric 1.
+ auto screenOnEvent = CreateScreenStateChangedEvent(
+ android::view::DISPLAY_STATE_ON,
+ timeBase2 + 200
+ );
+ processor2->OnLogEvent(screenOnEvent.get());
+
+ // Metric 1 active; Activation 1 is active, Activation 2 is set to kActiveOnBoot
+ // Metric 2 is active.
+ // {{{---------------------------------------------------------------------------
+ EXPECT_TRUE(metricProducer1001->isActive());
+ EXPECT_EQ(timeBase2 + ttl1 - activation1001_1->ttl_ns, activation1001_1->start_ns);
+ EXPECT_EQ(kActive, activation1001_1->state);
+ EXPECT_EQ(0, activation1001_2->start_ns);
+ EXPECT_EQ(kActiveOnBoot, activation1001_2->state);
+
+ EXPECT_TRUE(metricProducer1002->isActive());
+ // }}}---------------------------------------------------------------------------
+
+ // Simulate shutdown by saving state to disk
+ shutDownTime = timeBase2 + 50 * NS_PER_SEC;
+ processor2->SaveActiveConfigsToDisk(shutDownTime);
+ EXPECT_TRUE(metricProducer1001->isActive());
+ EXPECT_TRUE(metricProducer1002->isActive());
+ ttl1 = timeBase2 + metric1ActivationTrigger1->ttl_seconds() * NS_PER_SEC - shutDownTime;
+ int64_t ttl2 = metric1ActivationTrigger2->ttl_seconds() * NS_PER_SEC;
+
+ // Simulate device restarted state by creating new instance of StatsLogProcessor with the
+ // same config.
+ long timeBase3 = timeBase2 + 120 * NS_PER_SEC;
+ sp<StatsLogProcessor> processor3 =
+ CreateStatsLogProcessor(timeBase3, timeBase3, config1, cfgKey1);
+
+ // Metric 1 is not active.
+ // Metric 2 is active.
+ // {{{---------------------------------------------------------------------------
+ EXPECT_EQ(1, processor3->mMetricsManagers.size());
+ it = processor3->mMetricsManagers.find(cfgKey1);
+ EXPECT_TRUE(it != processor3->mMetricsManagers.end());
+ auto& metricsManagerTimeBase3 = it->second;
+ EXPECT_TRUE(metricsManagerTimeBase3->isActive());
+
+ metricIt = metricsManagerTimeBase3->mAllMetricProducers.begin();
+ for (; metricIt != metricsManagerTimeBase3->mAllMetricProducers.end(); metricIt++) {
+ if ((*metricIt)->getMetricId() == metricId1) {
+ break;
+ }
+ }
+ EXPECT_TRUE(metricIt != metricsManagerTimeBase3->mAllMetricProducers.end());
+ auto& metricProducerTimeBase3_1 = *metricIt;
+ EXPECT_FALSE(metricProducerTimeBase3_1->isActive());
+
+ metricIt = metricsManagerTimeBase3->mAllMetricProducers.begin();
+ for (; metricIt != metricsManagerTimeBase3->mAllMetricProducers.end(); metricIt++) {
+ if ((*metricIt)->getMetricId() == metricId2) {
+ break;
+ }
+ }
+ EXPECT_TRUE(metricIt != metricsManagerTimeBase3->mAllMetricProducers.end());
+ auto& metricProducerTimeBase3_2 = *metricIt;
+ EXPECT_TRUE(metricProducerTimeBase3_2->isActive());
+
+ i = 0;
+ for (; i < metricsManagerTimeBase3->mAllAtomMatchers.size(); i++) {
+ if (metricsManagerTimeBase3->mAllAtomMatchers[i]->getId() ==
+ metric1ActivationTrigger1->atom_matcher_id()) {
+ break;
+ }
+ }
+ const auto& activationTimeBase3_1 = metricProducerTimeBase3_1->mEventActivationMap.at(i);
+ EXPECT_EQ(100 * NS_PER_SEC, activationTimeBase3_1->ttl_ns);
+ EXPECT_EQ(0, activationTimeBase3_1->start_ns);
+ EXPECT_EQ(kNotActive, activationTimeBase3_1->state);
+
+ i = 0;
+ for (; i < metricsManagerTimeBase3->mAllAtomMatchers.size(); i++) {
+ if (metricsManagerTimeBase3->mAllAtomMatchers[i]->getId() ==
+ metric1ActivationTrigger2->atom_matcher_id()) {
+ break;
+ }
+ }
+
+ const auto& activationTimeBase3_2 = metricProducerTimeBase3_1->mEventActivationMap.at(i);
+ EXPECT_EQ(200 * NS_PER_SEC, activationTimeBase3_2->ttl_ns);
+ EXPECT_EQ(0, activationTimeBase3_2->start_ns);
+ EXPECT_EQ(kNotActive, activationTimeBase3_2->state);
+
+ EXPECT_TRUE(metricProducerTimeBase3_2->isActive());
+ // }}}----------------------------------------------------------------------------------
+
+ // Load saved state from disk.
+ processor3->LoadActiveConfigsFromDisk();
+
+ // Metric 1 active: Activation 1 is active, Activation 2 is active
+ // Metric 2 is active.
+ // {{{---------------------------------------------------------------------------
+ EXPECT_TRUE(metricProducerTimeBase3_1->isActive());
+ EXPECT_EQ(timeBase3 + ttl1 - activationTimeBase3_1->ttl_ns, activationTimeBase3_1->start_ns);
+ EXPECT_EQ(kActive, activationTimeBase3_1->state);
+ EXPECT_EQ(timeBase3 + ttl2 - activationTimeBase3_2->ttl_ns, activationTimeBase3_2->start_ns);
+ EXPECT_EQ(kActive, activationTimeBase3_2->state);
+
+ EXPECT_TRUE(metricProducerTimeBase3_2->isActive());
+ // }}}-------------------------------------------------------------------------------
+
+ // Trigger Activation 2 for Metric 1 again.
+ screenOnEvent = CreateScreenStateChangedEvent(
+ android::view::DISPLAY_STATE_ON,
+ timeBase3 + 100 * NS_PER_SEC
+ );
+ processor3->OnLogEvent(screenOnEvent.get());
+
+ // Metric 1 active; Activation 1 is not active, Activation 2 is set to active
+ // Metric 2 is active.
+ // {{{---------------------------------------------------------------------------
+ EXPECT_TRUE(metricProducerTimeBase3_1->isActive());
+ EXPECT_EQ(kNotActive, activationTimeBase3_1->state);
+ EXPECT_EQ(timeBase3 + ttl2 - activationTimeBase3_2->ttl_ns, activationTimeBase3_2->start_ns);
+ EXPECT_EQ(kActive, activationTimeBase3_2->state);
+
+ EXPECT_TRUE(metricProducerTimeBase3_2->isActive());
+ // }}}---------------------------------------------------------------------------
+
+ // Simulate shutdown by saving state to disk.
+ shutDownTime = timeBase3 + 500 * NS_PER_SEC;
+ processor3->SaveActiveConfigsToDisk(shutDownTime);
+ EXPECT_TRUE(metricProducer1001->isActive());
+ EXPECT_TRUE(metricProducer1002->isActive());
+ ttl1 = timeBase3 + ttl1 - shutDownTime;
+ ttl2 = timeBase3 + metric1ActivationTrigger2->ttl_seconds() * NS_PER_SEC - shutDownTime;
+
+ // Simulate device restarted state by creating new instance of StatsLogProcessor with the
+ // same config.
+ long timeBase4 = timeBase3 + 600 * NS_PER_SEC;
+ sp<StatsLogProcessor> processor4 =
+ CreateStatsLogProcessor(timeBase4, timeBase4, config1, cfgKey1);
+
+ // Metric 1 is not active.
+ // Metric 2 is active.
+ // {{{---------------------------------------------------------------------------
+ EXPECT_EQ(1, processor4->mMetricsManagers.size());
+ it = processor4->mMetricsManagers.find(cfgKey1);
+ EXPECT_TRUE(it != processor4->mMetricsManagers.end());
+ auto& metricsManagerTimeBase4 = it->second;
+ EXPECT_TRUE(metricsManagerTimeBase4->isActive());
+
+ metricIt = metricsManagerTimeBase4->mAllMetricProducers.begin();
+ for (; metricIt != metricsManagerTimeBase4->mAllMetricProducers.end(); metricIt++) {
+ if ((*metricIt)->getMetricId() == metricId1) {
+ break;
+ }
+ }
+ EXPECT_TRUE(metricIt != metricsManagerTimeBase4->mAllMetricProducers.end());
+ auto& metricProducerTimeBase4_1 = *metricIt;
+ EXPECT_FALSE(metricProducerTimeBase4_1->isActive());
+
+ metricIt = metricsManagerTimeBase4->mAllMetricProducers.begin();
+ for (; metricIt != metricsManagerTimeBase4->mAllMetricProducers.end(); metricIt++) {
+ if ((*metricIt)->getMetricId() == metricId2) {
+ break;
+ }
+ }
+ EXPECT_TRUE(metricIt != metricsManagerTimeBase4->mAllMetricProducers.end());
+ auto& metricProducerTimeBase4_2 = *metricIt;
+ EXPECT_TRUE(metricProducerTimeBase4_2->isActive());
+
+ i = 0;
+ for (; i < metricsManagerTimeBase4->mAllAtomMatchers.size(); i++) {
+ if (metricsManagerTimeBase4->mAllAtomMatchers[i]->getId() ==
+ metric1ActivationTrigger1->atom_matcher_id()) {
+ break;
+ }
+ }
+ const auto& activationTimeBase4_1 = metricProducerTimeBase4_1->mEventActivationMap.at(i);
+ EXPECT_EQ(100 * NS_PER_SEC, activationTimeBase4_1->ttl_ns);
+ EXPECT_EQ(0, activationTimeBase4_1->start_ns);
+ EXPECT_EQ(kNotActive, activationTimeBase4_1->state);
+
+ i = 0;
+ for (; i < metricsManagerTimeBase4->mAllAtomMatchers.size(); i++) {
+ if (metricsManagerTimeBase4->mAllAtomMatchers[i]->getId() ==
+ metric1ActivationTrigger2->atom_matcher_id()) {
+ break;
+ }
+ }
+
+ const auto& activationTimeBase4_2 = metricProducerTimeBase4_1->mEventActivationMap.at(i);
+ EXPECT_EQ(200 * NS_PER_SEC, activationTimeBase4_2->ttl_ns);
+ EXPECT_EQ(0, activationTimeBase4_2->start_ns);
+ EXPECT_EQ(kNotActive, activationTimeBase4_2->state);
+
+ EXPECT_TRUE(metricProducerTimeBase4_2->isActive());
+ // }}}----------------------------------------------------------------------------------
+
+ // Load saved state from disk.
+ processor4->LoadActiveConfigsFromDisk();
+
+ // Metric 1 active: Activation 1 is not active, Activation 2 is not active
+ // Metric 2 is active.
+ // {{{---------------------------------------------------------------------------
+ EXPECT_FALSE(metricProducerTimeBase4_1->isActive());
+ EXPECT_EQ(kNotActive, activationTimeBase4_1->state);
+ EXPECT_EQ(kNotActive, activationTimeBase4_2->state);
+
+ EXPECT_TRUE(metricProducerTimeBase4_2->isActive());
+ // }}}-------------------------------------------------------------------------------
+}
+
+TEST(StatsLogProcessorTest, TestActivationOnBootMultipleActivationsDifferentActivationTypes) {
+ int uid = 1111;
+
+ // Create config with 2 metrics:
+ // Metric 1: Activate on boot with 2 activations
+ // Metric 2: Always active
+ StatsdConfig config1;
+ config1.set_id(12341);
+ config1.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
+ auto wakelockAcquireMatcher = CreateAcquireWakelockAtomMatcher();
+ auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher();
+ *config1.add_atom_matcher() = wakelockAcquireMatcher;
+ *config1.add_atom_matcher() = screenOnMatcher;
+
+ long metricId1 = 1234561;
+ long metricId2 = 1234562;
+
+ auto countMetric1 = config1.add_count_metric();
+ countMetric1->set_id(metricId1);
+ countMetric1->set_what(wakelockAcquireMatcher.id());
+ countMetric1->set_bucket(FIVE_MINUTES);
+
+ auto countMetric2 = config1.add_count_metric();
+ countMetric2->set_id(metricId2);
+ countMetric2->set_what(wakelockAcquireMatcher.id());
+ countMetric2->set_bucket(FIVE_MINUTES);
+
+ auto metric1Activation = config1.add_metric_activation();
+ metric1Activation->set_metric_id(metricId1);
+ metric1Activation->set_activation_type(ACTIVATE_ON_BOOT);
+ auto metric1ActivationTrigger1 = metric1Activation->add_event_activation();
+ metric1ActivationTrigger1->set_atom_matcher_id(wakelockAcquireMatcher.id());
+ metric1ActivationTrigger1->set_ttl_seconds(100);
+ auto metric1ActivationTrigger2 = metric1Activation->add_event_activation();
+ metric1ActivationTrigger2->set_atom_matcher_id(screenOnMatcher.id());
+ metric1ActivationTrigger2->set_ttl_seconds(200);
+ metric1ActivationTrigger2->set_activation_type(ACTIVATE_IMMEDIATELY);
+
+ ConfigKey cfgKey1(uid, 12341);
+ long timeBase1 = 1;
+ sp<StatsLogProcessor> processor =
+ CreateStatsLogProcessor(timeBase1, timeBase1, config1, cfgKey1);
+
+ // Metric 1 is not active.
+ // Metric 2 is active.
+ // {{{---------------------------------------------------------------------------
+ EXPECT_EQ(1, processor->mMetricsManagers.size());
+ auto it = processor->mMetricsManagers.find(cfgKey1);
+ EXPECT_TRUE(it != processor->mMetricsManagers.end());
+ auto& metricsManager1 = it->second;
+ EXPECT_TRUE(metricsManager1->isActive());
+
+ auto metricIt = metricsManager1->mAllMetricProducers.begin();
+ for (; metricIt != metricsManager1->mAllMetricProducers.end(); metricIt++) {
+ if ((*metricIt)->getMetricId() == metricId1) {
+ break;
+ }
+ }
+ EXPECT_TRUE(metricIt != metricsManager1->mAllMetricProducers.end());
+ auto& metricProducer1 = *metricIt;
+ EXPECT_FALSE(metricProducer1->isActive());
+
+ metricIt = metricsManager1->mAllMetricProducers.begin();
+ for (; metricIt != metricsManager1->mAllMetricProducers.end(); metricIt++) {
+ if ((*metricIt)->getMetricId() == metricId2) {
+ break;
+ }
+ }
+ EXPECT_TRUE(metricIt != metricsManager1->mAllMetricProducers.end());
+ auto& metricProducer2 = *metricIt;
+ EXPECT_TRUE(metricProducer2->isActive());
+
+ int i = 0;
+ for (; i < metricsManager1->mAllAtomMatchers.size(); i++) {
+ if (metricsManager1->mAllAtomMatchers[i]->getId() ==
+ metric1ActivationTrigger1->atom_matcher_id()) {
+ break;
+ }
+ }
+ const auto& activation1 = metricProducer1->mEventActivationMap.at(i);
+ EXPECT_EQ(100 * NS_PER_SEC, activation1->ttl_ns);
+ EXPECT_EQ(0, activation1->start_ns);
+ EXPECT_EQ(kNotActive, activation1->state);
+ EXPECT_EQ(ACTIVATE_ON_BOOT, activation1->activationType);
+
+ i = 0;
+ for (; i < metricsManager1->mAllAtomMatchers.size(); i++) {
+ if (metricsManager1->mAllAtomMatchers[i]->getId() ==
+ metric1ActivationTrigger2->atom_matcher_id()) {
+ break;
+ }
+ }
+ const auto& activation2 = metricProducer1->mEventActivationMap.at(i);
+ EXPECT_EQ(200 * NS_PER_SEC, activation2->ttl_ns);
+ EXPECT_EQ(0, activation2->start_ns);
+ EXPECT_EQ(kNotActive, activation2->state);
+ EXPECT_EQ(ACTIVATE_IMMEDIATELY, activation2->activationType);
+ // }}}------------------------------------------------------------------------------
+
+ // Trigger Activation 1 for Metric 1
+ std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(111, "App1")};
+ auto event = CreateAcquireWakelockEvent(attributions1, "wl1", 100 + timeBase1);
+ processor->OnLogEvent(event.get());
+
+ // Metric 1 is not active; Activation 1 set to kActiveOnBoot
+ // Metric 2 is active.
+ // {{{---------------------------------------------------------------------------
+ EXPECT_FALSE(metricProducer1->isActive());
+ EXPECT_EQ(0, activation1->start_ns);
+ EXPECT_EQ(kActiveOnBoot, activation1->state);
+ EXPECT_EQ(0, activation2->start_ns);
+ EXPECT_EQ(kNotActive, activation2->state);
+
+ EXPECT_TRUE(metricProducer2->isActive());
+ // }}}-----------------------------------------------------------------------------
+
+ // Simulate shutdown by saving state to disk
+ int64_t shutDownTime = timeBase1 + 100 * NS_PER_SEC;
+ processor->SaveActiveConfigsToDisk(shutDownTime);
+ EXPECT_FALSE(metricProducer1->isActive());
+ int64_t ttl1 = metric1ActivationTrigger1->ttl_seconds() * NS_PER_SEC;
+
+ // Simulate device restarted state by creating new instance of StatsLogProcessor with the
+ // same config.
+ long timeBase2 = 1000;
+ sp<StatsLogProcessor> processor2 =
+ CreateStatsLogProcessor(timeBase2, timeBase2, config1, cfgKey1);
+
+ // Metric 1 is not active.
+ // Metric 2 is active.
+ // {{{---------------------------------------------------------------------------
+ EXPECT_EQ(1, processor2->mMetricsManagers.size());
+ it = processor2->mMetricsManagers.find(cfgKey1);
+ EXPECT_TRUE(it != processor2->mMetricsManagers.end());
+ auto& metricsManager1001 = it->second;
+ EXPECT_TRUE(metricsManager1001->isActive());
+
+ metricIt = metricsManager1001->mAllMetricProducers.begin();
+ for (; metricIt != metricsManager1001->mAllMetricProducers.end(); metricIt++) {
+ if ((*metricIt)->getMetricId() == metricId1) {
+ break;
+ }
+ }
+ EXPECT_TRUE(metricIt != metricsManager1001->mAllMetricProducers.end());
+ auto& metricProducer1001 = *metricIt;
+ EXPECT_FALSE(metricProducer1001->isActive());
+
+ metricIt = metricsManager1001->mAllMetricProducers.begin();
+ for (; metricIt != metricsManager1001->mAllMetricProducers.end(); metricIt++) {
+ if ((*metricIt)->getMetricId() == metricId2) {
+ break;
+ }
+ }
+ EXPECT_TRUE(metricIt != metricsManager1001->mAllMetricProducers.end());
+ auto& metricProducer1002 = *metricIt;
+ EXPECT_TRUE(metricProducer1002->isActive());
+
+ i = 0;
+ for (; i < metricsManager1001->mAllAtomMatchers.size(); i++) {
+ if (metricsManager1001->mAllAtomMatchers[i]->getId() ==
+ metric1ActivationTrigger1->atom_matcher_id()) {
+ break;
+ }
+ }
+ const auto& activation1001_1 = metricProducer1001->mEventActivationMap.at(i);
+ EXPECT_EQ(100 * NS_PER_SEC, activation1001_1->ttl_ns);
+ EXPECT_EQ(0, activation1001_1->start_ns);
+ EXPECT_EQ(kNotActive, activation1001_1->state);
+ EXPECT_EQ(ACTIVATE_ON_BOOT, activation1001_1->activationType);
+
+ i = 0;
+ for (; i < metricsManager1001->mAllAtomMatchers.size(); i++) {
+ if (metricsManager1001->mAllAtomMatchers[i]->getId() ==
+ metric1ActivationTrigger2->atom_matcher_id()) {
+ break;
+ }
+ }
+
+ const auto& activation1001_2 = metricProducer1001->mEventActivationMap.at(i);
+ EXPECT_EQ(200 * NS_PER_SEC, activation1001_2->ttl_ns);
+ EXPECT_EQ(0, activation1001_2->start_ns);
+ EXPECT_EQ(kNotActive, activation1001_2->state);
+ EXPECT_EQ(ACTIVATE_IMMEDIATELY, activation1001_2->activationType);
+ // }}}-----------------------------------------------------------------------------------
+
+ // Load saved state from disk.
+ processor2->LoadActiveConfigsFromDisk();
+
+ // Metric 1 active; Activation 1 is active, Activation 2 is not active
+ // Metric 2 is active.
+ // {{{---------------------------------------------------------------------------
+ EXPECT_TRUE(metricProducer1001->isActive());
+ EXPECT_EQ(timeBase2 + ttl1 - activation1001_1->ttl_ns, activation1001_1->start_ns);
+ EXPECT_EQ(kActive, activation1001_1->state);
+ EXPECT_EQ(0, activation1001_2->start_ns);
+ EXPECT_EQ(kNotActive, activation1001_2->state);
+
+ EXPECT_TRUE(metricProducer1002->isActive());
+ // }}}--------------------------------------------------------------------------------
+
+ // Trigger Activation 2 for Metric 1.
+ auto screenOnEvent = CreateScreenStateChangedEvent(
+ android::view::DISPLAY_STATE_ON,
+ timeBase2 + 200
+ );
+ processor2->OnLogEvent(screenOnEvent.get());
+
+ // Metric 1 active; Activation 1 is active, Activation 2 is active
+ // Metric 2 is active.
+ // {{{---------------------------------------------------------------------------
+ EXPECT_TRUE(metricProducer1001->isActive());
+ EXPECT_EQ(timeBase2 + ttl1 - activation1001_1->ttl_ns, activation1001_1->start_ns);
+ EXPECT_EQ(kActive, activation1001_1->state);
+ EXPECT_EQ(screenOnEvent->GetElapsedTimestampNs(), activation1001_2->start_ns);
+ EXPECT_EQ(kActive, activation1001_2->state);
+
+ EXPECT_TRUE(metricProducer1002->isActive());
+ // }}}---------------------------------------------------------------------------
+
+ // Simulate shutdown by saving state to disk
+ shutDownTime = timeBase2 + 50 * NS_PER_SEC;
+ processor2->SaveActiveConfigsToDisk(shutDownTime);
+ EXPECT_TRUE(metricProducer1001->isActive());
+ EXPECT_TRUE(metricProducer1002->isActive());
+ ttl1 = timeBase2 + metric1ActivationTrigger1->ttl_seconds() * NS_PER_SEC - shutDownTime;
+ int64_t ttl2 = screenOnEvent->GetElapsedTimestampNs() +
+ metric1ActivationTrigger2->ttl_seconds() * NS_PER_SEC - shutDownTime;
+
+ // Simulate device restarted state by creating new instance of StatsLogProcessor with the
+ // same config.
+ long timeBase3 = timeBase2 + 120 * NS_PER_SEC;
+ sp<StatsLogProcessor> processor3 =
+ CreateStatsLogProcessor(timeBase3, timeBase3, config1, cfgKey1);
+
+ // Metric 1 is not active.
+ // Metric 2 is active.
+ // {{{---------------------------------------------------------------------------
+ EXPECT_EQ(1, processor3->mMetricsManagers.size());
+ it = processor3->mMetricsManagers.find(cfgKey1);
+ EXPECT_TRUE(it != processor3->mMetricsManagers.end());
+ auto& metricsManagerTimeBase3 = it->second;
+ EXPECT_TRUE(metricsManagerTimeBase3->isActive());
+
+ metricIt = metricsManagerTimeBase3->mAllMetricProducers.begin();
+ for (; metricIt != metricsManagerTimeBase3->mAllMetricProducers.end(); metricIt++) {
+ if ((*metricIt)->getMetricId() == metricId1) {
+ break;
+ }
+ }
+ EXPECT_TRUE(metricIt != metricsManagerTimeBase3->mAllMetricProducers.end());
+ auto& metricProducerTimeBase3_1 = *metricIt;
+ EXPECT_FALSE(metricProducerTimeBase3_1->isActive());
+
+ metricIt = metricsManagerTimeBase3->mAllMetricProducers.begin();
+ for (; metricIt != metricsManagerTimeBase3->mAllMetricProducers.end(); metricIt++) {
+ if ((*metricIt)->getMetricId() == metricId2) {
+ break;
+ }
+ }
+ EXPECT_TRUE(metricIt != metricsManagerTimeBase3->mAllMetricProducers.end());
+ auto& metricProducerTimeBase3_2 = *metricIt;
+ EXPECT_TRUE(metricProducerTimeBase3_2->isActive());
+
+ i = 0;
+ for (; i < metricsManagerTimeBase3->mAllAtomMatchers.size(); i++) {
+ if (metricsManagerTimeBase3->mAllAtomMatchers[i]->getId() ==
+ metric1ActivationTrigger1->atom_matcher_id()) {
+ break;
+ }
+ }
+ const auto& activationTimeBase3_1 = metricProducerTimeBase3_1->mEventActivationMap.at(i);
+ EXPECT_EQ(100 * NS_PER_SEC, activationTimeBase3_1->ttl_ns);
+ EXPECT_EQ(0, activationTimeBase3_1->start_ns);
+ EXPECT_EQ(kNotActive, activationTimeBase3_1->state);
+ EXPECT_EQ(ACTIVATE_ON_BOOT, activationTimeBase3_1->activationType);
+
+ i = 0;
+ for (; i < metricsManagerTimeBase3->mAllAtomMatchers.size(); i++) {
+ if (metricsManagerTimeBase3->mAllAtomMatchers[i]->getId() ==
+ metric1ActivationTrigger2->atom_matcher_id()) {
+ break;
+ }
+ }
+
+ const auto& activationTimeBase3_2 = metricProducerTimeBase3_1->mEventActivationMap.at(i);
+ EXPECT_EQ(200 * NS_PER_SEC, activationTimeBase3_2->ttl_ns);
+ EXPECT_EQ(0, activationTimeBase3_2->start_ns);
+ EXPECT_EQ(kNotActive, activationTimeBase3_2->state);
+ EXPECT_EQ(ACTIVATE_IMMEDIATELY, activationTimeBase3_2->activationType);
+ // }}}----------------------------------------------------------------------------------
+
+ // Load saved state from disk.
+ processor3->LoadActiveConfigsFromDisk();
+
+ // Metric 1 active: Activation 1 is active, Activation 2 is active
+ // Metric 2 is active.
+ // {{{---------------------------------------------------------------------------
+ EXPECT_TRUE(metricProducerTimeBase3_1->isActive());
+ EXPECT_EQ(timeBase3 + ttl1 - activationTimeBase3_1->ttl_ns, activationTimeBase3_1->start_ns);
+ EXPECT_EQ(kActive, activationTimeBase3_1->state);
+ EXPECT_EQ(timeBase3 + ttl2 - activationTimeBase3_2->ttl_ns, activationTimeBase3_2->start_ns);
+ EXPECT_EQ(kActive, activationTimeBase3_2->state);
+
+ EXPECT_TRUE(metricProducerTimeBase3_2->isActive());
+ // }}}-------------------------------------------------------------------------------
+
+
+ // Trigger Activation 2 for Metric 1 again.
+ screenOnEvent = CreateScreenStateChangedEvent(
+ android::view::DISPLAY_STATE_ON,
+ timeBase3 + 100 * NS_PER_SEC
+ );
+ processor3->OnLogEvent(screenOnEvent.get());
+
+ // Metric 1 active; Activation 1 is not active, Activation 2 is set to active
+ // Metric 2 is active.
+ // {{{---------------------------------------------------------------------------
+ EXPECT_TRUE(metricProducerTimeBase3_1->isActive());
+ EXPECT_EQ(kNotActive, activationTimeBase3_1->state);
+ EXPECT_EQ(screenOnEvent->GetElapsedTimestampNs(), activationTimeBase3_2->start_ns);
+ EXPECT_EQ(kActive, activationTimeBase3_2->state);
+
+ EXPECT_TRUE(metricProducerTimeBase3_2->isActive());
+ // }}}---------------------------------------------------------------------------
}
#else
diff --git a/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp b/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp
index f01ad06..6ec0a11 100644
--- a/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp
+++ b/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp
@@ -418,7 +418,7 @@
const int64_t ttlNs = 2 * bucketSizeNs; // Two buckets.
auto metric_activation = config.add_metric_activation();
metric_activation->set_metric_id(metricId);
- metric_activation->set_activation_type(MetricActivation::ACTIVATE_IMMEDIATELY);
+ metric_activation->set_activation_type(ACTIVATE_IMMEDIATELY);
auto event_activation = metric_activation->add_event_activation();
event_activation->set_atom_matcher_id(batterySaverStartMatcher.id());
event_activation->set_ttl_seconds(ttlNs / 1000000000);
diff --git a/cmds/statsd/tests/e2e/MetricActivation_e2e_test.cpp b/cmds/statsd/tests/e2e/MetricActivation_e2e_test.cpp
index bf52bb0..d99d281 100644
--- a/cmds/statsd/tests/e2e/MetricActivation_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/MetricActivation_e2e_test.cpp
@@ -245,10 +245,10 @@
EXPECT_TRUE(eventActivationMap.find(0) != eventActivationMap.end());
EXPECT_TRUE(eventActivationMap.find(2) != eventActivationMap.end());
EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap[0]->activation_ns, 0);
+ EXPECT_EQ(eventActivationMap[0]->start_ns, 0);
EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap[2]->activation_ns, 0);
+ EXPECT_EQ(eventActivationMap[2]->start_ns, 0);
EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
std::unique_ptr<LogEvent> event;
@@ -268,10 +268,10 @@
EXPECT_EQ(activeConfigsBroadcast.size(), 1);
EXPECT_EQ(activeConfigsBroadcast[0], cfgId);
EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive);
- EXPECT_EQ(eventActivationMap[0]->activation_ns, bucketStartTimeNs + 10);
+ EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10);
EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap[2]->activation_ns, 0);
+ EXPECT_EQ(eventActivationMap[2]->start_ns, 0);
EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
// First processed event.
@@ -285,10 +285,10 @@
EXPECT_TRUE(metricsManager->isActive());
EXPECT_TRUE(metricProducer->mIsActive);
EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive);
- EXPECT_EQ(eventActivationMap[0]->activation_ns, bucketStartTimeNs + 10);
+ EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10);
EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive);
- EXPECT_EQ(eventActivationMap[2]->activation_ns, bucketStartTimeNs + 20);
+ EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20);
EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
// 2nd processed event.
@@ -298,10 +298,10 @@
EXPECT_TRUE(metricsManager->isActive());
EXPECT_TRUE(metricProducer->mIsActive);
EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive);
- EXPECT_EQ(eventActivationMap[0]->activation_ns, bucketStartTimeNs + 10);
+ EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10);
EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap[2]->activation_ns, bucketStartTimeNs + 20);
+ EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20);
EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
// No new broadcast since the config should still be active.
EXPECT_EQ(broadcastCount, 1);
@@ -319,10 +319,10 @@
EXPECT_EQ(broadcastCount, 2);
EXPECT_EQ(activeConfigsBroadcast.size(), 0);
EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap[0]->activation_ns, bucketStartTimeNs + 10);
+ EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10);
EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap[2]->activation_ns, bucketStartTimeNs + 20);
+ EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20);
EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
// Re-activate metric via screen on.
@@ -335,10 +335,10 @@
EXPECT_EQ(activeConfigsBroadcast.size(), 1);
EXPECT_EQ(activeConfigsBroadcast[0], cfgId);
EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap[0]->activation_ns, bucketStartTimeNs + 10);
+ EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10);
EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive);
- EXPECT_EQ(eventActivationMap[2]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
+ EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
// 4th processed event.
@@ -460,10 +460,10 @@
EXPECT_TRUE(eventActivationMap.find(0) != eventActivationMap.end());
EXPECT_TRUE(eventActivationMap.find(2) != eventActivationMap.end());
EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap[0]->activation_ns, 0);
+ EXPECT_EQ(eventActivationMap[0]->start_ns, 0);
EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap[2]->activation_ns, 0);
+ EXPECT_EQ(eventActivationMap[2]->start_ns, 0);
EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
EXPECT_EQ(eventDeactivationMap.size(), 1u);
EXPECT_TRUE(eventDeactivationMap.find(3) != eventDeactivationMap.end());
@@ -486,10 +486,10 @@
EXPECT_EQ(activeConfigsBroadcast.size(), 1);
EXPECT_EQ(activeConfigsBroadcast[0], cfgId);
EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive);
- EXPECT_EQ(eventActivationMap[0]->activation_ns, bucketStartTimeNs + 10);
+ EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10);
EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap[2]->activation_ns, 0);
+ EXPECT_EQ(eventActivationMap[2]->start_ns, 0);
EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
EXPECT_EQ(eventDeactivationMap[3], eventActivationMap[0]);
@@ -504,10 +504,10 @@
EXPECT_TRUE(metricsManager->isActive());
EXPECT_TRUE(metricProducer->mIsActive);
EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive);
- EXPECT_EQ(eventActivationMap[0]->activation_ns, bucketStartTimeNs + 10);
+ EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10);
EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive);
- EXPECT_EQ(eventActivationMap[2]->activation_ns, bucketStartTimeNs + 20);
+ EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20);
EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
EXPECT_EQ(eventDeactivationMap[3], eventActivationMap[0]);
@@ -518,10 +518,10 @@
EXPECT_TRUE(metricsManager->isActive());
EXPECT_TRUE(metricProducer->mIsActive);
EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive);
- EXPECT_EQ(eventActivationMap[0]->activation_ns, bucketStartTimeNs + 10);
+ EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10);
EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap[2]->activation_ns, bucketStartTimeNs + 20);
+ EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20);
EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
EXPECT_EQ(eventDeactivationMap[3], eventActivationMap[0]);
// No new broadcast since the config should still be active.
@@ -540,10 +540,10 @@
EXPECT_EQ(broadcastCount, 2);
EXPECT_EQ(activeConfigsBroadcast.size(), 0);
EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap[0]->activation_ns, bucketStartTimeNs + 10);
+ EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10);
EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap[2]->activation_ns, bucketStartTimeNs + 20);
+ EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20);
EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
EXPECT_EQ(eventDeactivationMap[3], eventActivationMap[0]);
@@ -557,10 +557,10 @@
EXPECT_EQ(activeConfigsBroadcast.size(), 1);
EXPECT_EQ(activeConfigsBroadcast[0], cfgId);
EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap[0]->activation_ns, bucketStartTimeNs + 10);
+ EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10);
EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive);
- EXPECT_EQ(eventActivationMap[2]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
+ EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
EXPECT_EQ(eventDeactivationMap[3], eventActivationMap[0]);
@@ -577,10 +577,10 @@
EXPECT_EQ(activeConfigsBroadcast.size(), 1);
EXPECT_EQ(activeConfigsBroadcast[0], cfgId);
EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive);
- EXPECT_EQ(eventActivationMap[0]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15);
+ EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15);
EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive);
- EXPECT_EQ(eventActivationMap[2]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
+ EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
EXPECT_EQ(eventDeactivationMap[3], eventActivationMap[0]);
@@ -597,10 +597,10 @@
EXPECT_EQ(activeConfigsBroadcast.size(), 1);
EXPECT_EQ(activeConfigsBroadcast[0], cfgId);
EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap[0]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15);
+ EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15);
EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive);
- EXPECT_EQ(eventActivationMap[2]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
+ EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
EXPECT_EQ(eventDeactivationMap[3], eventActivationMap[0]);
@@ -613,10 +613,10 @@
EXPECT_EQ(broadcastCount, 4);
EXPECT_EQ(activeConfigsBroadcast.size(), 0);
EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap[0]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15);
+ EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15);
EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap[2]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
+ EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
EXPECT_EQ(eventDeactivationMap[3], eventActivationMap[0]);
@@ -632,10 +632,10 @@
EXPECT_EQ(activeConfigsBroadcast.size(), 1);
EXPECT_EQ(activeConfigsBroadcast[0], cfgId);
EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive);
- EXPECT_EQ(eventActivationMap[0]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15);
+ EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15);
EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap[2]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
+ EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
EXPECT_EQ(eventDeactivationMap[3], eventActivationMap[0]);
@@ -647,10 +647,10 @@
EXPECT_EQ(broadcastCount, 6);
EXPECT_EQ(activeConfigsBroadcast.size(), 0);
EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap[0]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15);
+ EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15);
EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap[2]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
+ EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
EXPECT_EQ(eventDeactivationMap[3], eventActivationMap[0]);
@@ -782,10 +782,10 @@
EXPECT_TRUE(eventActivationMap.find(0) != eventActivationMap.end());
EXPECT_TRUE(eventActivationMap.find(2) != eventActivationMap.end());
EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap[0]->activation_ns, 0);
+ EXPECT_EQ(eventActivationMap[0]->start_ns, 0);
EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap[2]->activation_ns, 0);
+ EXPECT_EQ(eventActivationMap[2]->start_ns, 0);
EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
EXPECT_EQ(eventDeactivationMap.size(), 2u);
EXPECT_TRUE(eventDeactivationMap.find(3) != eventDeactivationMap.end());
@@ -810,10 +810,10 @@
EXPECT_EQ(activeConfigsBroadcast.size(), 1);
EXPECT_EQ(activeConfigsBroadcast[0], cfgId);
EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive);
- EXPECT_EQ(eventActivationMap[0]->activation_ns, bucketStartTimeNs + 10);
+ EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10);
EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap[2]->activation_ns, 0);
+ EXPECT_EQ(eventActivationMap[2]->start_ns, 0);
EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
EXPECT_EQ(eventDeactivationMap[3], eventActivationMap[0]);
EXPECT_EQ(eventDeactivationMap[4], eventActivationMap[2]);
@@ -829,10 +829,10 @@
EXPECT_TRUE(metricsManager->isActive());
EXPECT_TRUE(metricProducer->mIsActive);
EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive);
- EXPECT_EQ(eventActivationMap[0]->activation_ns, bucketStartTimeNs + 10);
+ EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10);
EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive);
- EXPECT_EQ(eventActivationMap[2]->activation_ns, bucketStartTimeNs + 20);
+ EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20);
EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
EXPECT_EQ(eventDeactivationMap[3], eventActivationMap[0]);
EXPECT_EQ(eventDeactivationMap[4], eventActivationMap[2]);
@@ -844,10 +844,10 @@
EXPECT_TRUE(metricsManager->isActive());
EXPECT_TRUE(metricProducer->mIsActive);
EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive);
- EXPECT_EQ(eventActivationMap[0]->activation_ns, bucketStartTimeNs + 10);
+ EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10);
EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap[2]->activation_ns, bucketStartTimeNs + 20);
+ EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20);
EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
EXPECT_EQ(eventDeactivationMap[3], eventActivationMap[0]);
EXPECT_EQ(eventDeactivationMap[4], eventActivationMap[2]);
@@ -867,10 +867,10 @@
EXPECT_EQ(broadcastCount, 2);
EXPECT_EQ(activeConfigsBroadcast.size(), 0);
EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap[0]->activation_ns, bucketStartTimeNs + 10);
+ EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10);
EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap[2]->activation_ns, bucketStartTimeNs + 20);
+ EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20);
EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
EXPECT_EQ(eventDeactivationMap[3], eventActivationMap[0]);
EXPECT_EQ(eventDeactivationMap[4], eventActivationMap[2]);
@@ -885,10 +885,10 @@
EXPECT_EQ(activeConfigsBroadcast.size(), 1);
EXPECT_EQ(activeConfigsBroadcast[0], cfgId);
EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap[0]->activation_ns, bucketStartTimeNs + 10);
+ EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10);
EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive);
- EXPECT_EQ(eventActivationMap[2]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
+ EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
EXPECT_EQ(eventDeactivationMap[3], eventActivationMap[0]);
EXPECT_EQ(eventDeactivationMap[4], eventActivationMap[2]);
@@ -906,10 +906,10 @@
EXPECT_EQ(activeConfigsBroadcast.size(), 1);
EXPECT_EQ(activeConfigsBroadcast[0], cfgId);
EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive);
- EXPECT_EQ(eventActivationMap[0]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15);
+ EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15);
EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive);
- EXPECT_EQ(eventActivationMap[2]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
+ EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
EXPECT_EQ(eventDeactivationMap[3], eventActivationMap[0]);
EXPECT_EQ(eventDeactivationMap[4], eventActivationMap[2]);
@@ -927,10 +927,10 @@
EXPECT_EQ(broadcastCount, 4);
EXPECT_EQ(activeConfigsBroadcast.size(), 0);
EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap[0]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15);
+ EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15);
EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap[2]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
+ EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
EXPECT_EQ(eventDeactivationMap[3], eventActivationMap[0]);
EXPECT_EQ(eventDeactivationMap[4], eventActivationMap[2]);
@@ -943,10 +943,10 @@
EXPECT_EQ(broadcastCount, 4);
EXPECT_EQ(activeConfigsBroadcast.size(), 0);
EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap[0]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15);
+ EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15);
EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap[2]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
+ EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
EXPECT_EQ(eventDeactivationMap[3], eventActivationMap[0]);
EXPECT_EQ(eventDeactivationMap[4], eventActivationMap[2]);
@@ -963,10 +963,10 @@
EXPECT_EQ(activeConfigsBroadcast.size(), 1);
EXPECT_EQ(activeConfigsBroadcast[0], cfgId);
EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive);
- EXPECT_EQ(eventActivationMap[0]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15);
+ EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15);
EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap[2]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
+ EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
EXPECT_EQ(eventDeactivationMap[3], eventActivationMap[0]);
EXPECT_EQ(eventDeactivationMap[4], eventActivationMap[2]);
@@ -979,10 +979,10 @@
EXPECT_EQ(broadcastCount, 6);
EXPECT_EQ(activeConfigsBroadcast.size(), 0);
EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap[0]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15);
+ EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15);
EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap[2]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
+ EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
EXPECT_EQ(eventDeactivationMap[3], eventActivationMap[0]);
EXPECT_EQ(eventDeactivationMap[4], eventActivationMap[2]);
@@ -1119,10 +1119,10 @@
EXPECT_TRUE(eventActivationMap.find(0) != eventActivationMap.end());
EXPECT_TRUE(eventActivationMap.find(2) != eventActivationMap.end());
EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap[0]->activation_ns, 0);
+ EXPECT_EQ(eventActivationMap[0]->start_ns, 0);
EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap[2]->activation_ns, 0);
+ EXPECT_EQ(eventActivationMap[2]->start_ns, 0);
EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
EXPECT_EQ(eventDeactivationMap.size(), 2u);
EXPECT_TRUE(eventDeactivationMap.find(3) != eventDeactivationMap.end());
@@ -1134,10 +1134,10 @@
EXPECT_TRUE(eventActivationMap2.find(0) != eventActivationMap2.end());
EXPECT_TRUE(eventActivationMap2.find(2) != eventActivationMap2.end());
EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap2[0]->activation_ns, 0);
+ EXPECT_EQ(eventActivationMap2[0]->start_ns, 0);
EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap2[2]->activation_ns, 0);
+ EXPECT_EQ(eventActivationMap2[2]->start_ns, 0);
EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
EXPECT_EQ(eventDeactivationMap2.size(), 2u);
EXPECT_TRUE(eventDeactivationMap2.find(3) != eventDeactivationMap2.end());
@@ -1165,19 +1165,19 @@
EXPECT_EQ(activeConfigsBroadcast[0], cfgId);
EXPECT_TRUE(metricProducer->mIsActive);
EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive);
- EXPECT_EQ(eventActivationMap[0]->activation_ns, bucketStartTimeNs + 10);
+ EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10);
EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap[2]->activation_ns, 0);
+ EXPECT_EQ(eventActivationMap[2]->start_ns, 0);
EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
EXPECT_EQ(eventDeactivationMap[3], eventActivationMap[0]);
EXPECT_EQ(eventDeactivationMap[4], eventActivationMap[2]);
EXPECT_TRUE(metricProducer2->mIsActive);
EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kActive);
- EXPECT_EQ(eventActivationMap2[0]->activation_ns, bucketStartTimeNs + 10);
+ EXPECT_EQ(eventActivationMap2[0]->start_ns, bucketStartTimeNs + 10);
EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap2[2]->activation_ns, 0);
+ EXPECT_EQ(eventActivationMap2[2]->start_ns, 0);
EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
EXPECT_EQ(eventDeactivationMap2[3], eventActivationMap2[0]);
EXPECT_EQ(eventDeactivationMap2[4], eventActivationMap2[2]);
@@ -1195,19 +1195,19 @@
EXPECT_TRUE(metricsManager->isActive());
EXPECT_TRUE(metricProducer->mIsActive);
EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive);
- EXPECT_EQ(eventActivationMap[0]->activation_ns, bucketStartTimeNs + 10);
+ EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10);
EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive);
- EXPECT_EQ(eventActivationMap[2]->activation_ns, bucketStartTimeNs + 20);
+ EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20);
EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
EXPECT_EQ(eventDeactivationMap[3], eventActivationMap[0]);
EXPECT_EQ(eventDeactivationMap[4], eventActivationMap[2]);
EXPECT_TRUE(metricProducer2->mIsActive);
EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kActive);
- EXPECT_EQ(eventActivationMap2[0]->activation_ns, bucketStartTimeNs + 10);
+ EXPECT_EQ(eventActivationMap2[0]->start_ns, bucketStartTimeNs + 10);
EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kActive);
- EXPECT_EQ(eventActivationMap2[2]->activation_ns, bucketStartTimeNs + 20);
+ EXPECT_EQ(eventActivationMap2[2]->start_ns, bucketStartTimeNs + 20);
EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
EXPECT_EQ(eventDeactivationMap2[3], eventActivationMap2[0]);
EXPECT_EQ(eventDeactivationMap2[4], eventActivationMap2[2]);
@@ -1221,19 +1221,19 @@
EXPECT_TRUE(metricsManager->isActive());
EXPECT_TRUE(metricProducer->mIsActive);
EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive);
- EXPECT_EQ(eventActivationMap[0]->activation_ns, bucketStartTimeNs + 10);
+ EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10);
EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap[2]->activation_ns, bucketStartTimeNs + 20);
+ EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20);
EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
EXPECT_EQ(eventDeactivationMap[3], eventActivationMap[0]);
EXPECT_EQ(eventDeactivationMap[4], eventActivationMap[2]);
EXPECT_TRUE(metricProducer2->mIsActive);
EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kActive);
- EXPECT_EQ(eventActivationMap2[0]->activation_ns, bucketStartTimeNs + 10);
+ EXPECT_EQ(eventActivationMap2[0]->start_ns, bucketStartTimeNs + 10);
EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap2[2]->activation_ns, bucketStartTimeNs + 20);
+ EXPECT_EQ(eventActivationMap2[2]->start_ns, bucketStartTimeNs + 20);
EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
EXPECT_EQ(eventDeactivationMap2[3], eventActivationMap2[0]);
EXPECT_EQ(eventDeactivationMap2[4], eventActivationMap2[2]);
@@ -1257,19 +1257,19 @@
EXPECT_EQ(activeConfigsBroadcast.size(), 0);
EXPECT_FALSE(metricProducer->mIsActive);
EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap[0]->activation_ns, bucketStartTimeNs + 10);
+ EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10);
EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap[2]->activation_ns, bucketStartTimeNs + 20);
+ EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20);
EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
EXPECT_EQ(eventDeactivationMap[3], eventActivationMap[0]);
EXPECT_EQ(eventDeactivationMap[4], eventActivationMap[2]);
EXPECT_FALSE(metricProducer2->mIsActive);
EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap2[0]->activation_ns, bucketStartTimeNs + 10);
+ EXPECT_EQ(eventActivationMap2[0]->start_ns, bucketStartTimeNs + 10);
EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap2[2]->activation_ns, bucketStartTimeNs + 20);
+ EXPECT_EQ(eventActivationMap2[2]->start_ns, bucketStartTimeNs + 20);
EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
EXPECT_EQ(eventDeactivationMap2[3], eventActivationMap2[0]);
EXPECT_EQ(eventDeactivationMap2[4], eventActivationMap2[2]);
@@ -1284,19 +1284,19 @@
EXPECT_EQ(activeConfigsBroadcast[0], cfgId);
EXPECT_TRUE(metricProducer->mIsActive);
EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap[0]->activation_ns, bucketStartTimeNs + 10);
+ EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10);
EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive);
- EXPECT_EQ(eventActivationMap[2]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
+ EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
EXPECT_EQ(eventDeactivationMap[3], eventActivationMap[0]);
EXPECT_EQ(eventDeactivationMap[4], eventActivationMap[2]);
EXPECT_TRUE(metricProducer2->mIsActive);
EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap2[0]->activation_ns, bucketStartTimeNs + 10);
+ EXPECT_EQ(eventActivationMap2[0]->start_ns, bucketStartTimeNs + 10);
EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kActive);
- EXPECT_EQ(eventActivationMap2[2]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
+ EXPECT_EQ(eventActivationMap2[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
EXPECT_EQ(eventDeactivationMap2[3], eventActivationMap2[0]);
EXPECT_EQ(eventDeactivationMap2[4], eventActivationMap2[2]);
@@ -1316,19 +1316,19 @@
EXPECT_EQ(activeConfigsBroadcast[0], cfgId);
EXPECT_TRUE(metricProducer->mIsActive);
EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive);
- EXPECT_EQ(eventActivationMap[0]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15);
+ EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15);
EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive);
- EXPECT_EQ(eventActivationMap[2]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
+ EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
EXPECT_EQ(eventDeactivationMap[3], eventActivationMap[0]);
EXPECT_EQ(eventDeactivationMap[4], eventActivationMap[2]);
EXPECT_TRUE(metricProducer2->mIsActive);
EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kActive);
- EXPECT_EQ(eventActivationMap2[0]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15);
+ EXPECT_EQ(eventActivationMap2[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15);
EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kActive);
- EXPECT_EQ(eventActivationMap2[2]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
+ EXPECT_EQ(eventActivationMap2[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
EXPECT_EQ(eventDeactivationMap2[3], eventActivationMap2[0]);
EXPECT_EQ(eventDeactivationMap2[4], eventActivationMap2[2]);
@@ -1348,19 +1348,19 @@
EXPECT_EQ(activeConfigsBroadcast.size(), 0);
EXPECT_FALSE(metricProducer->mIsActive);
EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap[0]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15);
+ EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15);
EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap[2]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
+ EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
EXPECT_EQ(eventDeactivationMap[3], eventActivationMap[0]);
EXPECT_EQ(eventDeactivationMap[4], eventActivationMap[2]);
EXPECT_FALSE(metricProducer2->mIsActive);
EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap2[0]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15);
+ EXPECT_EQ(eventActivationMap2[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15);
EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap2[2]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
+ EXPECT_EQ(eventActivationMap2[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
EXPECT_EQ(eventDeactivationMap2[3], eventActivationMap2[0]);
EXPECT_EQ(eventDeactivationMap2[4], eventActivationMap2[2]);
@@ -1375,19 +1375,19 @@
EXPECT_EQ(activeConfigsBroadcast.size(), 0);
EXPECT_FALSE(metricProducer->mIsActive);
EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap[0]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15);
+ EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15);
EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap[2]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
+ EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
EXPECT_EQ(eventDeactivationMap[3], eventActivationMap[0]);
EXPECT_EQ(eventDeactivationMap[4], eventActivationMap[2]);
EXPECT_FALSE(metricProducer2->mIsActive);
EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap2[0]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15);
+ EXPECT_EQ(eventActivationMap2[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15);
EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap2[2]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
+ EXPECT_EQ(eventActivationMap2[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
EXPECT_EQ(eventDeactivationMap2[3], eventActivationMap2[0]);
EXPECT_EQ(eventDeactivationMap2[4], eventActivationMap2[2]);
@@ -1406,19 +1406,19 @@
EXPECT_EQ(activeConfigsBroadcast[0], cfgId);
EXPECT_TRUE(metricProducer->mIsActive);
EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive);
- EXPECT_EQ(eventActivationMap[0]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15);
+ EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15);
EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap[2]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
+ EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
EXPECT_EQ(eventDeactivationMap[3], eventActivationMap[0]);
EXPECT_EQ(eventDeactivationMap[4], eventActivationMap[2]);
EXPECT_TRUE(metricProducer2->mIsActive);
EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kActive);
- EXPECT_EQ(eventActivationMap2[0]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15);
+ EXPECT_EQ(eventActivationMap2[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15);
EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap2[2]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
+ EXPECT_EQ(eventActivationMap2[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
EXPECT_EQ(eventDeactivationMap2[3], eventActivationMap2[0]);
EXPECT_EQ(eventDeactivationMap2[4], eventActivationMap2[2]);
@@ -1431,19 +1431,19 @@
EXPECT_EQ(activeConfigsBroadcast.size(), 0);
EXPECT_FALSE(metricProducer->mIsActive);
EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap[0]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15);
+ EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15);
EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap[2]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
+ EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
EXPECT_EQ(eventDeactivationMap[3], eventActivationMap[0]);
EXPECT_EQ(eventDeactivationMap[4], eventActivationMap[2]);
EXPECT_FALSE(metricProducer2->mIsActive);
EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap2[0]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15);
+ EXPECT_EQ(eventActivationMap2[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15);
EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap2[2]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
+ EXPECT_EQ(eventActivationMap2[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
EXPECT_EQ(eventDeactivationMap2[3], eventActivationMap2[0]);
EXPECT_EQ(eventDeactivationMap2[4], eventActivationMap2[2]);
diff --git a/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp b/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp
index e967eb3..ff6af38 100644
--- a/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp
@@ -279,7 +279,7 @@
const int64_t ttlNs = 2 * bucketSizeNs; // Two buckets.
auto metric_activation = config.add_metric_activation();
metric_activation->set_metric_id(metricId);
- metric_activation->set_activation_type(MetricActivation::ACTIVATE_IMMEDIATELY);
+ metric_activation->set_activation_type(ACTIVATE_IMMEDIATELY);
auto event_activation = metric_activation->add_event_activation();
event_activation->set_atom_matcher_id(batterySaverStartMatcher.id());
event_activation->set_ttl_seconds(ttlNs / 1000000000);
diff --git a/core/java/android/content/om/IOverlayManager.aidl b/core/java/android/content/om/IOverlayManager.aidl
index 722c128..43a4fe5 100644
--- a/core/java/android/content/om/IOverlayManager.aidl
+++ b/core/java/android/content/om/IOverlayManager.aidl
@@ -152,4 +152,9 @@
* @param userId The user for which to change the overlay.
*/
boolean setLowestPriority(in String packageName, in int userId);
+
+ /**
+ * Returns the list of default overlay packages, or an empty array if there are none.
+ */
+ String[] getDefaultOverlayPackages();
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index ced6115..8a21806 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -1791,6 +1791,58 @@
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
public static final String ACTION_MANAGE_DOMAIN_URLS = "android.settings.MANAGE_DOMAIN_URLS";
+ /**
+ * Broadcast to trigger notification of asking user to enable MMS.
+ * Need to specify {@link #EXTRA_ENABLE_MMS_DATA_REQUEST_REASON} and {@link #EXTRA_SUB_ID}.
+ *
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_ENABLE_MMS_DATA_REQUEST =
+ "android.settings.ENABLE_MMS_DATA_REQUEST";
+
+ /**
+ * Integer value that specifies the reason triggering enable MMS data notification.
+ * This must be passed as an extra field to the {@link #ACTION_ENABLE_MMS_DATA_REQUEST}.
+ * Extra with value of EnableMmsDataReason interface.
+ * @hide
+ */
+ public static final String EXTRA_ENABLE_MMS_DATA_REQUEST_REASON =
+ "android.settings.extra.ENABLE_MMS_DATA_REQUEST_REASON";
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = { "ENABLE_MMS_DATA_REQUEST_REASON_" }, value = {
+ ENABLE_MMS_DATA_REQUEST_REASON_INCOMING_MMS,
+ ENABLE_MMS_DATA_REQUEST_REASON_OUTGOING_MMS,
+ })
+ public @interface EnableMmsDataReason{}
+
+ /**
+ * Requesting to enable MMS data because there's an incoming MMS.
+ * @hide
+ */
+ public static final int ENABLE_MMS_DATA_REQUEST_REASON_INCOMING_MMS = 0;
+
+ /**
+ * Requesting to enable MMS data because user is sending MMS.
+ * @hide
+ */
+ public static final int ENABLE_MMS_DATA_REQUEST_REASON_OUTGOING_MMS = 1;
+
+ /**
+ * Activity Action: Show screen of a cellular subscription and highlight the
+ * "enable MMS" toggle.
+ * <p>
+ * Input: {@link #EXTRA_SUB_ID}: Sub ID of the subscription.
+ * <p>
+ * Output: Nothing
+ *
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_MMS_MESSAGE_SETTING = "android.settings.MMS_MESSAGE_SETTING";
+
// End of Intent actions for Settings
/**
diff --git a/core/java/com/android/internal/app/AbstractResolverComparator.java b/core/java/com/android/internal/app/AbstractResolverComparator.java
index b7276a0..87d80d4 100644
--- a/core/java/com/android/internal/app/AbstractResolverComparator.java
+++ b/core/java/com/android/internal/app/AbstractResolverComparator.java
@@ -22,9 +22,14 @@
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
import android.os.UserHandle;
import android.util.Log;
+
import com.android.internal.app.ResolverActivity.ResolvedComponentInfo;
+
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
@@ -35,6 +40,8 @@
abstract class AbstractResolverComparator implements Comparator<ResolvedComponentInfo> {
private static final int NUM_OF_TOP_ANNOTATIONS_TO_USE = 3;
+ private static final boolean DEBUG = false;
+ private static final String TAG = "AbstractResolverComp";
private AfterCompute mAfterCompute;
protected final PackageManager mPm;
@@ -47,6 +54,46 @@
// can be null if mHttp == false or current user has no default browser package
private final String mDefaultBrowserPackageName;
+ // message types
+ static final int RANKER_SERVICE_RESULT = 0;
+ static final int RANKER_RESULT_TIMEOUT = 1;
+
+ // timeout for establishing connections with a ResolverRankerService, collecting features and
+ // predicting ranking scores.
+ private static final int WATCHDOG_TIMEOUT_MILLIS = 500;
+
+ protected final Handler mHandler = new Handler(Looper.getMainLooper()) {
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case RANKER_SERVICE_RESULT:
+ if (DEBUG) {
+ Log.d(TAG, "RANKER_SERVICE_RESULT");
+ }
+ if (mHandler.hasMessages(RANKER_RESULT_TIMEOUT)) {
+ if (msg.obj != null) {
+ handleResultMessage(msg);
+ } else {
+ Log.e(TAG, "Receiving null prediction results.");
+ }
+ mHandler.removeMessages(RANKER_RESULT_TIMEOUT);
+ afterCompute();
+ }
+ break;
+
+ case RANKER_RESULT_TIMEOUT:
+ if (DEBUG) {
+ Log.d(TAG, "RANKER_RESULT_TIMEOUT; unbinding services");
+ }
+ mHandler.removeMessages(RANKER_SERVICE_RESULT);
+ afterCompute();
+ break;
+
+ default:
+ super.handleMessage(msg);
+ }
+ }
+ };
+
AbstractResolverComparator(Context context, Intent intent) {
String scheme = intent.getScheme();
mHttp = "http".equals(scheme) || "https".equals(scheme);
@@ -142,9 +189,16 @@
* #getScore(ComponentName)} or {@link #compare(Object, Object)}, in order to prepare the
* comparator for those calls. Note that {@link #getScore(ComponentName)} uses {@link
* ComponentName}, so the implementation will have to be prepared to identify a {@link
- * ResolvedComponentInfo} by {@link ComponentName}.
+ * ResolvedComponentInfo} by {@link ComponentName}. {@link #beforeCompute()} will be called
+ * before doing any computing.
*/
- abstract void compute(List<ResolvedComponentInfo> targets);
+ final void compute(List<ResolvedComponentInfo> targets) {
+ beforeCompute();
+ doCompute(targets);
+ }
+
+ /** Implementation of compute called after {@link #beforeCompute()}. */
+ abstract void doCompute(List<ResolvedComponentInfo> targets);
/**
* Returns the score that was calculated for the corresponding {@link ResolvedComponentInfo}
@@ -152,6 +206,9 @@
*/
abstract float getScore(ComponentName name);
+ /** Handles result message sent to mHandler. */
+ abstract void handleResultMessage(Message message);
+
/**
* Reports to UsageStats what was chosen.
*/
@@ -172,10 +229,26 @@
void updateModel(ComponentName componentName) {
}
+ /** Called before {@link #doCompute(List)}. Sets up 500ms timeout. */
+ void beforeCompute() {
+ if (DEBUG) Log.d(TAG, "Setting watchdog timer for " + WATCHDOG_TIMEOUT_MILLIS + "ms");
+ if (mHandler == null) {
+ Log.d(TAG, "Error: Handler is Null; Needs to be initialized.");
+ return;
+ }
+ mHandler.sendEmptyMessageDelayed(RANKER_RESULT_TIMEOUT, WATCHDOG_TIMEOUT_MILLIS);
+ }
+
/**
- * Called when the {@link ResolverActivity} is destroyed.
+ * Called when the {@link ResolverActivity} is destroyed. This calls {@link #afterCompute()}. If
+ * this call needs to happen at a different time during destroy, the method should be
+ * overridden.
*/
- abstract void destroy();
+ void destroy() {
+ mHandler.removeMessages(RANKER_SERVICE_RESULT);
+ mHandler.removeMessages(RANKER_RESULT_TIMEOUT);
+ afterCompute();
+ }
private boolean isDefaultBrowser(ResolveInfo ri) {
// It makes sense to prefer the default browser
diff --git a/core/java/com/android/internal/app/AppPredictionServiceResolverComparator.java b/core/java/com/android/internal/app/AppPredictionServiceResolverComparator.java
index cb44c67..3b4e1a0 100644
--- a/core/java/com/android/internal/app/AppPredictionServiceResolverComparator.java
+++ b/core/java/com/android/internal/app/AppPredictionServiceResolverComparator.java
@@ -26,6 +26,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.pm.ResolveInfo;
+import android.os.Message;
import android.os.UserHandle;
import android.view.textclassifier.Log;
@@ -73,7 +74,7 @@
}
@Override
- void compute(List<ResolvedComponentInfo> targets) {
+ void doCompute(List<ResolvedComponentInfo> targets) {
List<AppTarget> appTargets = new ArrayList<>();
for (ResolvedComponentInfo target : targets) {
appTargets.add(new AppTarget.Builder(new AppTargetId(target.name.flattenToString()))
@@ -82,15 +83,24 @@
}
mAppPredictor.sortTargets(appTargets, mContext.getMainExecutor(),
sortedAppTargets -> {
- for (int i = 0; i < sortedAppTargets.size(); i++) {
- mTargetRanks.put(new ComponentName(sortedAppTargets.get(i).getPackageName(),
- sortedAppTargets.get(i).getClassName()), i);
- }
- afterCompute();
+ Message msg =
+ Message.obtain(mHandler, RANKER_SERVICE_RESULT, sortedAppTargets);
+ msg.sendToTarget();
});
}
@Override
+ void handleResultMessage(Message msg) {
+ if (msg.what == RANKER_SERVICE_RESULT) {
+ final List<AppTarget> sortedAppTargets = (List<AppTarget>) msg.obj;
+ for (int i = 0; i < sortedAppTargets.size(); i++) {
+ mTargetRanks.put(new ComponentName(sortedAppTargets.get(i).getPackageName(),
+ sortedAppTargets.get(i).getClassName()), i);
+ }
+ }
+ }
+
+ @Override
float getScore(ComponentName name) {
Integer rank = mTargetRanks.get(name);
if (rank == null) {
@@ -111,9 +121,4 @@
.setClassName(componentName.getClassName()).build(),
ACTION_LAUNCH).build());
}
-
- @Override
- void destroy() {
- // Do nothing. App Predictor destruction is handled by caller.
- }
}
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 204012f..8b510a4 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -114,6 +114,7 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.util.ImageUtils;
+import com.android.internal.widget.ResolverDrawerLayout;
import com.google.android.collect.Lists;
@@ -143,6 +144,8 @@
public static final String EXTRA_PRIVATE_RETAIN_IN_ON_STOP
= "com.android.internal.app.ChooserActivity.EXTRA_PRIVATE_RETAIN_IN_ON_STOP";
+ private static final String PREF_NUM_SHEET_EXPANSIONS = "pref_num_sheet_expansions";
+
private static final boolean DEBUG = false;
/**
@@ -502,6 +505,21 @@
chooserHeader.setElevation(defaultElevation);
}
});
+
+ mResolverDrawerLayout.setOnCollapsedChangedListener(
+ new ResolverDrawerLayout.OnCollapsedChangedListener() {
+
+ // Only consider one expansion per activity creation
+ private boolean mWrittenOnce = false;
+
+ @Override
+ public void onCollapsedChanged(boolean isCollapsed) {
+ if (!isCollapsed && !mWrittenOnce) {
+ incrementNumSheetExpansions();
+ mWrittenOnce = true;
+ }
+ }
+ });
}
if (DEBUG) {
@@ -881,6 +899,15 @@
return CONTENT_PREVIEW_TEXT;
}
+ private int getNumSheetExpansions() {
+ return getPreferences(Context.MODE_PRIVATE).getInt(PREF_NUM_SHEET_EXPANSIONS, 0);
+ }
+
+ private void incrementNumSheetExpansions() {
+ getPreferences(Context.MODE_PRIVATE).edit().putInt(PREF_NUM_SHEET_EXPANSIONS,
+ getNumSheetExpansions() + 1).apply();
+ }
+
@Override
protected void onDestroy() {
super.onDestroy();
@@ -2030,7 +2057,8 @@
public static final int TARGET_STANDARD_AZ = 3;
private static final int MAX_SUGGESTED_APP_TARGETS = 4;
- private static final int MAX_TARGETS_PER_SERVICE = 2;
+ private static final int MAX_CHOOSER_TARGETS_PER_APP = 2;
+ private static final int MAX_SHORTCUT_TARGETS_PER_APP = 8;
private static final int MAX_SERVICE_TARGETS = 8;
@@ -2356,9 +2384,11 @@
final float baseScore = getBaseScore(origTarget, isShortcutResult);
Collections.sort(targets, mBaseTargetComparator);
+ final int maxTargets = isShortcutResult ? MAX_SHORTCUT_TARGETS_PER_APP
+ : MAX_CHOOSER_TARGETS_PER_APP;
float lastScore = 0;
boolean shouldNotify = false;
- for (int i = 0, N = Math.min(targets.size(), MAX_TARGETS_PER_SERVICE); i < N; i++) {
+ for (int i = 0, count = Math.min(targets.size(), maxTargets); i < count; i++) {
final ChooserTarget target = targets.get(i);
float targetScore = target.getScore();
targetScore *= baseScore;
@@ -2491,19 +2521,25 @@
private DirectShareViewHolder mDirectShareViewHolder;
private int mChooserTargetWidth = 0;
+ private boolean mShowAzLabelIfPoss;
private static final int VIEW_TYPE_DIRECT_SHARE = 0;
private static final int VIEW_TYPE_NORMAL = 1;
private static final int VIEW_TYPE_CONTENT_PREVIEW = 2;
private static final int VIEW_TYPE_PROFILE = 3;
+ private static final int VIEW_TYPE_AZ_LABEL = 4;
private static final int MAX_TARGETS_PER_ROW_PORTRAIT = 4;
private static final int MAX_TARGETS_PER_ROW_LANDSCAPE = 8;
+ private static final int NUM_EXPANSIONS_TO_HIDE_AZ_LABEL = 20;
+
public ChooserRowAdapter(ChooserListAdapter wrappedAdapter) {
mChooserListAdapter = wrappedAdapter;
mLayoutInflater = LayoutInflater.from(ChooserActivity.this);
+ mShowAzLabelIfPoss = getNumSheetExpansions() < NUM_EXPANSIONS_TO_HIDE_AZ_LABEL;
+
wrappedAdapter.registerDataSetObserver(new DataSetObserver() {
@Override
public void onChanged() {
@@ -2556,6 +2592,7 @@
+ getProfileRowCount()
+ getServiceTargetRowCount()
+ getCallerAndRankedTargetRowCount()
+ + getAzLabelRowCount()
+ Math.ceil(
(float) mChooserListAdapter.getAlphaTargetCount()
/ getMaxTargetsPerRow())
@@ -2593,6 +2630,11 @@
return 0;
}
+ public int getAzLabelRowCount() {
+ // Only show a label if the a-z list is showing
+ return (mShowAzLabelIfPoss && mChooserListAdapter.getAlphaTargetCount() > 0) ? 1 : 0;
+ }
+
@Override
public Object getItem(int position) {
// We have nothing useful to return here.
@@ -2617,6 +2659,10 @@
return createProfileView(convertView, parent);
}
+ if (viewType == VIEW_TYPE_AZ_LABEL) {
+ return createAzLabelView(parent);
+ }
+
if (convertView == null) {
holder = createViewHolder(viewType, parent);
} else {
@@ -2630,27 +2676,29 @@
@Override
public int getItemViewType(int position) {
- if (position == 0 && getContentPreviewRowCount() == 1) {
- return VIEW_TYPE_CONTENT_PREVIEW;
- }
+ int count;
- if (getProfileRowCount() == 1 && position == getContentPreviewRowCount()) {
- return VIEW_TYPE_PROFILE;
- }
+ int countSum = (count = getContentPreviewRowCount());
+ if (count > 0 && position < countSum) return VIEW_TYPE_CONTENT_PREVIEW;
- final int start = getFirstRowPosition(position);
- final int startType = mChooserListAdapter.getPositionTargetType(start);
+ countSum += (count = getProfileRowCount());
+ if (count > 0 && position < countSum) return VIEW_TYPE_PROFILE;
- if (startType == ChooserListAdapter.TARGET_SERVICE) {
- return VIEW_TYPE_DIRECT_SHARE;
- }
+ countSum += (count = getServiceTargetRowCount());
+ if (count > 0 && position < countSum) return VIEW_TYPE_DIRECT_SHARE;
+
+ countSum += (count = getCallerAndRankedTargetRowCount());
+ if (count > 0 && position < countSum) return VIEW_TYPE_NORMAL;
+
+ countSum += (count = getAzLabelRowCount());
+ if (count > 0 && position < countSum) return VIEW_TYPE_AZ_LABEL;
return VIEW_TYPE_NORMAL;
}
@Override
public int getViewTypeCount() {
- return 4;
+ return 5;
}
private ViewGroup createContentPreviewView(View convertView, ViewGroup parent) {
@@ -2677,6 +2725,10 @@
return profileRow;
}
+ private View createAzLabelView(ViewGroup parent) {
+ return mLayoutInflater.inflate(R.layout.chooser_az_label_row, parent, false);
+ }
+
private RowViewHolder loadViewsIntoRow(RowViewHolder holder) {
final int spec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
final int exactSpec = MeasureSpec.makeMeasureSpec(mChooserTargetWidth,
@@ -2775,16 +2827,24 @@
}
/**
- * Need to merge CALLER + ranked STANDARD into a single row. All other types
- * are placed into their own row as determined by their target type, and dividers
- * are added in the list to separate each type.
+ * Need to merge CALLER + ranked STANDARD into a single row and prevent a separator from
+ * showing on top of the AZ list if the AZ label is visible. All other types are placed into
+ * their own row as determined by their target type, and dividers are added in the list to
+ * separate each type.
*/
int getRowType(int rowPosition) {
+ // Merge caller and ranked standard into a single row
int positionType = mChooserListAdapter.getPositionTargetType(rowPosition);
if (positionType == ChooserListAdapter.TARGET_CALLER) {
return ChooserListAdapter.TARGET_STANDARD;
}
+ // If an the A-Z label is shown, prevent a separator from appearing by making the A-Z
+ // row type the same as the suggestion row type
+ if (getAzLabelRowCount() > 0 && positionType == ChooserListAdapter.TARGET_STANDARD_AZ) {
+ return ChooserListAdapter.TARGET_STANDARD;
+ }
+
return positionType;
}
@@ -2864,6 +2924,8 @@
return serviceCount + (row - serviceRows) * getMaxTargetsPerRow();
}
+ row -= getAzLabelRowCount();
+
return callerAndRankedCount + serviceCount
+ (row - callerAndRankedRows - serviceRows) * getMaxTargetsPerRow();
}
diff --git a/core/java/com/android/internal/app/ResolverRankerServiceResolverComparator.java b/core/java/com/android/internal/app/ResolverRankerServiceResolverComparator.java
index 726b186..a781907 100644
--- a/core/java/com/android/internal/app/ResolverRankerServiceResolverComparator.java
+++ b/core/java/com/android/internal/app/ResolverRankerServiceResolverComparator.java
@@ -18,7 +18,6 @@
package com.android.internal.app;
import android.app.usage.UsageStats;
-import android.app.usage.UsageStatsManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -28,9 +27,7 @@
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ResolveInfo;
import android.metrics.LogMaker;
-import android.os.Handler;
import android.os.IBinder;
-import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
import android.os.UserHandle;
@@ -67,10 +64,6 @@
private static final float RECENCY_MULTIPLIER = 2.f;
- // message types
- private static final int RESOLVER_RANKER_SERVICE_RESULT = 0;
- private static final int RESOLVER_RANKER_RESULT_TIMEOUT = 1;
-
// timeout for establishing connections with a ResolverRankerService.
private static final int CONNECTION_COST_TIMEOUT_MILLIS = 200;
// timeout for establishing connections with a ResolverRankerService, collecting features and
@@ -93,57 +86,6 @@
private Context mContext;
private CountDownLatch mConnectSignal;
- private final Handler mHandler = new Handler(Looper.getMainLooper()) {
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case RESOLVER_RANKER_SERVICE_RESULT:
- if (DEBUG) {
- Log.d(TAG, "RESOLVER_RANKER_SERVICE_RESULT");
- }
- if (mHandler.hasMessages(RESOLVER_RANKER_RESULT_TIMEOUT)) {
- if (msg.obj != null) {
- final List<ResolverTarget> receivedTargets =
- (List<ResolverTarget>) msg.obj;
- if (receivedTargets != null && mTargets != null
- && receivedTargets.size() == mTargets.size()) {
- final int size = mTargets.size();
- boolean isUpdated = false;
- for (int i = 0; i < size; ++i) {
- final float predictedProb =
- receivedTargets.get(i).getSelectProbability();
- if (predictedProb != mTargets.get(i).getSelectProbability()) {
- mTargets.get(i).setSelectProbability(predictedProb);
- isUpdated = true;
- }
- }
- if (isUpdated) {
- mRankerServiceName = mResolvedRankerName;
- }
- } else {
- Log.e(TAG, "Sizes of sent and received ResolverTargets diff.");
- }
- } else {
- Log.e(TAG, "Receiving null prediction results.");
- }
- mHandler.removeMessages(RESOLVER_RANKER_RESULT_TIMEOUT);
- afterCompute();
- }
- break;
-
- case RESOLVER_RANKER_RESULT_TIMEOUT:
- if (DEBUG) {
- Log.d(TAG, "RESOLVER_RANKER_RESULT_TIMEOUT; unbinding services");
- }
- mHandler.removeMessages(RESOLVER_RANKER_SERVICE_RESULT);
- afterCompute();
- break;
-
- default:
- super.handleMessage(msg);
- }
- }
- };
-
public ResolverRankerServiceResolverComparator(Context context, Intent intent,
String referrerPackage, AfterCompute afterCompute) {
super(context, intent);
@@ -159,11 +101,35 @@
setCallBack(afterCompute);
}
+ @Override
+ public void handleResultMessage(Message msg) {
+ if (msg.what != RANKER_SERVICE_RESULT) {
+ return;
+ }
+ final List<ResolverTarget> receivedTargets = (List<ResolverTarget>) msg.obj;
+ if (receivedTargets != null && mTargets != null
+ && receivedTargets.size() == mTargets.size()) {
+ final int size = mTargets.size();
+ boolean isUpdated = false;
+ for (int i = 0; i < size; ++i) {
+ final float predictedProb =
+ receivedTargets.get(i).getSelectProbability();
+ if (predictedProb != mTargets.get(i).getSelectProbability()) {
+ mTargets.get(i).setSelectProbability(predictedProb);
+ isUpdated = true;
+ }
+ }
+ if (isUpdated) {
+ mRankerServiceName = mResolvedRankerName;
+ }
+ } else {
+ Log.e(TAG, "Sizes of sent and received ResolverTargets diff.");
+ }
+ }
+
// compute features for each target according to usage stats of targets.
@Override
- public void compute(List<ResolvedComponentInfo> targets) {
- reset();
-
+ public void doCompute(List<ResolvedComponentInfo> targets) {
final long recentSinceTime = mCurrentTime - RECENCY_TIME_PERIOD;
float mostRecencyScore = 1.0f;
@@ -322,8 +288,8 @@
// unbind the service and clear unhandled messges.
@Override
public void destroy() {
- mHandler.removeMessages(RESOLVER_RANKER_SERVICE_RESULT);
- mHandler.removeMessages(RESOLVER_RANKER_RESULT_TIMEOUT);
+ mHandler.removeMessages(RANKER_SERVICE_RESULT);
+ mHandler.removeMessages(RANKER_RESULT_TIMEOUT);
if (mConnection != null) {
mContext.unbindService(mConnection);
mConnection.destroy();
@@ -417,15 +383,6 @@
return null;
}
- // set a watchdog, to avoid waiting for ranking service for too long.
- private void startWatchDog(int timeOutLimit) {
- if (DEBUG) Log.d(TAG, "Setting watchdog timer for " + timeOutLimit + "ms");
- if (mHandler == null) {
- Log.d(TAG, "Error: Handler is Null; Needs to be initialized.");
- }
- mHandler.sendEmptyMessageDelayed(RESOLVER_RANKER_RESULT_TIMEOUT, timeOutLimit);
- }
-
private class ResolverRankerServiceConnection implements ServiceConnection {
private final CountDownLatch mConnectSignal;
@@ -442,7 +399,7 @@
}
synchronized (mLock) {
final Message msg = Message.obtain();
- msg.what = RESOLVER_RANKER_SERVICE_RESULT;
+ msg.what = RANKER_SERVICE_RESULT;
msg.obj = targets;
mHandler.sendMessage(msg);
}
@@ -477,12 +434,13 @@
}
}
- private void reset() {
+ @Override
+ void beforeCompute() {
+ super.beforeCompute();
mTargetsDict.clear();
mTargets = null;
mRankerServiceName = new ComponentName(mContext, this.getClass());
mResolvedRankerName = null;
- startWatchDog(WATCHDOG_TIMEOUT_MILLIS);
initRanker(mContext);
}
diff --git a/core/java/com/android/internal/widget/ResolverDrawerLayout.java b/core/java/com/android/internal/widget/ResolverDrawerLayout.java
index 6b0d85e..3adb36f 100644
--- a/core/java/com/android/internal/widget/ResolverDrawerLayout.java
+++ b/core/java/com/android/internal/widget/ResolverDrawerLayout.java
@@ -104,6 +104,7 @@
private OnDismissedListener mOnDismissedListener;
private RunOnDismissedListener mRunOnDismissedListener;
+ private OnCollapsedChangedListener mOnCollapsedChangedListener;
private boolean mDismissLocked;
@@ -267,6 +268,10 @@
return mOnDismissedListener != null && !mDismissLocked;
}
+ public void setOnCollapsedChangedListener(OnCollapsedChangedListener listener) {
+ mOnCollapsedChangedListener = listener;
+ }
+
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
final int action = ev.getActionMasked();
@@ -548,6 +553,10 @@
if (mScrollIndicatorDrawable != null) {
setWillNotDraw(!isCollapsed);
}
+
+ if (mOnCollapsedChangedListener != null) {
+ mOnCollapsedChangedListener.onCollapsedChanged(isCollapsed);
+ }
}
void dispatchOnDismissed() {
@@ -1078,8 +1087,25 @@
};
}
+ /**
+ * Listener for sheet dismissed events.
+ */
public interface OnDismissedListener {
- public void onDismissed();
+ /**
+ * Callback when the sheet is dismissed by the user.
+ */
+ void onDismissed();
+ }
+
+ /**
+ * Listener for sheet collapsed / expanded events.
+ */
+ public interface OnCollapsedChangedListener {
+ /**
+ * Callback when the sheet is either fully expanded or collapsed.
+ * @param isCollapsed true when collapsed, false when expanded.
+ */
+ void onCollapsedChanged(boolean isCollapsed);
}
private class RunOnDismissedListener implements Runnable {
diff --git a/core/res/res/layout/chooser_az_label_row.xml b/core/res/res/layout/chooser_az_label_row.xml
new file mode 100644
index 0000000..1b733fc
--- /dev/null
+++ b/core/res/res/layout/chooser_az_label_row.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+
+<!-- Separator applied as background -->
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:text="@string/chooser_all_apps_button_label"
+ android:contentDescription="@string/chooser_all_apps_button_label"
+ android:background="@drawable/chooser_row_layer_list"
+ android:textAppearance="?attr/textAppearanceSmall"
+ android:textColor="?attr/textColorSecondary"
+ android:textSize="14sp"
+ android:singleLine="true"
+ android:paddingTop="16dp"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center"/>
+
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index ad3203e..04ccb74 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3863,10 +3863,6 @@
{@see android.view.Display#FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS} -->
<string name="config_secondaryHomeComponent" translatable="false">com.android.launcher3/com.android.launcher3.SecondaryDisplayLauncher</string>
- <!-- Force secondary home launcher specified in config_secondaryHomeComponent always. If this is
- not set, secondary home launcher can be replaced by user. -->
- <bool name ="config_useSystemProvidedLauncherForSecondary">false</bool>
-
<!-- If device supports corner radius on windows.
This should be turned off on low-end devices to improve animation performance. -->
<bool name="config_supportsRoundedCornersOnWindows">true</bool>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index ef834aa..75fd3a0 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -5373,4 +5373,7 @@
<!-- ChooserActivity - No direct share targets are available. [CHAR LIMIT=NONE] -->
<string name="chooser_no_direct_share_targets">Direct share not available</string>
+ <!-- ChooserActivity - Alphabetically sorted apps list label. [CHAR LIMIT=NONE] -->
+ <string name="chooser_all_apps_button_label">Apps list</string>
+
</resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 085ce56..fbe340e 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3678,7 +3678,6 @@
<!-- For Secondary Launcher -->
<java-symbol type="string" name="config_secondaryHomeComponent" />
- <java-symbol type="bool" name="config_useSystemProvidedLauncherForSecondary" />
<java-symbol type="string" name="battery_saver_notification_channel_name" />
<java-symbol type="string" name="battery_saver_sticky_disabled_notification_title" />
@@ -3772,4 +3771,6 @@
<java-symbol type="color" name="chooser_gradient_highlight" />
<java-symbol type="drawable" name="chooser_direct_share_label_placeholder" />
<java-symbol type="dimen" name="chooser_direct_share_label_placeholder_max_width" />
+ <java-symbol type="layout" name="chooser_az_label_row" />
+ <java-symbol type="string" name="chooser_all_apps_button_label" />
</resources>
diff --git a/packages/SystemUI/res-keyguard/values/dimens.xml b/packages/SystemUI/res-keyguard/values/dimens.xml
index d8f543f..112dde6 100644
--- a/packages/SystemUI/res-keyguard/values/dimens.xml
+++ b/packages/SystemUI/res-keyguard/values/dimens.xml
@@ -53,7 +53,7 @@
<!-- Clock with header -->
<dimen name="widget_small_font_size">22dp</dimen>
<dimen name="widget_vertical_padding">32dp</dimen>
- <dimen name="widget_vertical_padding_clock">30dp</dimen>
+ <dimen name="widget_vertical_padding_clock">12dp</dimen>
<!-- Subtitle paddings -->
<dimen name="widget_horizontal_padding">8dp</dimen>
<dimen name="widget_icon_size">16dp</dimen>
diff --git a/packages/SystemUI/res/drawable/button_border_selected.xml b/packages/SystemUI/res/drawable/button_border_selected.xml
index d9299ec..1e40ade 100644
--- a/packages/SystemUI/res/drawable/button_border_selected.xml
+++ b/packages/SystemUI/res/drawable/button_border_selected.xml
@@ -20,6 +20,6 @@
android:color="@color/notification_guts_selection_bg" />
<stroke
android:width="2dp"
- android:color="?android:attr/colorAccent"/>
+ android:color="@color/GM2_grey_300"/>
<corners android:radius="@dimen/rect_button_radius" />
</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/notification_info.xml b/packages/SystemUI/res/layout/notification_info.xml
index 58af4a2..087e0bd 100644
--- a/packages/SystemUI/res/layout/notification_info.xml
+++ b/packages/SystemUI/res/layout/notification_info.xml
@@ -103,7 +103,6 @@
android:id="@+id/channel_info"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginBottom="@dimen/notification_guts_button_spacing"
android:paddingEnd="@*android:dimen/notification_content_margin_end"
android:gravity="center"
android:orientation="vertical">
@@ -127,6 +126,7 @@
android:id="@+id/blocking_helper"
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/notification_guts_button_spacing"
android:layout_marginBottom="@dimen/notification_guts_button_spacing"
android:paddingEnd="@*android:dimen/notification_content_margin_end"
android:clipChildren="false"
@@ -216,67 +216,107 @@
android:id="@+id/interruptiveness_settings"
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:gravity="center"
android:orientation="vertical">
- <LinearLayout
- android:id="@+id/buttons"
+ <RelativeLayout
+ android:id="@+id/alert"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:orientation="horizontal"
- android:gravity="center">
-
- <Button
- android:id="@+id/alert"
- android:minWidth="@dimen/notification_importance_button_width"
+ android:padding="@dimen/notification_importance_button_padding"
+ android:clickable="true"
+ android:focusable="true">
+ <ImageView
+ android:id="@+id/alert_icon"
+ android:src="@drawable/ic_notification_interruptive"
+ android:background="@android:color/transparent"
+ android:layout_gravity="center"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:minHeight="@dimen/notification_importance_toggle_size"
- android:paddingStart="@dimen/notification_importance_button_horiz_padding"
- android:paddingEnd="@dimen/notification_importance_button_horiz_padding"
- android:drawablePadding="@dimen/notification_importance_drawable_padding"
- android:foreground="@drawable/button_ripple_radius"
- android:drawableLeft="@drawable/ic_notification_interruptive"
- android:text="@string/notification_alert_title" />
-
- <Button
- android:id="@+id/silence"
- android:minWidth="@dimen/notification_importance_button_width"
- android:layout_width="wrap_content"
+ android:clickable="false"
+ android:focusable="false"/>
+ <TextView
+ android:id="@+id/alert_label"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:minHeight="@dimen/notification_importance_toggle_size"
- android:paddingStart="@dimen/notification_importance_button_horiz_padding"
- android:paddingEnd="@dimen/notification_importance_button_horiz_padding"
- android:drawablePadding="@dimen/notification_importance_drawable_padding"
- android:foreground="@drawable/button_ripple_radius"
- android:layout_marginStart="@dimen/notification_importance_button_separation"
- android:drawableLeft="@drawable/ic_notification_gentle"
- android:text="@string/notification_silence_title" />
- </LinearLayout>
+ android:ellipsize="end"
+ android:maxLines="1"
+ android:clickable="false"
+ android:focusable="false"
+ android:layout_toEndOf="@id/alert_icon"
+ android:layout_marginStart="@dimen/notification_importance_drawable_padding"
+ android:textAppearance="@style/TextAppearance.NotificationImportanceButton.Unselected"
+ android:text="@string/notification_alert_title"/>
+ <TextView
+ android:id="@+id/alert_summary"
+ android:paddingTop="@dimen/notification_importance_button_padding"
+ android:text="@string/notification_channel_summary_default"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:clickable="false"
+ android:focusable="false"
+ android:ellipsize="end"
+ android:maxLines="2"
+ android:layout_below="@id/alert_icon"
+ android:textAppearance="@style/TextAppearance.NotificationImportanceDetail"/>
+ </RelativeLayout>
- <TextView
- android:id="@+id/description"
+ <RelativeLayout
+ android:id="@+id/silence"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:text="@string/notification_alert_title"
- android:gravity="center"
- android:layout_marginTop="@dimen/notification_importance_text_marginTop"
- android:paddingStart="@dimen/notification_importance_description_padding"
- android:paddingEnd="@dimen/notification_importance_description_padding"
- android:textAppearance="@style/TextAppearance.NotificationImportanceDetail" />
+ android:padding="@dimen/notification_importance_button_padding"
+ android:layout_marginTop="@dimen/notification_importance_button_separation"
+ android:clickable="true"
+ android:focusable="true">
+ <ImageView
+ android:id="@+id/silence_icon"
+ android:src="@drawable/ic_notification_gentle"
+ android:background="@android:color/transparent"
+ android:layout_gravity="center"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:clickable="false"
+ android:focusable="false"/>
+ <TextView
+ android:id="@+id/silence_label"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:ellipsize="end"
+ android:maxLines="1"
+ android:clickable="false"
+ android:focusable="false"
+ android:layout_toEndOf="@id/silence_icon"
+ android:layout_marginStart="@dimen/notification_importance_drawable_padding"
+ android:textAppearance="@style/TextAppearance.NotificationImportanceButton.Unselected"
+ android:text="@string/notification_silence_title"/>
+ <TextView
+ android:id="@+id/silence_summary"
+ android:paddingTop="@dimen/notification_importance_button_padding"
+ android:text="@string/notification_channel_summary_default"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:clickable="false"
+ android:focusable="false"
+ android:ellipsize="end"
+ android:maxLines="2"
+ android:layout_below="@id/silence_icon"
+ android:textAppearance="@style/TextAppearance.NotificationImportanceDetail"/>
+ </RelativeLayout>
+
</LinearLayout>
<RelativeLayout
android:id="@+id/bottom_buttons"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:paddingTop="@dimen/notification_guts_button_spacing" >
+ android:layout_marginTop="@dimen/notification_guts_button_spacing" >
<TextView
android:id="@+id/turn_off_notifications"
android:text="@string/inline_turn_off_notifications"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
- android:layout_centerVertical="true"
android:minWidth="@dimen/notification_importance_toggle_size"
android:minHeight="@dimen/notification_importance_toggle_size"
android:maxWidth="200dp"
@@ -286,11 +326,11 @@
android:text="@string/inline_ok_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_centerVertical="true"
+ android:layout_alignParentEnd="true"
+ android:gravity="center_vertical|end"
android:maxWidth="125dp"
android:minWidth="@dimen/notification_importance_toggle_size"
android:minHeight="@dimen/notification_importance_toggle_size"
- android:layout_alignParentEnd="true"
style="@style/TextAppearance.NotificationInfo.Button"/>
</RelativeLayout>
diff --git a/packages/SystemUI/res/layout/quick_qs_status_icons.xml b/packages/SystemUI/res/layout/quick_qs_status_icons.xml
index 7f69cf4..5b7e7e7 100644
--- a/packages/SystemUI/res/layout/quick_qs_status_icons.xml
+++ b/packages/SystemUI/res/layout/quick_qs_status_icons.xml
@@ -19,8 +19,8 @@
android:id="@+id/quick_qs_status_icons"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginTop="@dimen/qs_header_top_margin"
- android:layout_marginBottom="14dp"
+ android:paddingTop="@dimen/qs_header_top_padding"
+ android:paddingBottom="@dimen/qs_header_bottom_padding"
android:paddingStart="@dimen/status_bar_padding_start"
android:paddingEnd="@dimen/status_bar_padding_end"
android:layout_below="@id/quick_status_bar_system_icons"
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 955cfb0..ce958ab 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -212,7 +212,7 @@
<dimen name="notification_guts_option_horizontal_padding">15dp</dimen>
<!-- The vertical space between items in the alert selections in the inline settings -->
- <dimen name="notification_guts_option_vertical_padding">24dp</dimen>
+ <dimen name="notification_guts_option_vertical_padding">20dp</dimen>
<!-- The vertical space between the alert selections in the inline settings -->
<dimen name="notification_guts_option_vertical_margin">6dp</dimen>
@@ -226,10 +226,12 @@
<dimen name="notification_importance_button_horiz_padding">28dp</dimen>
<dimen name="notification_importance_drawable_padding">8dp</dimen>
<dimen name="notification_importance_description_padding">20dp</dimen>
- <dimen name="notification_importance_description_text">12sp</dimen>
+ <dimen name="notification_importance_header_text">12sp</dimen>
+ <dimen name="notification_importance_description_text">14sp</dimen>
<dimen name="notification_importance_channel_text">16sp</dimen>
<dimen name="notification_importance_channel_group_text">14sp</dimen>
<dimen name="notification_importance_button_text">16sp</dimen>
+ <dimen name="notification_importance_button_padding">14dp</dimen>
<dimen name="rect_button_radius">8dp</dimen>
<!-- The minimum height for the snackbar shown after the snooze option has been chosen. -->
@@ -492,7 +494,8 @@
<dimen name="qs_footer_padding_end">16dp</dimen>
<dimen name="qs_footer_icon_size">16dp</dimen>
<dimen name="qs_paged_tile_layout_padding_bottom">0dp</dimen>
- <dimen name="qs_header_top_margin">15dp</dimen>
+ <dimen name="qs_header_top_padding">15dp</dimen>
+ <dimen name="qs_header_bottom_padding">14dp</dimen>
<dimen name="qs_notif_collapsed_space">64dp</dimen>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 60d7126..cea336c 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1659,19 +1659,19 @@
<string name="notification_alert_title">Prioritized</string>
<!-- [CHAR LIMIT=150] Notification Importance title: low importance level summary -->
- <string name="notification_channel_summary_low">Always silent. Displays in pull-down shade.</string>
+ <string name="notification_channel_summary_low">Helps you focus with notifications only in the pull-down shade. Always silent.</string>
<!-- [CHAR LIMIT=150] Notification Importance title: low importance level summary -->
- <string name="notification_channel_summary_low_status">Always silent. Displays in pull-down shade & status bar.</string>
+ <string name="notification_channel_summary_low_status">Displays below priority notifications. Always silent.</string>
<!-- [CHAR LIMIT=150] Notification Importance title: low importance level summary -->
- <string name="notification_channel_summary_low_lock">Always silent. Displays in pull-down shade & on lock screen.</string>
+ <string name="notification_channel_summary_low_lock">Displays below priority notifications. Always silent.</string>
<!-- [CHAR LIMIT=150] Notification Importance title: low importance level summary -->
- <string name="notification_channel_summary_low_status_lock">Always silent. Displays in pull-down shade, status bar & on lock screen.</string>
+ <string name="notification_channel_summary_low_status_lock">Displays below priority notifications. Always silent.</string>
<!-- [CHAR LIMIT=150] Notification Importance title: normal importance level summary -->
- <string name="notification_channel_summary_default">Makes sound and displays in pull-down shade, status bar & on lock screen.</string>
+ <string name="notification_channel_summary_default">Gets your attention with sound & a status bar icon. Shows on lock screen.</string>
<!-- Notification: Control panel: Label that displays when the app's notifications cannot be blocked. -->
<string name="notification_unblockable_desc">These notifications can\'t be modified.</string>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index d765f0c..23de2ac 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -473,7 +473,7 @@
</style>
<style name="TextAppearance.NotificationImportanceHeader">
- <item name="android:textSize">@dimen/notification_importance_description_text</item>
+ <item name="android:textSize">@dimen/notification_importance_header_text</item>
<item name="android:fontFamily">@*android:string/config_bodyFontFamily</item>
<item name="android:textColor">@color/notification_guts_header_text_color</item>
</style>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java
index 1a684a0..40d98c1 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java
@@ -295,8 +295,10 @@
info.serviceInfo.name);
PluginInfo<T> t = handleLoadPlugin(name);
if (t == null) continue;
- mMainHandler.obtainMessage(mMainHandler.PLUGIN_CONNECTED, t).sendToTarget();
+
+ // add plugin before sending PLUGIN_CONNECTED message
mPlugins.add(t);
+ mMainHandler.obtainMessage(mMainHandler.PLUGIN_CONNECTED, t).sendToTarget();
}
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
index 2c5fa60..b826b30 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
@@ -38,11 +38,6 @@
void startScreenPinning(int taskId) = 1;
/**
- * Enables/disables launcher/overview interaction features {@link InteractionType}.
- */
- void setInteractionState(int flags) = 4;
-
- /**
* Notifies SystemUI that split screen has been invoked.
*/
void onSplitScreenInvoked() = 5;
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/NavigationBarCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/NavigationBarCompat.java
deleted file mode 100644
index 8d6f830..0000000
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/NavigationBarCompat.java
+++ /dev/null
@@ -1,60 +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 com.android.systemui.shared.system;
-
-import android.annotation.IntDef;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-public class NavigationBarCompat extends QuickStepContract {
-
- @Retention(RetentionPolicy.SOURCE)
- @IntDef({HIT_TARGET_NONE, HIT_TARGET_BACK, HIT_TARGET_HOME, HIT_TARGET_OVERVIEW})
- public @interface HitTarget{}
-
- public static final int HIT_TARGET_NONE = 0;
- public static final int HIT_TARGET_BACK = 1;
- public static final int HIT_TARGET_HOME = 2;
- public static final int HIT_TARGET_OVERVIEW = 3;
- public static final int HIT_TARGET_ROTATION = 4;
- public static final int HIT_TARGET_DEAD_ZONE = 5;
-
- @Retention(RetentionPolicy.SOURCE)
- @IntDef({FLAG_DISABLE_SWIPE_UP,
- FLAG_DISABLE_QUICK_SCRUB,
- FLAG_SHOW_OVERVIEW_BUTTON
- })
- public @interface InteractionType {}
-
- /**
- * Interaction type: whether the gesture to swipe up from the navigation bar will trigger
- * launcher to show overview
- */
- public static final int FLAG_DISABLE_SWIPE_UP = 0x1;
- /**
- * Interaction type: enable quick scrub interaction on the home button
- */
- public static final int FLAG_DISABLE_QUICK_SCRUB = 0x2;
-
- /**
- * Interaction type: show/hide the overview button while this service is connected to launcher
- */
- public static final int FLAG_SHOW_OVERVIEW_BUTTON = 0x4;
-
-
-}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java
index 0a6885c..037a8d3 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java
@@ -59,14 +59,22 @@
private ColorStateList mDefaultColorState;
private CharSequence mMessage;
private ColorStateList mNextMessageColorState = ColorStateList.valueOf(DEFAULT_COLOR);
+ private boolean mBouncerVisible;
private KeyguardUpdateMonitorCallback mInfoCallback = new KeyguardUpdateMonitorCallback() {
public void onFinishedGoingToSleep(int why) {
setSelected(false);
- };
+ }
+
public void onStartedWakingUp() {
setSelected(true);
- };
+ }
+
+ @Override
+ public void onKeyguardBouncerChanged(boolean bouncer) {
+ mBouncerVisible = bouncer;
+ update();
+ }
};
public KeyguardMessageArea(Context context) {
@@ -188,7 +196,7 @@
private void update() {
CharSequence status = mMessage;
- setVisibility(TextUtils.isEmpty(status) ? INVISIBLE : VISIBLE);
+ setVisibility(TextUtils.isEmpty(status) || !mBouncerVisible ? INVISIBLE : VISIBLE);
setText(status);
ColorStateList colorState = mDefaultColorState;
if (mNextMessageColorState.getDefaultColor() != DEFAULT_COLOR) {
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index 1feb63d..4b6306a 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -35,6 +35,7 @@
import com.android.systemui.assist.AssistManager;
import com.android.systemui.bubbles.BubbleController;
import com.android.systemui.colorextraction.SysuiColorExtractor;
+import com.android.systemui.dock.DockManager;
import com.android.systemui.fragments.FragmentService;
import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.keyguard.WakefulnessLifecycle;
@@ -296,6 +297,7 @@
@Inject Lazy<PackageManagerWrapper> mPackageManagerWrapper;
@Inject Lazy<SensorPrivacyController> mSensorPrivacyController;
@Inject Lazy<DumpController> mDumpController;
+ @Inject Lazy<DockManager> mDockManager;
@Inject
public Dependency() {
@@ -470,6 +472,7 @@
mProviders.put(PackageManagerWrapper.class, mPackageManagerWrapper::get);
mProviders.put(SensorPrivacyController.class, mSensorPrivacyController::get);
mProviders.put(DumpController.class, mDumpController::get);
+ mProviders.put(DockManager.class, mDockManager::get);
// TODO(b/118592525): to support multi-display , we start to add something which is
// per-display, while others may be global. I think it's time to add
diff --git a/packages/SystemUI/src/com/android/systemui/Prefs.java b/packages/SystemUI/src/com/android/systemui/Prefs.java
index 2797570..10ae343 100644
--- a/packages/SystemUI/src/com/android/systemui/Prefs.java
+++ b/packages/SystemUI/src/com/android/systemui/Prefs.java
@@ -57,7 +57,6 @@
Key.SEEN_RINGER_GUIDANCE_COUNT,
Key.QS_HAS_TURNED_OFF_MOBILE_DATA,
Key.TOUCHED_RINGER_TOGGLE,
- Key.QUICK_STEP_INTERACTION_FLAGS,
Key.HAS_SEEN_ODI_CAPTIONS_TOOLTIP
})
public @interface Key {
@@ -103,7 +102,6 @@
String QS_TILE_SPECS_REVEALED = "QsTileSpecsRevealed";
String QS_HAS_TURNED_OFF_MOBILE_DATA = "QsHasTurnedOffMobileData";
String TOUCHED_RINGER_TOGGLE = "TouchedRingerToggle";
- String QUICK_STEP_INTERACTION_FLAGS = "QuickStepInteractionFlags";
String HAS_SEEN_ODI_CAPTIONS_TOOLTIP = "HasSeenODICaptionsTooltip";
}
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
index ffb5e81..f9926f3 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
@@ -34,6 +34,7 @@
import com.android.keyguard.ViewMediatorCallback;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.classifier.FalsingManager;
+import com.android.systemui.dock.DockManager;
import com.android.systemui.fragments.FragmentService;
import com.android.systemui.keyguard.DismissCallbackRegistry;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -179,6 +180,13 @@
@Singleton
@Provides
+ @Nullable
+ public DockManager provideDockManager(Context context) {
+ return null;
+ }
+
+ @Singleton
+ @Provides
public NotificationEntryManager provideNotificationEntryManager(Context context) {
return new NotificationEntryManager(context);
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
index 0332477..7094d28 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
@@ -16,9 +16,12 @@
package com.android.systemui.bubbles;
+import static com.android.internal.annotations.VisibleForTesting.Visibility.PRIVATE;
+
import android.os.UserHandle;
import android.view.LayoutInflater;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.R;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -40,15 +43,24 @@
public NotificationEntry entry;
BubbleView iconView;
BubbleExpandedView expandedView;
+ private long mLastUpdated;
+ private long mLastAccessed;
private static String groupId(NotificationEntry entry) {
UserHandle user = entry.notification.getUser();
- return user.getIdentifier() + '|' + entry.notification.getPackageName();
+ return user.getIdentifier() + "|" + entry.notification.getPackageName();
+ }
+
+ /** Used in tests when no UI is required. */
+ @VisibleForTesting(visibility = PRIVATE)
+ Bubble(NotificationEntry e) {
+ this (e, null);
}
Bubble(NotificationEntry e, BubbleExpandedView.OnBubbleBlockedListener listener) {
entry = e;
mKey = e.key;
+ mLastUpdated = e.notification.getPostTime();
mGroupId = groupId(e);
mListener = listener;
}
@@ -101,12 +113,37 @@
void setEntry(NotificationEntry entry) {
this.entry = entry;
+ mLastUpdated = entry.notification.getPostTime();
if (mInflated) {
iconView.update(entry);
expandedView.update(entry);
}
}
+ public long getLastActivity() {
+ return Math.max(mLastUpdated, mLastAccessed);
+ }
+
+ /**
+ * Should be invoked whenever a Bubble is accessed (selected while expanded).
+ */
+ void markAsAccessedAt(long lastAccessedMillis) {
+ mLastAccessed = lastAccessedMillis;
+ entry.setShowInShadeWhenBubble(false);
+ }
+
+ /**
+ * @return whether bubble is from a notification associated with a foreground service.
+ */
+ public boolean isOngoing() {
+ return entry.isForegroundService();
+ }
+
+ @Override
+ public String toString() {
+ return "Bubble{" + mKey + '}';
+ }
+
@Override
public boolean equals(Object o) {
if (this == o) return true;
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index ef383ad..d071363 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -41,6 +41,7 @@
import android.os.ServiceManager;
import android.provider.Settings;
import android.service.notification.StatusBarNotification;
+import android.util.Log;
import android.view.Display;
import android.view.IPinnedStackController;
import android.view.IPinnedStackListener;
@@ -83,6 +84,7 @@
public class BubbleController implements ConfigurationController.ConfigurationListener {
private static final String TAG = "BubbleController";
+ private static final boolean DEBUG = true;
@Retention(SOURCE)
@IntDef({DISMISS_USER_GESTURE, DISMISS_AGED, DISMISS_TASK_FINISHED, DISMISS_BLOCKED,
@@ -203,6 +205,9 @@
configurationController.addCallback(this /* configurationListener */);
+ mBubbleData = data;
+ mBubbleData.setListener(mBubbleDataListener);
+
mNotificationEntryManager = Dependency.get(NotificationEntryManager.class);
mNotificationEntryManager.addNotificationEntryListener(mEntryListener);
@@ -219,9 +224,6 @@
} catch (RemoteException e) {
e.printStackTrace();
}
-
- mBubbleData = data;
- mBubbleData.setListener(mBubbleDataListener);
mSurfaceSynchronizer = synchronizer;
mBarService = IStatusBarService.Stub.asInterface(
@@ -482,7 +484,7 @@
}
@Override
- public void onSelectionChanged(Bubble selectedBubble) {
+ public void onSelectionChanged(@Nullable Bubble selectedBubble) {
if (mStackView != null) {
mStackView.setSelectedBubble(selectedBubble);
}
@@ -506,6 +508,18 @@
public void apply() {
mNotificationEntryManager.updateNotifications();
updateVisibility();
+
+ if (DEBUG) {
+ Log.d(TAG, "[BubbleData]");
+ Log.d(TAG, formatBubblesString(mBubbleData.getBubbles(),
+ mBubbleData.getSelectedBubble()));
+
+ if (mStackView != null) {
+ Log.d(TAG, "[BubbleStackView]");
+ Log.d(TAG, formatBubblesString(mStackView.getBubblesOnScreen(),
+ mStackView.getExpandedBubble()));
+ }
+ }
}
};
@@ -623,6 +637,23 @@
entry.setShowInShadeWhenBubble(!suppressNotification);
}
+ static String formatBubblesString(List<Bubble> bubbles, Bubble selected) {
+ StringBuilder sb = new StringBuilder();
+ for (Bubble bubble : bubbles) {
+ if (bubble == null) {
+ sb.append(" <null> !!!!!\n");
+ } else {
+ boolean isSelected = (bubble == selected);
+ sb.append(String.format("%s Bubble{act=%12d, ongoing=%d, key=%s}\n",
+ ((isSelected) ? "->" : " "),
+ bubble.getLastActivity(),
+ (bubble.isOngoing() ? 1 : 0),
+ bubble.getKey()));
+ }
+ }
+ return sb.toString();
+ }
+
/**
* Return true if the applications with the package name is running in foreground.
*
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
index 259665d..38ba91e 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
@@ -17,21 +17,26 @@
import static com.android.internal.annotations.VisibleForTesting.Visibility.PRIVATE;
-import android.app.ActivityManager;
+import static java.util.stream.Collectors.toList;
+
import android.app.Notification;
import android.app.PendingIntent;
import android.content.Context;
import android.util.Log;
+import androidx.annotation.Nullable;
+
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.bubbles.BubbleController.DismissReason;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import java.util.ArrayList;
-import java.util.Collection;
import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
+import java.util.Map;
import java.util.Objects;
import javax.inject.Inject;
@@ -44,6 +49,15 @@
public class BubbleData {
private static final String TAG = "BubbleData";
+ private static final boolean DEBUG = false;
+
+ private static final int MAX_BUBBLES = 5;
+
+ private static final Comparator<Bubble> BUBBLES_BY_LAST_ACTIVITY_DESCENDING =
+ Comparator.comparing(Bubble::getLastActivity).reversed();
+
+ private static final Comparator<Map.Entry<String, Long>> GROUPS_BY_LAST_ACTIVITY_DESCENDING =
+ Comparator.<Map.Entry<String, Long>, Long>comparing(Map.Entry::getValue).reversed();
/**
* This interface reports changes to the state and appearance of bubbles which should be applied
@@ -83,7 +97,7 @@
void onOrderChanged(List<Bubble> bubbles);
/** Indicates the selected bubble changed. */
- void onSelectionChanged(Bubble selectedBubble);
+ void onSelectionChanged(@Nullable Bubble selectedBubble);
/**
* The UI should transition to the given state, incorporating any pending changes during
@@ -98,16 +112,28 @@
void apply();
}
+ interface TimeSource {
+ long currentTimeMillis();
+ }
+
private final Context mContext;
- private final List<Bubble> mBubbles = new ArrayList<>();
+ private List<Bubble> mBubbles;
private Bubble mSelectedBubble;
private boolean mExpanded;
+
+ // TODO: ensure this is invalidated at the appropriate time
+ private int mSelectedBubbleExpandedPosition = -1;
+
+ private TimeSource mTimeSource = System::currentTimeMillis;
+
+ @Nullable
private Listener mListener;
@VisibleForTesting
@Inject
public BubbleData(Context context) {
mContext = context;
+ mBubbles = new ArrayList<>();
}
public boolean hasBubbles() {
@@ -122,29 +148,41 @@
return getBubbleWithKey(key) != null;
}
+ @Nullable
+ public Bubble getSelectedBubble() {
+ return mSelectedBubble;
+ }
+
public void setExpanded(boolean expanded) {
if (setExpandedInternal(expanded)) {
- mListener.apply();
+ dispatchApply();
}
}
public void setSelectedBubble(Bubble bubble) {
+ if (DEBUG) {
+ Log.d(TAG, "setSelectedBubble: " + bubble);
+ }
if (setSelectedBubbleInternal(bubble)) {
- mListener.apply();
+ dispatchApply();
}
}
public void notificationEntryUpdated(NotificationEntry entry) {
+ if (DEBUG) {
+ Log.d(TAG, "notificationEntryUpdated: " + entry);
+ }
Bubble bubble = getBubbleWithKey(entry.key);
if (bubble == null) {
// Create a new bubble
bubble = new Bubble(entry, this::onBubbleBlocked);
- mBubbles.add(0, bubble); // TODO: reorder/group
- mListener.onBubbleAdded(bubble);
+ doAdd(bubble);
+ dispatchOnBubbleAdded(bubble);
} else {
// Updates an existing bubble
bubble.setEntry(entry);
- mListener.onBubbleUpdated(bubble);
+ doUpdate(bubble);
+ dispatchOnBubbleUpdated(bubble);
}
if (shouldAutoExpand(entry)) {
setSelectedBubbleInternal(bubble);
@@ -154,49 +192,148 @@
} else if (mSelectedBubble == null) {
setSelectedBubbleInternal(bubble);
}
- // TODO: reorder/group
- mListener.apply();
+ dispatchApply();
+ }
+
+ private void doAdd(Bubble bubble) {
+ if (DEBUG) {
+ Log.d(TAG, "doAdd: " + bubble);
+ }
+ int minInsertPoint = 0;
+ boolean newGroup = !hasBubbleWithGroupId(bubble.getGroupId());
+ if (isExpanded()) {
+ // first bubble of a group goes to the end, otherwise it goes within the existing group
+ minInsertPoint =
+ newGroup ? mBubbles.size() : findFirstIndexForGroup(bubble.getGroupId());
+ }
+ insertBubble(minInsertPoint, bubble);
+ if (!isExpanded()) {
+ packGroup(findFirstIndexForGroup(bubble.getGroupId()));
+ }
+ if (mBubbles.size() > MAX_BUBBLES) {
+ mBubbles.stream()
+ // sort oldest first (ascending lastActivity)
+ .sorted(Comparator.comparingLong(Bubble::getLastActivity))
+ // skip the selected bubble
+ .filter((b) -> !b.equals(mSelectedBubble))
+ .findFirst()
+ .ifPresent((b) -> {
+ doRemove(b.getKey(), BubbleController.DISMISS_AGED);
+ dispatchApply();
+ });
+ }
+ }
+
+ private void doUpdate(Bubble bubble) {
+ if (DEBUG) {
+ Log.d(TAG, "doUpdate: " + bubble);
+ }
+ if (!isExpanded()) {
+ // while collapsed, update causes re-sort
+ mBubbles.remove(bubble);
+ insertBubble(0, bubble);
+ packGroup(findFirstIndexForGroup(bubble.getGroupId()));
+ }
}
public void notificationEntryRemoved(NotificationEntry entry, @DismissReason int reason) {
- int indexToRemove = indexForKey(entry.key);
- if (indexToRemove >= 0) {
- Bubble removed = mBubbles.remove(indexToRemove);
- removed.setDismissed();
- mListener.onBubbleRemoved(removed, reason);
- maybeSendDeleteIntent(reason, removed.entry);
+ if (DEBUG) {
+ Log.d(TAG, "notificationEntryRemoved: entry=" + entry + " reason=" + reason);
+ }
+ doRemove(entry.key, reason);
+ dispatchApply();
+ }
- if (mBubbles.isEmpty()) {
+ private void doRemove(String key, @DismissReason int reason) {
+ int indexToRemove = indexForKey(key);
+ if (indexToRemove >= 0) {
+ Bubble bubbleToRemove = mBubbles.get(indexToRemove);
+ if (mBubbles.size() == 1) {
+ // Going to become empty, handle specially.
setExpandedInternal(false);
setSelectedBubbleInternal(null);
- } else if (removed == mSelectedBubble) {
+ }
+ mBubbles.remove(indexToRemove);
+ dispatchOnBubbleRemoved(bubbleToRemove, reason);
+
+ // Note: If mBubbles.isEmpty(), then mSelectedBubble is now null.
+ if (Objects.equals(mSelectedBubble, bubbleToRemove)) {
+ // Move selection to the new bubble at the same position.
int newIndex = Math.min(indexToRemove, mBubbles.size() - 1);
Bubble newSelected = mBubbles.get(newIndex);
setSelectedBubbleInternal(newSelected);
}
- // TODO: reorder/group
- mListener.apply();
+ bubbleToRemove.setDismissed();
+ maybeSendDeleteIntent(reason, bubbleToRemove.entry);
}
}
public void dismissAll(@DismissReason int reason) {
- boolean changed = setExpandedInternal(false);
+ if (DEBUG) {
+ Log.d(TAG, "dismissAll: reason=" + reason);
+ }
+ if (mBubbles.isEmpty()) {
+ return;
+ }
+ setExpandedInternal(false);
+ setSelectedBubbleInternal(null);
while (!mBubbles.isEmpty()) {
Bubble bubble = mBubbles.remove(0);
bubble.setDismissed();
maybeSendDeleteIntent(reason, bubble.entry);
- mListener.onBubbleRemoved(bubble, reason);
- changed = true;
+ dispatchOnBubbleRemoved(bubble, reason);
}
- if (setSelectedBubbleInternal(null)) {
- changed = true;
- }
- if (changed) {
- // TODO: reorder/group
+ dispatchApply();
+ }
+
+ private void dispatchApply() {
+ if (mListener != null) {
mListener.apply();
}
}
+ private void dispatchOnBubbleAdded(Bubble bubble) {
+ if (mListener != null) {
+ mListener.onBubbleAdded(bubble);
+ }
+ }
+
+ private void dispatchOnBubbleRemoved(Bubble bubble, @DismissReason int reason) {
+ if (mListener != null) {
+ mListener.onBubbleRemoved(bubble, reason);
+ }
+ }
+
+ private void dispatchOnExpandedChanged(boolean expanded) {
+ if (mListener != null) {
+ mListener.onExpandedChanged(expanded);
+ }
+ }
+
+ private void dispatchOnSelectionChanged(@Nullable Bubble bubble) {
+ if (mListener != null) {
+ mListener.onSelectionChanged(bubble);
+ }
+ }
+
+ private void dispatchOnBubbleUpdated(Bubble bubble) {
+ if (mListener != null) {
+ mListener.onBubbleUpdated(bubble);
+ }
+ }
+
+ private void dispatchOnOrderChanged(List<Bubble> bubbles) {
+ if (mListener != null) {
+ mListener.onOrderChanged(bubbles);
+ }
+ }
+
+ private void dispatchShowFlyoutText(Bubble bubble, String text) {
+ if (mListener != null) {
+ mListener.showFlyoutText(bubble, text);
+ }
+ }
+
/**
* Requests a change to the selected bubble. Calls {@link Listener#onSelectionChanged} if
* the value changes.
@@ -204,7 +341,10 @@
* @param bubble the new selected bubble
* @return true if the state changed as a result
*/
- private boolean setSelectedBubbleInternal(Bubble bubble) {
+ private boolean setSelectedBubbleInternal(@Nullable Bubble bubble) {
+ if (DEBUG) {
+ Log.d(TAG, "setSelectedBubbleInternal: " + bubble);
+ }
if (Objects.equals(bubble, mSelectedBubble)) {
return false;
}
@@ -213,16 +353,17 @@
+ " (" + bubble + ") bubbles=" + mBubbles);
return false;
}
- if (mExpanded) {
- // TODO: bubble.markAsActive() ?
- bubble.entry.setShowInShadeWhenBubble(false);
+ if (mExpanded && bubble != null) {
+ mSelectedBubble.markAsAccessedAt(mTimeSource.currentTimeMillis());
}
mSelectedBubble = bubble;
- mListener.onSelectionChanged(mSelectedBubble);
+ dispatchOnSelectionChanged(mSelectedBubble);
+ if (!mExpanded || mSelectedBubble == null) {
+ mSelectedBubbleExpandedPosition = -1;
+ }
return true;
}
-
/**
* Requests a change to the expanded state. Calls {@link Listener#onExpandedChanged} if
* the value changes.
@@ -231,9 +372,15 @@
* @return true if the state changed as a result
*/
private boolean setExpandedInternal(boolean shouldExpand) {
+ if (DEBUG) {
+ Log.d(TAG, "setExpandedInternal: shouldExpand=" + shouldExpand);
+ }
if (mExpanded == shouldExpand) {
return false;
}
+ if (mSelectedBubble != null) {
+ mSelectedBubble.markAsAccessedAt(mTimeSource.currentTimeMillis());
+ }
if (shouldExpand) {
if (mBubbles.isEmpty()) {
Log.e(TAG, "Attempt to expand stack when empty!");
@@ -243,15 +390,126 @@
Log.e(TAG, "Attempt to expand stack without selected bubble!");
return false;
}
- // TODO: bubble.markAsActive() ?
- mSelectedBubble.entry.setShowInShadeWhenBubble(false);
+ } else {
+ repackAll();
}
- // TODO: reorder/regroup
mExpanded = shouldExpand;
- mListener.onExpandedChanged(mExpanded);
+ dispatchOnExpandedChanged(mExpanded);
return true;
}
+ private static long sortKey(Bubble bubble) {
+ long key = bubble.getLastActivity();
+ if (bubble.isOngoing()) {
+ // Set 2nd highest bit (signed long int), to partition between ongoing and regular
+ key |= 0x4000000000000000L;
+ }
+ return key;
+ }
+
+ /**
+ * Locates and inserts the bubble into a sorted position. The is inserted
+ * based on sort key, groupId is not considered. A call to {@link #packGroup(int)} may be
+ * required to keep grouping intact.
+ *
+ * @param minPosition the first insert point to consider
+ * @param newBubble the bubble to insert
+ * @return the position where the bubble was inserted
+ */
+ private int insertBubble(int minPosition, Bubble newBubble) {
+ long newBubbleSortKey = sortKey(newBubble);
+ String previousGroupId = null;
+
+ for (int pos = minPosition; pos < mBubbles.size(); pos++) {
+ Bubble bubbleAtPos = mBubbles.get(pos);
+ String groupIdAtPos = bubbleAtPos.getGroupId();
+ boolean atStartOfGroup = !groupIdAtPos.equals(previousGroupId);
+
+ if (atStartOfGroup && newBubbleSortKey > sortKey(bubbleAtPos)) {
+ // Insert before the start of first group which has older bubbles.
+ mBubbles.add(pos, newBubble);
+ return pos;
+ }
+ previousGroupId = groupIdAtPos;
+ }
+ mBubbles.add(newBubble);
+ return mBubbles.size() - 1;
+ }
+
+ private boolean hasBubbleWithGroupId(String groupId) {
+ return mBubbles.stream().anyMatch(b -> b.getGroupId().equals(groupId));
+ }
+
+ private int findFirstIndexForGroup(String appId) {
+ for (int i = 0; i < mBubbles.size(); i++) {
+ Bubble bubbleAtPos = mBubbles.get(i);
+ if (bubbleAtPos.getGroupId().equals(appId)) {
+ return i;
+ }
+ }
+ return 0;
+ }
+
+ /**
+ * Starting at the given position, moves all bubbles with the same group id to follow. Bubbles
+ * at positions lower than {@code position} are unchanged. Relative order within the group
+ * unchanged. Relative order of any other bubbles are also unchanged.
+ *
+ * @param position the position of the first bubble for the group
+ */
+ private void packGroup(int position) {
+ if (DEBUG) {
+ Log.d(TAG, "packGroup: position=" + position);
+ }
+ Bubble groupStart = mBubbles.get(position);
+ final String groupAppId = groupStart.getGroupId();
+ List<Bubble> moving = new ArrayList<>();
+
+ // Walk backward, collect bubbles within the group
+ for (int i = mBubbles.size() - 1; i > position; i--) {
+ if (mBubbles.get(i).getGroupId().equals(groupAppId)) {
+ moving.add(0, mBubbles.get(i));
+ }
+ }
+ mBubbles.removeAll(moving);
+ mBubbles.addAll(position + 1, moving);
+ }
+
+ private void repackAll() {
+ if (DEBUG) {
+ Log.d(TAG, "repackAll()");
+ }
+ if (mBubbles.isEmpty()) {
+ return;
+ }
+ Map<String, Long> groupLastActivity = new HashMap<>();
+ for (Bubble bubble : mBubbles) {
+ long maxSortKeyForGroup = groupLastActivity.getOrDefault(bubble.getGroupId(), 0L);
+ long sortKeyForBubble = sortKey(bubble);
+ if (sortKeyForBubble > maxSortKeyForGroup) {
+ groupLastActivity.put(bubble.getGroupId(), sortKeyForBubble);
+ }
+ }
+
+ // Sort groups by their most recently active bubble
+ List<String> groupsByMostRecentActivity =
+ groupLastActivity.entrySet().stream()
+ .sorted(GROUPS_BY_LAST_ACTIVITY_DESCENDING)
+ .map(Map.Entry::getKey)
+ .collect(toList());
+
+ List<Bubble> repacked = new ArrayList<>(mBubbles.size());
+
+ // For each group, add bubbles, freshest to oldest
+ for (String appId : groupsByMostRecentActivity) {
+ mBubbles.stream()
+ .filter((b) -> b.getGroupId().equals(appId))
+ .sorted(BUBBLES_BY_LAST_ACTIVITY_DESCENDING)
+ .forEachOrdered(repacked::add);
+ }
+ mBubbles = repacked;
+ }
+
private void maybeSendDeleteIntent(@DismissReason int reason, NotificationEntry entry) {
if (reason == BubbleController.DISMISS_USER_GESTURE) {
Notification.BubbleMetadata bubbleMetadata = entry.getBubbleMetadata();
@@ -275,13 +533,14 @@
Bubble bubble = i.next();
if (bubble.getPackageName().equals(blockedPackage)) {
i.remove();
- mListener.onBubbleRemoved(bubble, BubbleController.DISMISS_BLOCKED);
+ // TODO: handle removal of selected bubble, and collapse safely if emptied (see
+ // dismissAll)
+ dispatchOnBubbleRemoved(bubble, BubbleController.DISMISS_BLOCKED);
changed = true;
}
}
if (changed) {
- // TODO: reorder/group
- mListener.apply();
+ dispatchApply();
}
}
@@ -295,24 +554,11 @@
return -1;
}
- private Bubble removeBubbleWithKey(String key) {
- for (int i = 0; i < mBubbles.size(); i++) {
- Bubble bubble = mBubbles.get(i);
- if (bubble.getKey().equals(key)) {
- mBubbles.remove(i);
- return bubble;
- }
- }
- return null;
- }
-
/**
* The set of bubbles.
- *
- * @deprecated
*/
- @Deprecated
- public Collection<Bubble> getBubbles() {
+ @VisibleForTesting(visibility = PRIVATE)
+ public List<Bubble> getBubbles() {
return Collections.unmodifiableList(mBubbles);
}
@@ -327,6 +573,11 @@
return null;
}
+ @VisibleForTesting(visibility = PRIVATE)
+ void setTimeSource(TimeSource timeSource) {
+ mTimeSource = timeSource;
+ }
+
public void setListener(Listener listener) {
mListener = listener;
}
@@ -334,17 +585,6 @@
boolean shouldAutoExpand(NotificationEntry entry) {
Notification.BubbleMetadata metadata = entry.getBubbleMetadata();
return metadata != null && metadata.getAutoExpandBubble()
- && isForegroundApp(entry.notification.getPackageName());
- }
-
- /**
- * Return true if the applications with the package name is running in foreground.
- *
- * @param pkgName application package name.
- */
- boolean isForegroundApp(String pkgName) {
- ActivityManager am = mContext.getSystemService(ActivityManager.class);
- List<ActivityManager.RunningTaskInfo> tasks = am.getRunningTasks(1 /* maxNum */);
- return !tasks.isEmpty() && pkgName.equals(tasks.get(0).topActivity.getPackageName());
+ && BubbleController.isForegroundApp(mContext, entry.notification.getPackageName());
}
}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index 7029931..d1bc9a9 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -66,6 +66,7 @@
import java.math.BigDecimal;
import java.math.RoundingMode;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -227,7 +228,7 @@
mBubbleData = data;
mInflater = LayoutInflater.from(context);
- mTouchHandler = new BubbleTouchHandler(context, this);
+ mTouchHandler = new BubbleTouchHandler(this, data, context);
setOnTouchListener(mTouchHandler);
mInflater = LayoutInflater.from(context);
@@ -503,6 +504,9 @@
// via BubbleData.Listener
void addBubble(Bubble bubble) {
+ if (DEBUG) {
+ Log.d(TAG, "addBubble: " + bubble);
+ }
bubble.inflate(mInflater, this);
mBubbleContainer.addView(bubble.iconView, 0,
new FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
@@ -513,10 +517,17 @@
// via BubbleData.Listener
void removeBubble(Bubble bubble) {
+ if (DEBUG) {
+ Log.d(TAG, "removeBubble: " + bubble);
+ }
// Remove it from the views
int removedIndex = mBubbleContainer.indexOfChild(bubble.iconView);
- mBubbleContainer.removeViewAt(removedIndex);
- logBubbleEvent(bubble, StatsLog.BUBBLE_UICHANGED__ACTION__DISMISSED);
+ if (removedIndex >= 0) {
+ mBubbleContainer.removeViewAt(removedIndex);
+ logBubbleEvent(bubble, StatsLog.BUBBLE_UICHANGED__ACTION__DISMISSED);
+ } else {
+ Log.d(TAG, "was asked to remove Bubble, but didn't find the view! " + bubble);
+ }
}
// via BubbleData.Listener
@@ -531,7 +542,10 @@
* position of any bubble.
*/
// via BubbleData.Listener
- public void setSelectedBubble(Bubble bubbleToSelect) {
+ public void setSelectedBubble(@Nullable Bubble bubbleToSelect) {
+ if (DEBUG) {
+ Log.d(TAG, "setSelectedBubble: " + bubbleToSelect);
+ }
if (mExpandedBubble != null && mExpandedBubble.equals(bubbleToSelect)) {
return;
}
@@ -562,6 +576,9 @@
*/
// via BubbleData.Listener
public void setExpanded(boolean shouldExpand) {
+ if (DEBUG) {
+ Log.d(TAG, "setExpanded: " + shouldExpand);
+ }
boolean wasExpanded = mIsExpanded;
if (shouldExpand == wasExpanded) {
return;
@@ -586,6 +603,9 @@
*/
@Deprecated
void stackDismissed(int reason) {
+ if (DEBUG) {
+ Log.d(TAG, "stackDismissed: reason=" + reason);
+ }
mBubbleData.dismissAll(reason);
logBubbleEvent(null /* no bubble associated with bubble stack dismiss */,
StatsLog.BUBBLE_UICHANGED__ACTION__STACK_DISMISSED);
@@ -633,6 +653,9 @@
@Deprecated
@MainThread
void collapseStack() {
+ if (DEBUG) {
+ Log.d(TAG, "collapseStack()");
+ }
mBubbleData.setExpanded(false);
}
@@ -642,6 +665,9 @@
@Deprecated
@MainThread
void collapseStack(Runnable endRunnable) {
+ if (DEBUG) {
+ Log.d(TAG, "collapseStack(endRunnable)");
+ }
collapseStack();
// TODO - use the runnable at end of animation
endRunnable.run();
@@ -657,6 +683,9 @@
@Deprecated
@MainThread
void expandStack() {
+ if (DEBUG) {
+ Log.d(TAG, "expandStack()");
+ }
mBubbleData.setExpanded(true);
}
@@ -664,6 +693,9 @@
* Tell the stack to animate to collapsed or expanded state.
*/
private void animateExpansion(boolean shouldExpand) {
+ if (DEBUG) {
+ Log.d(TAG, "animateExpansion: shouldExpand=" + shouldExpand);
+ }
if (mIsExpanded != shouldExpand) {
hideFlyoutImmediate();
@@ -745,6 +777,9 @@
/** Called when a drag operation on an individual bubble has started. */
public void onBubbleDragStart(View bubble) {
+ if (DEBUG) {
+ Log.d(TAG, "onBubbleDragStart: bubble=" + bubble);
+ }
mExpandedAnimationController.prepareForBubbleDrag(bubble);
}
@@ -760,6 +795,9 @@
/** Called when a drag operation on an individual bubble has finished. */
public void onBubbleDragFinish(
View bubble, float x, float y, float velX, float velY, boolean dismissed) {
+ if (DEBUG) {
+ Log.d(TAG, "onBubbleDragFinish: bubble=" + bubble + ", dismissed=" + dismissed);
+ }
if (!mIsExpanded || mIsExpansionAnimating) {
return;
}
@@ -772,6 +810,9 @@
}
void onDragStart() {
+ if (DEBUG) {
+ Log.d(TAG, "onDragStart()");
+ }
if (mIsExpanded || mIsExpansionAnimating) {
return;
}
@@ -792,6 +833,9 @@
}
void onDragFinish(float x, float y, float velX, float velY) {
+ if (DEBUG) {
+ Log.d(TAG, "onDragFinish");
+ }
// TODO: Add fling to bottom to dismiss.
mIsDragging = false;
@@ -958,6 +1002,9 @@
}
private void updateExpandedBubble() {
+ if (DEBUG) {
+ Log.d(TAG, "updateExpandedBubble()");
+ }
mExpandedViewContainer.removeAllViews();
if (mExpandedBubble != null && mIsExpanded) {
mExpandedViewContainer.addView(mExpandedBubble.expandedView);
@@ -1036,7 +1083,9 @@
}
private void applyCurrentState() {
- Log.d(TAG, "applyCurrentState: mIsExpanded=" + mIsExpanded);
+ if (DEBUG) {
+ Log.d(TAG, "applyCurrentState: mIsExpanded=" + mIsExpanded);
+ }
mExpandedViewContainer.setVisibility(mIsExpanded ? VISIBLE : GONE);
if (mIsExpanded) {
// First update the view so that it calculates a new height (ensuring the y position
@@ -1075,10 +1124,14 @@
}
private void updatePointerPosition() {
- if (mExpandedBubble != null) {
- float pointerPosition = mExpandedBubble.iconView.getTranslationX()
- + (mExpandedBubble.iconView.getWidth() / 2f);
- mExpandedBubble.expandedView.setPointerPosition((int) pointerPosition);
+ if (DEBUG) {
+ Log.d(TAG, "updatePointerPosition()");
+ }
+ Bubble expandedBubble = getExpandedBubble();
+ if (expandedBubble != null) {
+ BubbleView iconView = expandedBubble.iconView;
+ float pointerPosition = iconView.getTranslationX() + (iconView.getWidth() / 2f);
+ expandedBubble.expandedView.setPointerPosition((int) pointerPosition);
}
}
@@ -1174,4 +1227,18 @@
}
return mExpandedBubble.expandedView.performBackPressIfNeeded();
}
+
+ /** For debugging only */
+ List<Bubble> getBubblesOnScreen() {
+ List<Bubble> bubbles = new ArrayList<>();
+ for (int i = 0; i < mBubbleContainer.getChildCount(); i++) {
+ View child = mBubbleContainer.getChildAt(i);
+ if (child instanceof BubbleView) {
+ String key = ((BubbleView) child).getKey();
+ Bubble bubble = mBubbleData.getBubbleWithKey(key);
+ bubbles.add(bubble);
+ }
+ }
+ return bubbles;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java
index a51d46c..82e6279 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java
@@ -37,9 +37,12 @@
/** Velocity required to dismiss a bubble without dragging it into the dismiss target. */
private static final float DISMISS_MIN_VELOCITY = 4000f;
+ private static final String TAG = "BubbleTouchHandler";
+
private final PointF mTouchDown = new PointF();
private final PointF mViewPositionOnTouchDown = new PointF();
private final BubbleStackView mStack;
+ private final BubbleData mBubbleData;
private BubbleController mController = Dependency.get(BubbleController.class);
private PipDismissViewController mDismissViewController;
@@ -60,10 +63,12 @@
/** View that was initially touched, when we received the first ACTION_DOWN event. */
private View mTouchedView;
- BubbleTouchHandler(Context context, BubbleStackView stackView) {
+ BubbleTouchHandler(BubbleStackView stackView,
+ BubbleData bubbleData, Context context) {
final int touchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
mTouchSlopSquared = touchSlop * touchSlop;
mDismissViewController = new PipDismissViewController(context);
+ mBubbleData = bubbleData;
mStack = stackView;
}
@@ -80,7 +85,7 @@
// If this is an ACTION_OUTSIDE event, or the stack reported that we aren't touching
// anything, collapse the stack.
if (action == MotionEvent.ACTION_OUTSIDE || mTouchedView == null) {
- mStack.collapseStack();
+ mBubbleData.setExpanded(false);
resetForNextGesture();
return false;
}
@@ -151,8 +156,8 @@
mStack.onDragFinishAsDismiss();
} else if (isFlyout) {
// TODO(b/129768381): Expand if tapped, dismiss if swiped away.
- if (!mStack.isExpanded() && !mMovedEnough) {
- mStack.expandStack();
+ if (!mBubbleData.isExpanded() && !mMovedEnough) {
+ mBubbleData.setExpanded(true);
}
} else if (mMovedEnough) {
mVelocityTracker.computeCurrentVelocity(/* maxVelocity */ 1000);
@@ -170,15 +175,13 @@
}
}
} else if (mTouchedView == mStack.getExpandedBubbleView()) {
- mStack.collapseStack();
+ mBubbleData.setExpanded(false);
} else if (isStack) {
- if (mStack.isExpanded()) {
- mStack.collapseStack();
- } else {
- mStack.expandStack();
- }
+ // Toggle expansion
+ mBubbleData.setExpanded(!mBubbleData.isExpanded());
} else {
- mStack.setExpandedBubble(((BubbleView) mTouchedView).getKey());
+ final String key = ((BubbleView) mTouchedView).getKey();
+ mBubbleData.setSelectedBubble(mBubbleData.getBubbleWithKey(key));
}
resetForNextGesture();
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java b/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
index 0607654..5196ec6 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
@@ -27,7 +27,6 @@
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.Dependency;
import com.android.systemui.R;
-import com.android.systemui.SysUiServiceProvider;
import com.android.systemui.SystemUIApplication;
import com.android.systemui.classifier.FalsingManager;
import com.android.systemui.dock.DockManager;
@@ -46,7 +45,7 @@
Context context = dozeService;
SensorManager sensorManager = Dependency.get(AsyncSensorManager.class);
AlarmManager alarmManager = context.getSystemService(AlarmManager.class);
- DockManager dockManager = SysUiServiceProvider.getComponent(context, DockManager.class);
+ DockManager dockManager = Dependency.get(DockManager.class);
DozeHost host = getHost(dozeService);
AmbientDisplayConfiguration config = new AmbientDisplayConfiguration(context);
diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipNotification.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipNotification.java
index 89ecc6a..d50f294e4 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipNotification.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipNotification.java
@@ -43,7 +43,7 @@
*/
public class PipNotification {
private static final String TAG = "PipNotification";
- private static final String NOTIFICATION_TAG = PipNotification.class.getName();
+ private static final String NOTIFICATION_TAG = PipNotification.class.getSimpleName();
private static final boolean DEBUG = PipManager.DEBUG;
private static final String ACTION_MENU = "PipNotification.menu";
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index 6adce83..9431f20 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -38,6 +38,7 @@
import android.service.notification.ZenModeConfig;
import android.text.format.DateUtils;
import android.util.AttributeSet;
+import android.util.Log;
import android.util.Pair;
import android.util.StatsLog;
import android.view.ContextThemeWrapper;
@@ -524,11 +525,11 @@
mActivityStarter.postStartActivityDismissingKeyguard(new Intent(
AlarmClock.ACTION_SHOW_ALARMS), 0);
} else if (v == mNextAlarmContainer) {
- if (mNextAlarm.getShowIntent() != null
- && mNextAlarm.getShowIntent().getIntent() != null) {
+ if (mNextAlarm.getShowIntent() != null) {
mActivityStarter.postStartActivityDismissingKeyguard(
- mNextAlarm.getShowIntent().getIntent(), 0);
+ mNextAlarm.getShowIntent());
} else {
+ Log.d(TAG, "No PendingIntent for next alarm. Using default intent");
mActivityStarter.postStartActivityDismissingKeyguard(new Intent(
AlarmClock.ACTION_SHOW_ALARMS), 0);
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index 5ed6a42..4fb4999 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -22,9 +22,6 @@
import static android.view.MotionEvent.ACTION_UP;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON;
-import static com.android.systemui.shared.system.NavigationBarCompat.FLAG_DISABLE_SWIPE_UP;
-import static com.android.systemui.shared.system.NavigationBarCompat.FLAG_SHOW_OVERVIEW_BUTTON;
-import static com.android.systemui.shared.system.NavigationBarCompat.InteractionType;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_INPUT_MONITOR;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SUPPORTS_WINDOW_CORNERS;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SYSUI_PROXY;
@@ -61,7 +58,6 @@
import com.android.internal.policy.ScreenDecorationsUtils;
import com.android.systemui.Dependency;
import com.android.systemui.Dumpable;
-import com.android.systemui.Prefs;
import com.android.systemui.SysUiServiceProvider;
import com.android.systemui.recents.OverviewProxyService.OverviewProxyListener;
import com.android.systemui.shared.recents.IOverviewProxy;
@@ -103,10 +99,6 @@
// Max backoff caps at 5 mins
private static final long MAX_BACKOFF_MILLIS = 10 * 60 * 1000;
- // Default interaction flags if swipe up is disabled before connecting to launcher
- private static final int DEFAULT_DISABLE_SWIPE_UP_STATE = FLAG_DISABLE_SWIPE_UP
- | FLAG_SHOW_OVERVIEW_BUTTON;
-
private final Context mContext;
private final Handler mHandler;
private final Runnable mConnectionRunnable = this::internalConnectToCurrentUser;
@@ -119,7 +111,6 @@
private IOverviewProxy mOverviewProxy;
private int mConnectionBackoffAttempts;
- private @InteractionType int mInteractionFlags;
private @SystemUiStateFlags int mSysUiStateFlags;
private boolean mBound;
private boolean mIsEnabled;
@@ -233,27 +224,6 @@
}
@Override
- public void setInteractionState(@InteractionType int flags) {
- if (!verifyCaller("setInteractionState")) {
- return;
- }
- long token = Binder.clearCallingIdentity();
- try {
- if (mInteractionFlags != flags) {
- mInteractionFlags = flags;
- mHandler.post(() -> {
- for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) {
- mConnectionCallbacks.get(i).onInteractionFlagsChanged(flags);
- }
- });
- }
- } finally {
- Prefs.putInt(mContext, Prefs.Key.QUICK_STEP_INTERACTION_FLAGS, mInteractionFlags);
- Binder.restoreCallingIdentity(token);
- }
- }
-
- @Override
public Rect getNonMinimizedSplitScreenSecondaryBounds() {
if (!verifyCaller("getNonMinimizedSplitScreenSecondaryBounds")) {
return null;
@@ -380,12 +350,6 @@
public void onReceive(Context context, Intent intent) {
updateEnabledState();
- // When launcher service is disabled, reset interaction flags because it is inactive
- if (!isEnabled()) {
- mInteractionFlags = getDefaultInteractionFlags();
- Prefs.remove(mContext, Prefs.Key.QUICK_STEP_INTERACTION_FLAGS);
- }
-
// Reconnect immediately, instead of waiting for resume to arrive.
startConnectionToCurrentUser();
}
@@ -479,8 +443,6 @@
com.android.internal.R.string.config_recentsComponentName));
mQuickStepIntent = new Intent(ACTION_QUICKSTEP)
.setPackage(mRecentsComponentName.getPackageName());
- mInteractionFlags = Prefs.getInt(mContext, Prefs.Key.QUICK_STEP_INTERACTION_FLAGS,
- getDefaultInteractionFlags());
mWindowCornerRadius = ScreenDecorationsUtils.getWindowCornerRadius(mContext.getResources());
mSupportsRoundedCornersOnWindows = ScreenDecorationsUtils
.supportsRoundedCornersOnWindows(mContext.getResources());
@@ -653,7 +615,6 @@
public void addCallback(OverviewProxyListener listener) {
mConnectionCallbacks.add(listener);
listener.onConnectionChanged(mOverviewProxy != null);
- listener.onInteractionFlagsChanged(mInteractionFlags);
listener.onBackButtonAlphaChanged(mBackButtonAlpha, false);
}
@@ -663,7 +624,7 @@
}
public boolean shouldShowSwipeUpUI() {
- return isEnabled() && ((mInteractionFlags & FLAG_DISABLE_SWIPE_UP) == 0);
+ return isEnabled() && !QuickStepContract.isLegacyMode(mNavBarMode);
}
public boolean isEnabled() {
@@ -674,10 +635,6 @@
return mOverviewProxy;
}
- public int getInteractionFlags() {
- return mInteractionFlags;
- }
-
private void disconnectFromLauncherService() {
if (mBound) {
// Always unbind the service (ie. if called through onNullBinding or onBindingDied)
@@ -693,13 +650,6 @@
}
}
- private int getDefaultInteractionFlags() {
- // If there is no settings available use device default or get it from settings
- return QuickStepContract.isLegacyMode(mNavBarMode)
- ? DEFAULT_DISABLE_SWIPE_UP_STATE
- : 0;
- }
-
private void notifyBackButtonAlphaChanged(float alpha, boolean animate) {
for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) {
mConnectionCallbacks.get(i).onBackButtonAlphaChanged(alpha, animate);
@@ -765,7 +715,6 @@
pw.print(" isCurrentUserSetup="); pw.println(mDeviceProvisionedController
.isCurrentUserSetup());
pw.print(" connectionBackoffAttempts="); pw.println(mConnectionBackoffAttempts);
- pw.print(" interactionFlags="); pw.println(mInteractionFlags);
pw.print(" quickStepIntent="); pw.println(mQuickStepIntent);
pw.print(" quickStepIntentResolved="); pw.println(isEnabled());
@@ -775,7 +724,6 @@
public interface OverviewProxyListener {
default void onConnectionChanged(boolean isConnected) {}
default void onQuickStepStarted() {}
- default void onInteractionFlagsChanged(@InteractionType int flags) {}
default void onOverviewShown(boolean fromHome) {}
default void onQuickScrubStarted() {}
default void onBackButtonAlphaChanged(float alpha, boolean animate) {}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java
index 85848ca..bd25209 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java
@@ -53,7 +53,7 @@
@Singleton
public class NavigationBarController implements Callbacks {
- private static final String TAG = NavigationBarController.class.getName();
+ private static final String TAG = NavigationBarController.class.getSimpleName();
private final Context mContext;
private final Handler mHandler;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
index 1f8ca37..d49f168 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
@@ -40,8 +40,11 @@
import android.metrics.LogMaker;
import android.os.Handler;
import android.os.RemoteException;
+import android.provider.Settings;
import android.service.notification.StatusBarNotification;
import android.text.TextUtils;
+import android.transition.AutoTransition;
+import android.transition.TransitionManager;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
@@ -88,6 +91,8 @@
// standard controls
private static final int ACTION_ALERT = 5;
+ private static final int BUTTON_ANIM_TIME_MS = 200;
+
private INotificationManager mINotificationManager;
private PackageManager mPm;
private MetricsLogger mMetricsLogger;
@@ -102,6 +107,8 @@
private boolean mWasShownHighPriority;
private boolean mShowOnLockscreen;
private boolean mShowInStatusBar;
+ private boolean mPressedApply;
+
/**
* The last importance level chosen by the user. Null if the user has not chosen an importance
* level; non-null once the user takes an action which indicates an explicit preference.
@@ -132,7 +139,7 @@
private OnClickListener mOnAlert = v -> {
mExitReason = NotificationCounters.BLOCKING_HELPER_KEEP_SHOWING;
mChosenImportance = IMPORTANCE_DEFAULT;
- setImportanceSummary(ACTION_ALERT);
+ setImportanceSummary(ACTION_ALERT, true);
updateButtons(ACTION_ALERT);
};
@@ -140,12 +147,13 @@
private OnClickListener mOnSilent = v -> {
mExitReason = NotificationCounters.BLOCKING_HELPER_DELIVER_SILENTLY;
mChosenImportance = IMPORTANCE_LOW;
- setImportanceSummary(ACTION_TOGGLE_SILENT);
+ setImportanceSummary(ACTION_TOGGLE_SILENT, true);
updateButtons(ACTION_TOGGLE_SILENT);
};
// used by standard ui
private OnClickListener mOnDismissSettings = v -> {
+ mPressedApply = true;
closeControls(v);
};
@@ -294,7 +302,8 @@
mShowInStatusBar = !mINotificationManager.shouldHideSilentStatusIcons(
mContext.getPackageName());
- // TODO: b/128445911 use show on lockscreen setting
+ mShowOnLockscreen = Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, 0) == 1;
bindHeader();
bindChannelDetails();
@@ -334,6 +343,7 @@
findViewById(R.id.non_configurable_multichannel_text).setVisibility(GONE);
findViewById(R.id.interruptiveness_settings).setVisibility(GONE);
((TextView) findViewById(R.id.done)).setText(R.string.inline_done_button);
+ findViewById(R.id.turn_off_notifications).setVisibility(GONE);
} else if (mNumUniqueChannelsInRow > 1) {
findViewById(R.id.non_configurable_text).setVisibility(GONE);
findViewById(R.id.interruptiveness_settings).setVisibility(GONE);
@@ -360,10 +370,10 @@
if (mWasShownHighPriority) {
updateButtons(ACTION_ALERT);
- setImportanceSummary(ACTION_ALERT);
+ setImportanceSummary(ACTION_ALERT, false);
} else {
updateButtons(ACTION_TOGGLE_SILENT);
- setImportanceSummary(ACTION_TOGGLE_SILENT);
+ setImportanceSummary(ACTION_TOGGLE_SILENT, false);
}
}
@@ -484,14 +494,6 @@
}
}
- private boolean hasImportanceChanged() {
- return mSingleNotificationChannel != null
- && mChosenImportance != null
- && (mStartingChannelImportance == IMPORTANCE_UNSPECIFIED
- || (mWasShownHighPriority && mChosenImportance < IMPORTANCE_DEFAULT)
- || (!mWasShownHighPriority && mChosenImportance >= IMPORTANCE_DEFAULT));
- }
-
private void saveImportance() {
if (!mIsNonblockable
|| mExitReason != NotificationCounters.BLOCKING_HELPER_STOP_NOTIFICATIONS) {
@@ -526,8 +528,8 @@
}
private void updateButtons(int blockState) {
- TextView silence = findViewById(R.id.silence);
- TextView alert = findViewById(R.id.alert);
+ View silence = findViewById(R.id.silence);
+ View alert = findViewById(R.id.alert);
TextView done = findViewById(R.id.done);
switch (blockState) {
case ACTION_TOGGLE_SILENT:
@@ -549,22 +551,28 @@
}
}
- private void updateButtons(TextView selected, TextView unselected) {
+ private void updateButtons(View selected, View unselected) {
selected.setBackground(mSelectedBackground);
selected.setSelected(true);
- selected.setTextAppearance(
- R.style.TextAppearance_NotificationImportanceButton_Selected);
unselected.setBackground(mUnselectedBackground);
unselected.setSelected(false);
- unselected.setTextAppearance(
- R.style.TextAppearance_NotificationImportanceButton_Unselected);
}
- void setImportanceSummary(int blockState) {
- TextView view = findViewById(R.id.description);
+ void setImportanceSummary(int blockState, boolean userTriggered) {
+ if (userTriggered) {
+ AutoTransition transition = new AutoTransition();
+ transition.setDuration(BUTTON_ANIM_TIME_MS);
+ TransitionManager.beginDelayedTransition(this, transition);
+ }
if (blockState == ACTION_ALERT) {
+ TextView view = findViewById(R.id.alert_summary);
+ view.setVisibility(VISIBLE);
+ findViewById(R.id.silence_summary).setVisibility(GONE);
view.setText(R.string.notification_channel_summary_default);
} else {
+ TextView view = findViewById(R.id.silence_summary);
+ view.setVisibility(VISIBLE);
+ findViewById(R.id.alert_summary).setVisibility(GONE);
if (mShowInStatusBar) {
if (mShowOnLockscreen) {
view.setText(R.string.notification_channel_summary_low_status_lock);
@@ -742,12 +750,12 @@
@Override
public boolean willBeRemoved() {
- return hasImportanceChanged();
+ return false;
}
@Override
public boolean shouldBeSaved() {
- return hasImportanceChanged();
+ return mPressedApply;
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
index 586e82c..a831a5d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
@@ -31,10 +31,13 @@
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityNodeInfo;
+import androidx.annotation.Nullable;
+
import com.android.internal.graphics.ColorUtils;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.systemui.R;
+import com.android.systemui.dock.DockManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.KeyguardAffordanceView;
import com.android.systemui.statusbar.policy.AccessibilityController;
@@ -62,6 +65,7 @@
private final UnlockMethodCache mUnlockMethodCache;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private final AccessibilityController mAccessibilityController;
+ private final DockManager mDockManager;
private int mLastState = 0;
private boolean mTransientBiometricsError;
@@ -72,13 +76,26 @@
private boolean mPulsing;
private boolean mDozing;
private boolean mBouncerVisible;
+ private boolean mDocked;
private boolean mLastDozing;
private boolean mLastPulsing;
private boolean mLastBouncerVisible;
private int mIconColor;
+ private float mDozeAmount;
private final Runnable mDrawOffTimeout = () -> update(true /* forceUpdate */);
- private float mDozeAmount;
+ private final DockManager.DockEventListener mDockEventListener =
+ new DockManager.DockEventListener() {
+ @Override
+ public void onEvent(int event) {
+ boolean docked = event == DockManager.STATE_DOCKED
+ || event == DockManager.STATE_DOCKED_HIDE;
+ if (docked != mDocked) {
+ mDocked = docked;
+ update(true /* force */);
+ }
+ }
+ };
private final KeyguardUpdateMonitorCallback mUpdateMonitorCallback =
new KeyguardUpdateMonitorCallback() {
@@ -115,7 +132,8 @@
public LockIcon(@Named(VIEW_CONTEXT) Context context, AttributeSet attrs,
StatusBarStateController statusBarStateController,
ConfigurationController configurationController,
- AccessibilityController accessibilityController) {
+ AccessibilityController accessibilityController,
+ @Nullable DockManager dockManager) {
super(context, attrs);
mContext = context;
mUnlockMethodCache = UnlockMethodCache.getInstance(context);
@@ -123,6 +141,7 @@
mAccessibilityController = accessibilityController;
mConfigurationController = configurationController;
mStatusBarStateController = statusBarStateController;
+ mDockManager = dockManager;
}
@Override
@@ -132,6 +151,9 @@
mConfigurationController.addCallback(this);
mKeyguardUpdateMonitor.registerCallback(mUpdateMonitorCallback);
mUnlockMethodCache.addListener(this);
+ if (mDockManager != null) {
+ mDockManager.addListener(mDockEventListener);
+ }
onThemeChanged();
}
@@ -142,6 +164,9 @@
mConfigurationController.removeCallback(this);
mKeyguardUpdateMonitor.removeCallback(mUpdateMonitorCallback);
mUnlockMethodCache.removeListener(this);
+ if (mDockManager != null) {
+ mDockManager.removeListener(mDockEventListener);
+ }
}
@Override
@@ -237,7 +262,8 @@
mLastBouncerVisible = mBouncerVisible;
}
- setVisibility(mDozing && !mPulsing ? INVISIBLE : VISIBLE);
+ boolean invisible = mDozing && (!mPulsing || mDocked);
+ setVisibility(invisible ? INVISIBLE : VISIBLE);
updateClickability();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarEdgePanel.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarEdgePanel.java
index 79c7ab1..4603ba6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarEdgePanel.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarEdgePanel.java
@@ -23,6 +23,7 @@
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
+import android.os.SystemClock;
import android.os.VibrationEffect;
import android.util.MathUtils;
import android.view.ContextThemeWrapper;
@@ -51,6 +52,11 @@
private static final long DISAPPEAR_ARROW_ANIMATION_DURATION_MS = 100;
/**
+ * The minimum time required since the first vibration effect to receive a second one
+ */
+ private static final int MIN_TIME_BETWEEN_EFFECTS_MS = 120;
+
+ /**
* The size of the protection of the arrow in px. Only used if this is not background protected
*/
private static final int PROTECTION_WIDTH_PX = 2;
@@ -182,6 +188,7 @@
private int mArrowStartColor;
private int mCurrentArrowColor;
private float mDisappearAmount;
+ private long mVibrationTime;
private DynamicAnimation.OnAnimationEndListener mSetGoneEndListener
= new DynamicAnimation.OnAnimationEndListener() {
@@ -394,7 +401,7 @@
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL: {
if (mTriggerBack) {
- triggerBackAnimation();
+ triggerBack();
} else {
if (mTranslationAnimation.isRunning()) {
mTranslationAnimation.addEndListener(mSetGoneEndListener);
@@ -521,8 +528,10 @@
return mCurrentTranslation;
}
- private void triggerBackAnimation() {
-
+ private void triggerBack() {
+ if (SystemClock.uptimeMillis() - mVibrationTime >= MIN_TIME_BETWEEN_EFFECTS_MS) {
+ mVibratorHelper.vibrate(VibrationEffect.EFFECT_CLICK);
+ }
mVelocityTracker.computeCurrentVelocity(1000);
// Only do the extra translation if we're not already flinging
boolean doExtraTranslation = Math.abs(mVelocityTracker.getXVelocity()) < 1000;
@@ -577,6 +586,7 @@
setCurrentTranslation(0);
mPreviousTouchTranslation = 0;
mTotalTouchDelta = 0;
+ mVibrationTime = 0;
setDesiredVerticalTransition(0, false /* animated */);
}
@@ -599,6 +609,7 @@
if (!mDragSlopPassed && touchTranslation > mSwipeThreshold) {
mDragSlopPassed = true;
mVibratorHelper.vibrate(VibrationEffect.EFFECT_TICK);
+ mVibrationTime = SystemClock.uptimeMillis();
// Let's show the arrow and animate it in!
mDisappearAmount = 0.0f;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
index de57066..fa3377a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -24,7 +24,6 @@
import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON;
import static com.android.systemui.recents.OverviewProxyService.OverviewProxyListener;
-import static com.android.systemui.shared.system.NavigationBarCompat.InteractionType;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_CLICKABLE;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NAV_BAR_HIDDEN;
@@ -197,12 +196,6 @@
}
@Override
- public void onInteractionFlagsChanged(@InteractionType int flags) {
- mNavigationBarView.updateStates();
- updateScreenPinningGestures();
- }
-
- @Override
public void startAssistant(Bundle bundle) {
mAssistManager.startAssist(bundle);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index 4333200..22636de 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -19,7 +19,6 @@
import static android.view.WindowManagerPolicyConstants.NAV_BAR_INVALID;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON;
-import static com.android.systemui.shared.system.NavigationBarCompat.FLAG_SHOW_OVERVIEW_BUTTON;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED;
import static com.android.systemui.statusbar.phone.BarTransitions.MODE_OPAQUE;
@@ -637,10 +636,6 @@
// as they are used for exiting.
final boolean pinningActive = ActivityManagerWrapper.getInstance().isScreenPinningActive();
if (mOverviewProxyService.isEnabled()) {
- // Use interaction flags to show/hide navigation buttons but will be shown if required
- // to exit screen pinning.
- final int flags = mOverviewProxyService.getInteractionFlags();
- disableRecent |= (flags & FLAG_SHOW_OVERVIEW_BUTTON) == 0;
if (pinningActive) {
disableBack = disableHome = false;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationModeController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationModeController.java
index a00feeb..fa2263f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationModeController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationModeController.java
@@ -17,8 +17,10 @@
package com.android.systemui.statusbar.phone;
import static android.content.Intent.ACTION_OVERLAY_CHANGED;
-import static android.content.Intent.ACTION_USER_SWITCHED;
+import static android.os.UserHandle.USER_CURRENT;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON;
+import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY;
+import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -28,16 +30,21 @@
import android.content.pm.PackageManager;
import android.content.res.ApkAssets;
import android.os.PatternMatcher;
+import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
import android.util.Log;
+import android.util.SparseBooleanArray;
import com.android.systemui.Dumpable;
+import com.android.systemui.UiOffloadThread;
import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Arrays;
import javax.inject.Inject;
import javax.inject.Singleton;
@@ -48,7 +55,7 @@
@Singleton
public class NavigationModeController implements Dumpable {
- private static final String TAG = NavigationModeController.class.getName();
+ private static final String TAG = NavigationModeController.class.getSimpleName();
private static final boolean DEBUG = true;
public interface ModeChangedListener {
@@ -57,6 +64,10 @@
private final Context mContext;
private final IOverlayManager mOverlayManager;
+ private final DeviceProvisionedController mDeviceProvisionedController;
+ private final UiOffloadThread mUiOffloadThread;
+
+ private SparseBooleanArray mRestoreGesturalNavBarMode = new SparseBooleanArray();
private int mMode = NAV_BAR_MODE_3BUTTON;
private ArrayList<ModeChangedListener> mListeners = new ArrayList<>();
@@ -64,36 +75,90 @@
private BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
- context = getCurrentUserContext();
- int mode = getCurrentInteractionMode(context);
- mMode = mode;
- if (DEBUG) {
- Log.e(TAG, "ACTION_OVERLAY_CHANGED: mode=" + mMode
- + " contextUser=" + context.getUserId());
- dumpAssetPaths(context);
- }
-
- for (int i = 0; i < mListeners.size(); i++) {
- mListeners.get(i).onNavigationModeChanged(mode);
+ if (intent.getAction().equals(ACTION_OVERLAY_CHANGED)) {
+ if (DEBUG) {
+ Log.d(TAG, "ACTION_OVERLAY_CHANGED");
+ }
+ updateCurrentInteractionMode(true /* notify */);
}
}
};
+ private final DeviceProvisionedController.DeviceProvisionedListener mDeviceProvisionedCallback =
+ new DeviceProvisionedController.DeviceProvisionedListener() {
+ @Override
+ public void onDeviceProvisionedChanged() {
+ if (DEBUG) {
+ Log.d(TAG, "onDeviceProvisionedChanged: "
+ + mDeviceProvisionedController.isDeviceProvisioned());
+ }
+ // Once the device has been provisioned, check if we can restore gestural nav
+ restoreGesturalNavOverlayIfNecessary();
+ }
+
+ @Override
+ public void onUserSetupChanged() {
+ if (DEBUG) {
+ Log.d(TAG, "onUserSetupChanged: "
+ + mDeviceProvisionedController.isCurrentUserSetup());
+ }
+ // Once the user has been setup, check if we can restore gestural nav
+ restoreGesturalNavOverlayIfNecessary();
+ }
+
+ @Override
+ public void onUserSwitched() {
+ if (DEBUG) {
+ Log.d(TAG, "onUserSwitched: "
+ + ActivityManagerWrapper.getInstance().getCurrentUserId());
+ }
+
+ // Update the nav mode for the current user
+ updateCurrentInteractionMode(true /* notify */);
+
+ // When switching users, defer enabling the gestural nav overlay until the user
+ // is all set up
+ deferGesturalNavOverlayIfNecessary();
+ }
+ };
+
@Inject
- public NavigationModeController(Context context) {
+ public NavigationModeController(Context context,
+ DeviceProvisionedController deviceProvisionedController,
+ UiOffloadThread uiOffloadThread) {
mContext = context;
mOverlayManager = IOverlayManager.Stub.asInterface(
ServiceManager.getService(Context.OVERLAY_SERVICE));
+ mUiOffloadThread = uiOffloadThread;
+ mDeviceProvisionedController = deviceProvisionedController;
+ mDeviceProvisionedController.addCallback(mDeviceProvisionedCallback);
IntentFilter overlayFilter = new IntentFilter(ACTION_OVERLAY_CHANGED);
overlayFilter.addDataScheme("package");
overlayFilter.addDataSchemeSpecificPart("android", PatternMatcher.PATTERN_LITERAL);
mContext.registerReceiverAsUser(mReceiver, UserHandle.ALL, overlayFilter, null, null);
- IntentFilter userFilter = new IntentFilter(ACTION_USER_SWITCHED);
- mContext.registerReceiverAsUser(mReceiver, UserHandle.ALL, userFilter, null, null);
+ updateCurrentInteractionMode(false /* notify */);
- mMode = getCurrentInteractionMode(getCurrentUserContext());
+ // Check if we need to defer enabling gestural nav
+ deferGesturalNavOverlayIfNecessary();
+ }
+
+ public void updateCurrentInteractionMode(boolean notify) {
+ Context context = getCurrentUserContext();
+ int mode = getCurrentInteractionMode(context);
+ mMode = mode;
+ if (DEBUG) {
+ Log.e(TAG, "updateCurrentInteractionMode: mode=" + mMode
+ + " contextUser=" + context.getUserId());
+ dumpAssetPaths(context);
+ }
+
+ if (notify) {
+ for (int i = 0; i < mListeners.size(); i++) {
+ mListeners.get(i).onNavigationModeChanged(mode);
+ }
+ }
}
public int addListener(ModeChangedListener listener) {
@@ -129,10 +194,77 @@
}
}
+ private void deferGesturalNavOverlayIfNecessary() {
+ final int userId = mDeviceProvisionedController.getCurrentUser();
+ mRestoreGesturalNavBarMode.put(userId, false);
+ if (mDeviceProvisionedController.isDeviceProvisioned()
+ && mDeviceProvisionedController.isCurrentUserSetup()) {
+ // User is already setup and device is provisioned, nothing to do
+ if (DEBUG) {
+ Log.d(TAG, "deferGesturalNavOverlayIfNecessary: device is provisioned and user is "
+ + "setup");
+ }
+ return;
+ }
+
+ ArrayList<String> defaultOverlays = new ArrayList<>();
+ try {
+ defaultOverlays.addAll(Arrays.asList(mOverlayManager.getDefaultOverlayPackages()));
+ } catch (RemoteException e) {
+ Log.e(TAG, "deferGesturalNavOverlayIfNecessary: failed to fetch default overlays");
+ }
+ if (!defaultOverlays.contains(NAV_BAR_MODE_GESTURAL_OVERLAY)) {
+ // No default gesture nav overlay
+ if (DEBUG) {
+ Log.d(TAG, "deferGesturalNavOverlayIfNecessary: no default gestural overlay, "
+ + "default=" + defaultOverlays);
+ }
+ return;
+ }
+
+ // If the default is gestural, force-enable three button mode until the device is
+ // provisioned
+ setModeOverlay(NAV_BAR_MODE_3BUTTON_OVERLAY, USER_CURRENT);
+ mRestoreGesturalNavBarMode.put(userId, true);
+ if (DEBUG) {
+ Log.d(TAG, "deferGesturalNavOverlayIfNecessary: setting to 3 button mode");
+ }
+ }
+
+ private void restoreGesturalNavOverlayIfNecessary() {
+ if (DEBUG) {
+ Log.d(TAG, "restoreGesturalNavOverlayIfNecessary: needs restore="
+ + mRestoreGesturalNavBarMode);
+ }
+ final int userId = mDeviceProvisionedController.getCurrentUser();
+ if (mRestoreGesturalNavBarMode.get(userId)) {
+ // Restore the gestural state if necessary
+ setModeOverlay(NAV_BAR_MODE_GESTURAL_OVERLAY, USER_CURRENT);
+ mRestoreGesturalNavBarMode.put(userId, false);
+ }
+ }
+
+ public void setModeOverlay(String overlayPkg, int userId) {
+ mUiOffloadThread.submit(() -> {
+ try {
+ mOverlayManager.setEnabledExclusiveInCategory(overlayPkg, userId);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to enable overlay " + overlayPkg + " for user " + userId);
+ }
+ });
+ }
+
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println("NavigationModeController:");
pw.println(" mode=" + mMode);
+ String defaultOverlays = "";
+ try {
+ defaultOverlays = String.join(", ", mOverlayManager.getDefaultOverlayPackages());
+ } catch (RemoteException e) {
+ defaultOverlays = "failed_to_fetch";
+ }
+ pw.println(" defaultOverlays=" + defaultOverlays);
dumpAssetPaths(getCurrentUserContext());
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
index 9923ec9..41fa346 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
@@ -56,7 +56,7 @@
import com.android.systemui.bubbles.BubbleController;
import com.android.systemui.plugins.statusbar.phone.NavBarButtonProvider.ButtonInterface;
import com.android.systemui.recents.OverviewProxyService;
-import com.android.systemui.shared.system.NavigationBarCompat;
+import com.android.systemui.shared.system.QuickStepContract;
public class KeyButtonView extends ImageView implements ButtonInterface {
private static final String TAG = KeyButtonView.class.getSimpleName();
@@ -244,11 +244,11 @@
y = (int)ev.getRawY();
boolean exceededTouchSlopX = Math.abs(x - mTouchDownX) > (mIsVertical
- ? NavigationBarCompat.getQuickScrubTouchSlopPx()
- : NavigationBarCompat.getQuickStepTouchSlopPx());
+ ? QuickStepContract.getQuickScrubTouchSlopPx()
+ : QuickStepContract.getQuickStepTouchSlopPx());
boolean exceededTouchSlopY = Math.abs(y - mTouchDownY) > (mIsVertical
- ? NavigationBarCompat.getQuickStepTouchSlopPx()
- : NavigationBarCompat.getQuickScrubTouchSlopPx());
+ ? QuickStepContract.getQuickStepTouchSlopPx()
+ : QuickStepContract.getQuickScrubTouchSlopPx());
if (exceededTouchSlopX || exceededTouchSlopY) {
// When quick step is enabled, prevent animating the ripple triggered by
// setPressed and decide to run it on touch up
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
index 11e5625..c398ad8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
@@ -94,8 +94,7 @@
mPhone = phone;
mDefaults = defaults;
mSubscriptionInfo = info;
- mPhoneStateListener = new MobilePhoneStateListener(info.getSubscriptionId(),
- receiverLooper);
+ mPhoneStateListener = new MobilePhoneStateListener(receiverLooper);
mNetworkNameSeparator = getStringIfExists(R.string.status_bar_network_name_separator);
mNetworkNameDefault = getStringIfExists(
com.android.internal.R.string.lockscreen_carrier_default);
@@ -574,8 +573,8 @@
}
class MobilePhoneStateListener extends PhoneStateListener {
- public MobilePhoneStateListener(int subId, Looper looper) {
- super(subId, looper);
+ public MobilePhoneStateListener(Looper looper) {
+ super(looper);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
index faf63c8..950f07d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -662,8 +662,9 @@
cachedControllers.remove(subId);
} else {
MobileSignalController controller = new MobileSignalController(mContext, mConfig,
- mHasMobileDataFeature, mPhone, mCallbackHandler,
- this, subscriptions.get(i), mSubDefaults, mReceiverHandler.getLooper());
+ mHasMobileDataFeature, mPhone.createForSubscriptionId(subId),
+ mCallbackHandler, this, subscriptions.get(i),
+ mSubDefaults, mReceiverHandler.getLooper());
controller.setUserSetupComplete(mUserSetup);
mMobileSignalControllers.put(subId, controller);
if (subscriptions.get(i).getSimSlotIndex() == 0) {
@@ -1049,7 +1050,8 @@
SubscriptionInfo info = new SubscriptionInfo(id, "", simSlotIndex, "", "", 0, 0, "", 0,
null, null, null, "", false, null, null);
MobileSignalController controller = new MobileSignalController(mContext,
- mConfig, mHasMobileDataFeature, mPhone, mCallbackHandler, this, info,
+ mConfig, mHasMobileDataFeature,
+ mPhone.createForSubscriptionId(info.getSubscriptionId()), mCallbackHandler, this, info,
mSubDefaults, mReceiverHandler.getLooper());
mMobileSignalControllers.put(id, controller);
controller.getState().userSetup = true;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java
new file mode 100644
index 0000000..d6dac2f
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java
@@ -0,0 +1,707 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.bubbles;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.Notification;
+import android.app.PendingIntent;
+import android.graphics.drawable.Icon;
+import android.os.UserHandle;
+import android.service.notification.StatusBarNotification;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.bubbles.BubbleData.TimeSource;
+import com.android.systemui.statusbar.NotificationTestHelper;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+
+import com.google.common.collect.ImmutableList;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+public class BubbleDataTest extends SysuiTestCase {
+
+ private NotificationEntry mEntryA1;
+ private NotificationEntry mEntryA2;
+ private NotificationEntry mEntryA3;
+ private NotificationEntry mEntryB1;
+ private NotificationEntry mEntryB2;
+ private NotificationEntry mEntryB3;
+ private NotificationEntry mEntryC1;
+
+ private Bubble mBubbleA1;
+ private Bubble mBubbleA2;
+ private Bubble mBubbleA3;
+ private Bubble mBubbleB1;
+ private Bubble mBubbleB2;
+ private Bubble mBubbleB3;
+ private Bubble mBubbleC1;
+
+ private BubbleData mBubbleData;
+
+ @Mock
+ private TimeSource mTimeSource;
+ @Mock
+ private BubbleData.Listener mListener;
+ @Mock
+ private PendingIntent mExpandIntent;
+ @Mock
+ private PendingIntent mDeleteIntent;
+
+ private NotificationTestHelper mNotificationTestHelper;
+
+ @Before
+ public void setUp() throws Exception {
+ mNotificationTestHelper = new NotificationTestHelper(mContext);
+ MockitoAnnotations.initMocks(this);
+
+ mEntryA1 = createBubbleEntry(1, "a1", "package.a");
+ mEntryA2 = createBubbleEntry(1, "a2", "package.a");
+ mEntryA3 = createBubbleEntry(1, "a3", "package.a");
+ mEntryB1 = createBubbleEntry(1, "b1", "package.b");
+ mEntryB2 = createBubbleEntry(1, "b2", "package.b");
+ mEntryB3 = createBubbleEntry(1, "b3", "package.b");
+ mEntryC1 = createBubbleEntry(1, "c1", "package.c");
+
+ mBubbleA1 = new Bubble(mEntryA1);
+ mBubbleA2 = new Bubble(mEntryA2);
+ mBubbleA3 = new Bubble(mEntryA3);
+ mBubbleB1 = new Bubble(mEntryB1);
+ mBubbleB2 = new Bubble(mEntryB2);
+ mBubbleB3 = new Bubble(mEntryB3);
+ mBubbleC1 = new Bubble(mEntryC1);
+
+ mBubbleData = new BubbleData(getContext());
+
+ // Used by BubbleData to set lastAccessedTime
+ when(mTimeSource.currentTimeMillis()).thenReturn(1000L);
+ mBubbleData.setTimeSource(mTimeSource);
+ }
+
+ private NotificationEntry createBubbleEntry(int userId, String notifKey, String packageName) {
+ return createBubbleEntry(userId, notifKey, packageName, 1000);
+ }
+
+ private void setPostTime(NotificationEntry entry, long postTime) {
+ when(entry.notification.getPostTime()).thenReturn(postTime);
+ }
+
+ private void setOngoing(NotificationEntry entry, boolean ongoing) {
+ if (ongoing) {
+ entry.notification.getNotification().flags |= Notification.FLAG_FOREGROUND_SERVICE;
+ } else {
+ entry.notification.getNotification().flags &= ~Notification.FLAG_FOREGROUND_SERVICE;
+ }
+ }
+
+ /**
+ * No ExpandableNotificationRow is required to test BubbleData. This setup is all that is
+ * required for BubbleData functionality and verification. NotificationTestHelper is used only
+ * as a convenience to create a Notification w/BubbleMetadata.
+ */
+ private NotificationEntry createBubbleEntry(int userId, String notifKey, String packageName,
+ long postTime) {
+ // BubbleMetadata
+ Notification.BubbleMetadata bubbleMetadata = new Notification.BubbleMetadata.Builder()
+ .setIntent(mExpandIntent)
+ .setDeleteIntent(mDeleteIntent)
+ .setIcon(Icon.createWithResource("", 0))
+ .build();
+ // Notification -> BubbleMetadata
+ Notification notification = mNotificationTestHelper.createNotification(false,
+ null /* groupKey */, bubbleMetadata);
+
+ // StatusBarNotification
+ StatusBarNotification sbn = mock(StatusBarNotification.class);
+ when(sbn.getKey()).thenReturn(notifKey);
+ when(sbn.getUser()).thenReturn(new UserHandle(userId));
+ when(sbn.getPackageName()).thenReturn(packageName);
+ when(sbn.getPostTime()).thenReturn(postTime);
+ when(sbn.getNotification()).thenReturn(notification);
+
+ // NotificationEntry -> StatusBarNotification -> Notification -> BubbleMetadata
+ return new NotificationEntry(sbn);
+ }
+
+ private void sendUpdatedEntryAtTime(NotificationEntry entry, long postTime) {
+ setPostTime(entry, postTime);
+ mBubbleData.notificationEntryUpdated(entry);
+ }
+
+ private void changeExpandedStateAtTime(boolean shouldBeExpanded, long time) {
+ when(mTimeSource.currentTimeMillis()).thenReturn(time);
+ mBubbleData.setExpanded(shouldBeExpanded);
+ }
+
+ @Test
+ public void testAddBubble() {
+ // Setup
+ mBubbleData.setListener(mListener);
+
+ // Test
+ setPostTime(mEntryA1, 1000);
+ mBubbleData.notificationEntryUpdated(mEntryA1);
+
+ // Verify
+ verify(mListener).onBubbleAdded(eq(mBubbleA1));
+ verify(mListener).onSelectionChanged(eq(mBubbleA1));
+ verify(mListener).apply();
+ }
+
+ @Test
+ public void testRemoveBubble() {
+ // Setup
+ mBubbleData.notificationEntryUpdated(mEntryA1);
+ mBubbleData.notificationEntryUpdated(mEntryA2);
+ mBubbleData.notificationEntryUpdated(mEntryA3);
+ mBubbleData.setListener(mListener);
+
+ // Test
+ mBubbleData.notificationEntryRemoved(mEntryA1, BubbleController.DISMISS_USER_GESTURE);
+
+ // Verify
+ verify(mListener).onBubbleRemoved(eq(mBubbleA1), eq(BubbleController.DISMISS_USER_GESTURE));
+ verify(mListener).onSelectionChanged(eq(mBubbleA2));
+ verify(mListener).apply();
+ }
+
+ @Test
+ public void test_collapsed_addBubble_atMaxBubbles_expiresLeastActive() {
+ // Given
+ sendUpdatedEntryAtTime(mEntryA1, 1000);
+ sendUpdatedEntryAtTime(mEntryA2, 2000);
+ sendUpdatedEntryAtTime(mEntryA3, 3000);
+ sendUpdatedEntryAtTime(mEntryB1, 4000);
+ sendUpdatedEntryAtTime(mEntryB2, 5000);
+ assertThat(mBubbleData.getSelectedBubble()).isEqualTo(mBubbleA1);
+
+ // When
+ sendUpdatedEntryAtTime(mEntryC1, 6000);
+
+ // Then
+ // A2 is removed. A1 is oldest but is the selected bubble.
+ assertThat(mBubbleData.getBubbles()).doesNotContain(mBubbleA2);
+ }
+
+ @Test
+ public void test_collapsed_expand_whenEmpty_doesNothing() {
+ assertThat(mBubbleData.hasBubbles()).isFalse();
+ changeExpandedStateAtTime(true, 2000L);
+
+ verify(mListener, never()).onExpandedChanged(anyBoolean());
+ verify(mListener, never()).apply();
+ }
+
+ // New bubble while stack is collapsed
+ @Test
+ public void test_collapsed_addBubble() {
+ // Given
+ assertThat(mBubbleData.hasBubbles()).isFalse();
+ assertThat(mBubbleData.isExpanded()).isFalse();
+
+ // When
+ sendUpdatedEntryAtTime(mEntryA1, 1000);
+ sendUpdatedEntryAtTime(mEntryB1, 2000);
+ sendUpdatedEntryAtTime(mEntryB2, 3000);
+ sendUpdatedEntryAtTime(mEntryA2, 4000);
+
+ // Then
+ // New bubbles move to front when collapsed, bringing bubbles from the same app along
+ assertThat(mBubbleData.getBubbles()).isEqualTo(
+ ImmutableList.of(mBubbleA2, mBubbleA1, mBubbleB2, mBubbleB1));
+ }
+
+ // New bubble while collapsed with ongoing bubble present
+ @Test
+ public void test_collapsed_addBubble_withOngoing() {
+ // Given
+ assertThat(mBubbleData.hasBubbles()).isFalse();
+ assertThat(mBubbleData.isExpanded()).isFalse();
+
+ // When
+ setOngoing(mEntryA1, true);
+ setPostTime(mEntryA1, 1000);
+ mBubbleData.notificationEntryUpdated(mEntryA1);
+ setPostTime(mEntryB1, 2000);
+ mBubbleData.notificationEntryUpdated(mEntryB1);
+ setPostTime(mEntryB2, 3000);
+ mBubbleData.notificationEntryUpdated(mEntryB2);
+ setPostTime(mEntryA2, 4000);
+ mBubbleData.notificationEntryUpdated(mEntryA2);
+
+ // Then
+ // New bubbles move to front, but stay behind any ongoing bubbles.
+ assertThat(mBubbleData.getBubbles()).isEqualTo(
+ ImmutableList.of(mBubbleA1, mBubbleA2, mBubbleB2, mBubbleB1));
+ }
+
+ // Remove the selected bubble (middle bubble), while the stack is collapsed.
+ @Test
+ public void test_collapsed_removeBubble_selected() {
+ // Given
+ assertThat(mBubbleData.hasBubbles()).isFalse();
+ assertThat(mBubbleData.isExpanded()).isFalse();
+
+ setPostTime(mEntryA1, 1000);
+ mBubbleData.notificationEntryUpdated(mEntryA1);
+
+ setPostTime(mEntryB1, 2000);
+ mBubbleData.notificationEntryUpdated(mEntryB1);
+
+ setPostTime(mEntryB2, 3000);
+ mBubbleData.notificationEntryUpdated(mEntryB2);
+
+ setPostTime(mEntryA2, 4000);
+ mBubbleData.notificationEntryUpdated(mEntryA2);
+
+ mBubbleData.setSelectedBubble(mBubbleB2);
+ assertThat(mBubbleData.getBubbles()).isEqualTo(
+ ImmutableList.of(mBubbleA2, mBubbleA1, mBubbleB2, mBubbleB1));
+
+ // When
+ mBubbleData.notificationEntryRemoved(mEntryB2, BubbleController.DISMISS_USER_GESTURE);
+
+ // Then
+ // (Selection remains in the same position)
+ assertThat(mBubbleData.getSelectedBubble()).isEqualTo(mBubbleB1);
+ }
+
+ // Remove the selected bubble (last bubble), while the stack is collapsed.
+ @Test
+ public void test_collapsed_removeSelectedBubble_inLastPosition() {
+ // Given
+ assertThat(mBubbleData.hasBubbles()).isFalse();
+ assertThat(mBubbleData.isExpanded()).isFalse();
+
+ sendUpdatedEntryAtTime(mEntryA1, 1000);
+ sendUpdatedEntryAtTime(mEntryB1, 2000);
+ sendUpdatedEntryAtTime(mEntryB2, 3000);
+ sendUpdatedEntryAtTime(mEntryA2, 4000);
+
+ mBubbleData.setSelectedBubble(mBubbleB1);
+ assertThat(mBubbleData.getBubbles()).isEqualTo(
+ ImmutableList.of(mBubbleA2, mBubbleA1, mBubbleB2, mBubbleB1));
+
+ // When
+ mBubbleData.notificationEntryRemoved(mEntryB1, BubbleController.DISMISS_USER_GESTURE);
+
+ // Then
+ // (Selection is forced to move to previous)
+ assertThat(mBubbleData.getSelectedBubble()).isEqualTo(mBubbleB2);
+ }
+
+ @Test
+ public void test_collapsed_addBubble_ongoing() {
+ // Given
+ assertThat(mBubbleData.hasBubbles()).isFalse();
+ assertThat(mBubbleData.isExpanded()).isFalse();
+
+ // When
+ setPostTime(mEntryA1, 1000);
+ mBubbleData.notificationEntryUpdated(mEntryA1);
+
+ setPostTime(mEntryB1, 2000);
+ mBubbleData.notificationEntryUpdated(mEntryB1);
+
+ setPostTime(mEntryB2, 3000);
+ setOngoing(mEntryB2, true);
+ mBubbleData.notificationEntryUpdated(mEntryB2);
+
+ setPostTime(mEntryA2, 4000);
+ mBubbleData.notificationEntryUpdated(mEntryA2);
+
+ // Then
+ // New bubbles move to front, but stay behind any ongoing bubbles.
+ // Does not break grouping. (A2 is inserted after B1, even though it's newer).
+ assertThat(mBubbleData.getBubbles()).isEqualTo(
+ ImmutableList.of(mBubbleB2, mBubbleB1, mBubbleA2, mBubbleA1));
+ }
+
+ @Test
+ public void test_collapsed_removeBubble() {
+ // Given
+ assertThat(mBubbleData.hasBubbles()).isFalse();
+ assertThat(mBubbleData.isExpanded()).isFalse();
+
+ sendUpdatedEntryAtTime(mEntryA1, 1000);
+ sendUpdatedEntryAtTime(mEntryB1, 2000);
+ sendUpdatedEntryAtTime(mEntryB2, 3000);
+ sendUpdatedEntryAtTime(mEntryA2, 4000);
+
+ // When
+ mBubbleData.notificationEntryRemoved(mEntryB2, BubbleController.DISMISS_USER_GESTURE);
+
+ // Then
+ assertThat(mBubbleData.getBubbles()).isEqualTo(
+ ImmutableList.of(mBubbleA2, mBubbleA1, mBubbleB1));
+ }
+
+ @Test
+ public void test_collapsed_updateBubble() {
+ // Given
+ assertThat(mBubbleData.hasBubbles()).isFalse();
+ assertThat(mBubbleData.isExpanded()).isFalse();
+
+ sendUpdatedEntryAtTime(mEntryA1, 1000);
+ sendUpdatedEntryAtTime(mEntryB1, 2000);
+ sendUpdatedEntryAtTime(mEntryB2, 3000);
+ sendUpdatedEntryAtTime(mEntryA2, 4000);
+
+ assertThat(mBubbleData.getBubbles()).isEqualTo(
+ ImmutableList.of(mBubbleA2, mBubbleA1, mBubbleB2, mBubbleB1));
+
+ // When
+ sendUpdatedEntryAtTime(mEntryB2, 5000);
+
+ // Then
+ assertThat(mBubbleData.getBubbles()).isEqualTo(
+ ImmutableList.of(mBubbleB2, mBubbleB1, mBubbleA2, mBubbleA1));
+ }
+
+ @Test
+ public void test_collapsed_updateBubble_withOngoing() {
+ // Given
+ assertThat(mBubbleData.hasBubbles()).isFalse();
+ assertThat(mBubbleData.isExpanded()).isFalse();
+
+ setPostTime(mEntryA1, 1000);
+ mBubbleData.notificationEntryUpdated(mEntryA1);
+
+ setPostTime(mEntryB1, 2000);
+ mBubbleData.notificationEntryUpdated(mEntryB1);
+
+ setPostTime(mEntryB2, 3000);
+ mBubbleData.notificationEntryUpdated(mEntryB2);
+
+ setOngoing(mEntryA2, true);
+ setPostTime(mEntryA2, 4000);
+ mBubbleData.notificationEntryUpdated(mEntryA2);
+
+ assertThat(mBubbleData.getBubbles()).isEqualTo(
+ ImmutableList.of(mBubbleA2, mBubbleA1, mBubbleB2, mBubbleB1));
+
+ // When
+ setPostTime(mEntryB1, 5000);
+ mBubbleData.notificationEntryUpdated(mEntryB1);
+
+ // Then
+ // A2 remains in first position, due to being ongoing. B1 moves before B2, Group A
+ // remains before group B.
+ assertThat(mBubbleData.getBubbles()).isEqualTo(
+ ImmutableList.of(mBubbleA2, mBubbleA1, mBubbleB1, mBubbleB2));
+ }
+
+ @Test
+ public void test_collapse_afterUpdateWhileExpanded() {
+ // Given
+ assertThat(mBubbleData.hasBubbles()).isFalse();
+ assertThat(mBubbleData.isExpanded()).isFalse();
+
+ sendUpdatedEntryAtTime(mEntryA1, 1000);
+ sendUpdatedEntryAtTime(mEntryB1, 2000);
+ sendUpdatedEntryAtTime(mEntryB2, 3000);
+ sendUpdatedEntryAtTime(mEntryA2, 4000);
+
+ assertThat(mBubbleData.getSelectedBubble()).isEqualTo(mBubbleA1);
+
+ changeExpandedStateAtTime(true, 5000L);
+ assertThat(mBubbleData.getBubbles()).isEqualTo(
+ ImmutableList.of(mBubbleA2, mBubbleA1, mBubbleB2, mBubbleB1));
+
+ sendUpdatedEntryAtTime(mEntryB1, 6000);
+
+ // (No reordering while expanded)
+ assertThat(mBubbleData.getBubbles()).isEqualTo(
+ ImmutableList.of(mBubbleA2, mBubbleA1, mBubbleB2, mBubbleB1));
+
+ // When
+ changeExpandedStateAtTime(false, 7000L);
+
+ // Then
+ // A1 moves to front on collapse, since it is the selected bubble (and most recently
+ // accessed).
+ // A2 moves next to A1 to maintain grouping.
+ // B1 moves in front of B2, since it received an update while expanded
+ assertThat(mBubbleData.getBubbles()).isEqualTo(
+ ImmutableList.of(mBubbleA1, mBubbleA2, mBubbleB1, mBubbleB2));
+ }
+
+ @Test
+ public void test_collapse_afterUpdateWhileExpanded_withOngoing() {
+ // Given
+ assertThat(mBubbleData.hasBubbles()).isFalse();
+ assertThat(mBubbleData.isExpanded()).isFalse();
+
+ sendUpdatedEntryAtTime(mEntryA1, 1000);
+ sendUpdatedEntryAtTime(mEntryB1, 2000);
+
+ setOngoing(mEntryB2, true);
+ sendUpdatedEntryAtTime(mEntryB2, 3000);
+
+ sendUpdatedEntryAtTime(mEntryA2, 4000);
+
+ assertThat(mBubbleData.getSelectedBubble()).isEqualTo(mBubbleA1);
+
+ changeExpandedStateAtTime(true, 5000L);
+ assertThat(mBubbleData.getBubbles()).isEqualTo(
+ ImmutableList.of(mBubbleB2, mBubbleB1, mBubbleA2, mBubbleA1));
+
+ sendUpdatedEntryAtTime(mEntryA1, 6000);
+
+ // No reordering if expanded
+ assertThat(mBubbleData.getBubbles()).isEqualTo(
+ ImmutableList.of(mBubbleB2, mBubbleB1, mBubbleA2, mBubbleA1));
+
+ // When
+ changeExpandedStateAtTime(false, 7000L);
+
+ // Then
+ // B2 remains in first position because it is ongoing.
+ // B1 remains grouped with B2
+ // A1 moves in front of A2, since it is more recently updated (and is selected).
+ // B1 moves in front of B2, since it has more recent activity.
+ assertThat(mBubbleData.getBubbles()).isEqualTo(
+ ImmutableList.of(mBubbleB2, mBubbleB1, mBubbleA1, mBubbleA2));
+ }
+
+ @Test
+ public void test_collapsed_removeLastBubble_clearsSelectedBubble() {
+ // Given
+ assertThat(mBubbleData.hasBubbles()).isFalse();
+ assertThat(mBubbleData.isExpanded()).isFalse();
+
+ sendUpdatedEntryAtTime(mEntryA1, 1000);
+ sendUpdatedEntryAtTime(mEntryB1, 2000);
+ sendUpdatedEntryAtTime(mEntryB2, 3000);
+ sendUpdatedEntryAtTime(mEntryA2, 4000);
+
+ assertThat(mBubbleData.getSelectedBubble()).isEqualTo(mBubbleA1);
+
+ mBubbleData.notificationEntryRemoved(mEntryA1, BubbleController.DISMISS_USER_GESTURE);
+ mBubbleData.notificationEntryRemoved(mEntryB1, BubbleController.DISMISS_USER_GESTURE);
+ mBubbleData.notificationEntryRemoved(mEntryB2, BubbleController.DISMISS_USER_GESTURE);
+ mBubbleData.notificationEntryRemoved(mEntryA2, BubbleController.DISMISS_USER_GESTURE);
+
+ assertThat(mBubbleData.getSelectedBubble()).isNull();
+ }
+
+ @Test
+ public void test_expanded_addBubble_atMaxBubbles_expiresLeastActive() {
+ // Given
+ sendUpdatedEntryAtTime(mEntryA1, 1000);
+ assertThat(mBubbleData.getSelectedBubble()).isEqualTo(mBubbleA1);
+
+ changeExpandedStateAtTime(true, 2000L);
+ assertThat(mBubbleData.getSelectedBubble().getLastActivity()).isEqualTo(2000);
+
+ sendUpdatedEntryAtTime(mEntryA2, 3000);
+ sendUpdatedEntryAtTime(mEntryA3, 4000);
+ sendUpdatedEntryAtTime(mEntryB1, 5000);
+ sendUpdatedEntryAtTime(mEntryB2, 6000);
+ sendUpdatedEntryAtTime(mEntryB3, 7000);
+
+
+ // Then
+ // A1 would be removed, but it is selected and expanded, so it should not go away.
+ // Instead, fall through to removing A2 (the next oldest).
+ assertThat(mBubbleData.getBubbles()).doesNotContain(mEntryA2);
+ }
+
+ @Test
+ public void test_expanded_removeLastBubble_collapsesStack() {
+ // Given
+ setPostTime(mEntryA1, 1000);
+ mBubbleData.notificationEntryUpdated(mEntryA1);
+
+ setPostTime(mEntryB1, 2000);
+ mBubbleData.notificationEntryUpdated(mEntryB1);
+
+ setPostTime(mEntryB2, 3000);
+ mBubbleData.notificationEntryUpdated(mEntryC1);
+
+ mBubbleData.setExpanded(true);
+
+ mBubbleData.notificationEntryRemoved(mEntryA1, BubbleController.DISMISS_USER_GESTURE);
+ mBubbleData.notificationEntryRemoved(mEntryB1, BubbleController.DISMISS_USER_GESTURE);
+ mBubbleData.notificationEntryRemoved(mEntryC1, BubbleController.DISMISS_USER_GESTURE);
+
+ assertThat(mBubbleData.isExpanded()).isFalse();
+ assertThat(mBubbleData.getSelectedBubble()).isNull();
+ }
+
+ // Bubbles do not reorder while expanded
+ @Test
+ public void test_expanded_selection_collapseToTop() {
+ // Given
+ assertThat(mBubbleData.hasBubbles()).isFalse();
+ assertThat(mBubbleData.isExpanded()).isFalse();
+
+ sendUpdatedEntryAtTime(mEntryA1, 1000);
+ sendUpdatedEntryAtTime(mEntryA2, 2000);
+ sendUpdatedEntryAtTime(mEntryB1, 3000);
+
+ assertThat(mBubbleData.getBubbles()).isEqualTo(
+ ImmutableList.of(mBubbleB1, mBubbleA2, mBubbleA1));
+
+ changeExpandedStateAtTime(true, 4000L);
+
+ // regrouping only happens when collapsed (after new or update) or expanded->collapsed
+ assertThat(mBubbleData.getBubbles()).isEqualTo(
+ ImmutableList.of(mBubbleB1, mBubbleA2, mBubbleA1));
+
+ changeExpandedStateAtTime(false, 6000L);
+
+ // A1 is still selected and it's lastAccessed time has been updated
+ // on collapse, sorting is applied, keeping the selected bubble at the front
+ assertThat(mBubbleData.getSelectedBubble()).isEqualTo(mBubbleA1);
+ assertThat(mBubbleData.getBubbles()).isEqualTo(
+ ImmutableList.of(mBubbleA1, mBubbleA2, mBubbleB1));
+ }
+
+ // New bubble from new app while stack is expanded
+ @Test
+ public void test_expanded_addBubble_newApp() {
+ // Given
+ sendUpdatedEntryAtTime(mEntryA1, 1000);
+ sendUpdatedEntryAtTime(mEntryA2, 2000);
+ sendUpdatedEntryAtTime(mEntryA3, 3000);
+ sendUpdatedEntryAtTime(mEntryB1, 4000);
+ sendUpdatedEntryAtTime(mEntryB2, 5000);
+
+ assertThat(mBubbleData.getSelectedBubble()).isEqualTo(mBubbleA1);
+
+ changeExpandedStateAtTime(true, 6000L);
+
+ assertThat(mBubbleData.getSelectedBubble()).isEqualTo(mBubbleA1);
+ assertThat(mBubbleData.getSelectedBubble().getLastActivity()).isEqualTo(6000L);
+
+ // regrouping only happens when collapsed (after new or update) or expanded->collapsed
+ assertThat(mBubbleData.getBubbles()).isEqualTo(
+ ImmutableList.of(mBubbleB2, mBubbleB1, mBubbleA3, mBubbleA2, mBubbleA1));
+
+ // When
+ sendUpdatedEntryAtTime(mEntryC1, 7000);
+
+ // Then
+ // A2 is expired. A1 was oldest, but lastActivityTime is reset when expanded, since A1 is
+ // selected.
+ // C1 is added at the end since bubbles are expanded.
+ assertThat(mBubbleData.getBubbles()).isEqualTo(
+ ImmutableList.of(mBubbleB2, mBubbleB1, mBubbleA3, mBubbleA1, mBubbleC1));
+ }
+
+ // New bubble from existing app while stack is expanded
+ @Test
+ public void test_expanded_addBubble_existingApp() {
+ // Given
+ sendUpdatedEntryAtTime(mEntryB1, 1000);
+ sendUpdatedEntryAtTime(mEntryB2, 2000);
+ sendUpdatedEntryAtTime(mEntryA1, 3000);
+ sendUpdatedEntryAtTime(mEntryA2, 4000);
+ sendUpdatedEntryAtTime(mEntryA3, 5000);
+
+ assertThat(mBubbleData.getSelectedBubble()).isEqualTo(mBubbleB1);
+
+ changeExpandedStateAtTime(true, 6000L);
+
+ // B1 is first (newest, since it's just been expanded and is selected)
+ assertThat(mBubbleData.getSelectedBubble()).isEqualTo(mBubbleB1);
+ assertThat(mBubbleData.getSelectedBubble().getLastActivity()).isEqualTo(6000L);
+
+ // regrouping only happens when collapsed (after new or update) or while collapsing
+ assertThat(mBubbleData.getBubbles()).isEqualTo(
+ ImmutableList.of(mBubbleA3, mBubbleA2, mBubbleA1, mBubbleB2, mBubbleB1));
+
+ // When
+ sendUpdatedEntryAtTime(mEntryB3, 7000);
+
+ // Then
+ // (B2 is expired, B1 was oldest, but it's lastActivityTime is updated at the point when
+ // the stack was expanded, since it is the selected bubble.
+ assertThat(mBubbleData.getBubbles()).isEqualTo(
+ ImmutableList.of(mBubbleA3, mBubbleA2, mBubbleA1, mBubbleB3, mBubbleB1));
+ }
+
+ // Updated bubble from existing app while stack is expanded
+ @Test
+ public void test_expanded_updateBubble_existingApp() {
+ sendUpdatedEntryAtTime(mEntryA1, 1000);
+ sendUpdatedEntryAtTime(mEntryA2, 2000);
+ sendUpdatedEntryAtTime(mEntryB1, 3000);
+ sendUpdatedEntryAtTime(mEntryB2, 4000);
+
+ assertThat(mBubbleData.getBubbles()).isEqualTo(
+ ImmutableList.of(mBubbleB2, mBubbleB1, mBubbleA2, mBubbleA1));
+ mBubbleData.setExpanded(true);
+
+ sendUpdatedEntryAtTime(mEntryA1, 5000);
+
+ // Does not reorder while expanded (for an update).
+ assertThat(mBubbleData.getBubbles()).isEqualTo(
+ ImmutableList.of(mBubbleB2, mBubbleB1, mBubbleA2, mBubbleA1));
+ }
+
+ @Test
+ public void test_expanded_updateBubble() {
+ // Given
+ assertThat(mBubbleData.hasBubbles()).isFalse();
+ assertThat(mBubbleData.isExpanded()).isFalse();
+
+ setPostTime(mEntryA1, 1000);
+ mBubbleData.notificationEntryUpdated(mEntryA1);
+
+ setPostTime(mEntryB1, 2000);
+ mBubbleData.notificationEntryUpdated(mEntryB1);
+
+ setPostTime(mEntryB2, 3000);
+ mBubbleData.notificationEntryUpdated(mEntryB2);
+
+ setPostTime(mEntryA2, 4000);
+ mBubbleData.notificationEntryUpdated(mEntryA2);
+
+ mBubbleData.setExpanded(true);
+ assertThat(mBubbleData.getBubbles()).isEqualTo(
+ ImmutableList.of(mBubbleA2, mBubbleA1, mBubbleB2, mBubbleB1));
+
+ // When
+ setPostTime(mEntryB1, 5000);
+ mBubbleData.notificationEntryUpdated(mEntryB1);
+
+ // Then
+ // B1 remains in the same place due to being expanded
+ assertThat(mBubbleData.getBubbles()).isEqualTo(
+ ImmutableList.of(mBubbleA2, mBubbleA1, mBubbleB2, mBubbleB1));
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
index e4b90c5..028fd7a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
@@ -234,7 +234,7 @@
* @param bubbleMetadata the bubble metadata to use for this notification if it exists.
* @return a notification that is in the group specified or standalone if unspecified
*/
- private Notification createNotification(boolean isGroupSummary,
+ public Notification createNotification(boolean isGroupSummary,
@Nullable String groupKey, @Nullable BubbleMetadata bubbleMetadata) {
Notification publicVersion = new Notification.Builder(mContext).setSmallIcon(
R.drawable.ic_person)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
index 25995eb..7bd25808 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
@@ -891,7 +891,31 @@
}
@Test
- public void testWillBeRemovedReturnsFalseBeforeBind() throws Exception {
+ public void testCloseControls_withoutHittingApply() throws Exception {
+ mNotificationChannel.setImportance(IMPORTANCE_LOW);
+ mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+ TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn,
+ (Runnable saveImportance, StatusBarNotification sbn) -> {
+ saveImportance.run();
+ }, null, null, true, false, IMPORTANCE_LOW, false
+ );
+
+ mNotificationInfo.findViewById(R.id.alert).performClick();
+
+ assertFalse(mNotificationInfo.shouldBeSaved());
+ }
+
+ @Test
+ public void testWillBeRemovedReturnsFalse() throws Exception {
+ assertFalse(mNotificationInfo.willBeRemoved());
+
+ mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+ TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn,
+ (Runnable saveImportance, StatusBarNotification sbn) -> {
+ saveImportance.run();
+ }, null, null, true, false, IMPORTANCE_LOW, false
+ );
+
assertFalse(mNotificationInfo.willBeRemoved());
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
index ce5bfce..616b46a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
@@ -130,6 +130,7 @@
when(mMockCm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE)).thenReturn(true);
when(mMockCm.getDefaultNetworkCapabilitiesForUser(0)).thenReturn(
new NetworkCapabilities[] { mNetCapabilities });
+ when(mMockTm.createForSubscriptionId(anyInt())).thenReturn(mMockTm);
mSignalStrength = mock(SignalStrength.class);
mServiceState = mock(ServiceState.class);
diff --git a/packages/overlays/AccentColorBlackOverlay/Android.mk b/packages/overlays/AccentColorBlackOverlay/Android.mk
index b81ae5bb..a689def 100644
--- a/packages/overlays/AccentColorBlackOverlay/Android.mk
+++ b/packages/overlays/AccentColorBlackOverlay/Android.mk
@@ -18,7 +18,7 @@
include $(CLEAR_VARS)
LOCAL_RRO_THEME := AccentColorBlack
-LOCAL_CERTIFICATE := platform
+
LOCAL_PRODUCT_MODULE := true
LOCAL_SRC_FILES := $(call all-subdir-java-files)
diff --git a/packages/overlays/AccentColorCinnamonOverlay/Android.mk b/packages/overlays/AccentColorCinnamonOverlay/Android.mk
index d53c114..3a6cbe3 100644
--- a/packages/overlays/AccentColorCinnamonOverlay/Android.mk
+++ b/packages/overlays/AccentColorCinnamonOverlay/Android.mk
@@ -18,7 +18,7 @@
include $(CLEAR_VARS)
LOCAL_RRO_THEME := AccentColorCinnamon
-LOCAL_CERTIFICATE := platform
+
LOCAL_PRODUCT_MODULE := true
LOCAL_SRC_FILES := $(call all-subdir-java-files)
diff --git a/packages/overlays/AccentColorGreenOverlay/Android.mk b/packages/overlays/AccentColorGreenOverlay/Android.mk
index db92157..d96dbe1 100644
--- a/packages/overlays/AccentColorGreenOverlay/Android.mk
+++ b/packages/overlays/AccentColorGreenOverlay/Android.mk
@@ -18,7 +18,7 @@
include $(CLEAR_VARS)
LOCAL_RRO_THEME := AccentColorGreen
-LOCAL_CERTIFICATE := platform
+
LOCAL_PRODUCT_MODULE := true
LOCAL_SRC_FILES := $(call all-subdir-java-files)
diff --git a/packages/overlays/AccentColorOceanOverlay/Android.mk b/packages/overlays/AccentColorOceanOverlay/Android.mk
index a28fc72..cf0c6b3 100644
--- a/packages/overlays/AccentColorOceanOverlay/Android.mk
+++ b/packages/overlays/AccentColorOceanOverlay/Android.mk
@@ -18,7 +18,7 @@
include $(CLEAR_VARS)
LOCAL_RRO_THEME := AccentColorOcean
-LOCAL_CERTIFICATE := platform
+
LOCAL_PRODUCT_MODULE := true
LOCAL_SRC_FILES := $(call all-subdir-java-files)
diff --git a/packages/overlays/AccentColorOrchidOverlay/Android.mk b/packages/overlays/AccentColorOrchidOverlay/Android.mk
index c635890..fc55bef 100644
--- a/packages/overlays/AccentColorOrchidOverlay/Android.mk
+++ b/packages/overlays/AccentColorOrchidOverlay/Android.mk
@@ -18,7 +18,7 @@
include $(CLEAR_VARS)
LOCAL_RRO_THEME := AccentColorOrchid
-LOCAL_CERTIFICATE := platform
+
LOCAL_PRODUCT_MODULE := true
LOCAL_SRC_FILES := $(call all-subdir-java-files)
diff --git a/packages/overlays/AccentColorPurpleOverlay/Android.mk b/packages/overlays/AccentColorPurpleOverlay/Android.mk
index d7dc497..3a28efa 100644
--- a/packages/overlays/AccentColorPurpleOverlay/Android.mk
+++ b/packages/overlays/AccentColorPurpleOverlay/Android.mk
@@ -18,7 +18,7 @@
include $(CLEAR_VARS)
LOCAL_RRO_THEME := AccentColorPurple
-LOCAL_CERTIFICATE := platform
+
LOCAL_PRODUCT_MODULE := true
LOCAL_SRC_FILES := $(call all-subdir-java-files)
diff --git a/packages/overlays/AccentColorSpaceOverlay/Android.mk b/packages/overlays/AccentColorSpaceOverlay/Android.mk
index a0edb96..78cbf73 100644
--- a/packages/overlays/AccentColorSpaceOverlay/Android.mk
+++ b/packages/overlays/AccentColorSpaceOverlay/Android.mk
@@ -18,7 +18,7 @@
include $(CLEAR_VARS)
LOCAL_RRO_THEME := AccentColorSpace
-LOCAL_CERTIFICATE := platform
+
LOCAL_PRODUCT_MODULE := true
LOCAL_SRC_FILES := $(call all-subdir-java-files)
diff --git a/packages/overlays/DisplayCutoutEmulationCornerOverlay/Android.mk b/packages/overlays/DisplayCutoutEmulationCornerOverlay/Android.mk
index bf2b631..b73aea3 100644
--- a/packages/overlays/DisplayCutoutEmulationCornerOverlay/Android.mk
+++ b/packages/overlays/DisplayCutoutEmulationCornerOverlay/Android.mk
@@ -2,7 +2,7 @@
include $(CLEAR_VARS)
LOCAL_RRO_THEME := DisplayCutoutEmulationCorner
-LOCAL_CERTIFICATE := platform
+
LOCAL_PRODUCT_MODULE := true
diff --git a/packages/overlays/DisplayCutoutEmulationDoubleOverlay/Android.mk b/packages/overlays/DisplayCutoutEmulationDoubleOverlay/Android.mk
index 7042906..8ca2dad 100644
--- a/packages/overlays/DisplayCutoutEmulationDoubleOverlay/Android.mk
+++ b/packages/overlays/DisplayCutoutEmulationDoubleOverlay/Android.mk
@@ -2,7 +2,7 @@
include $(CLEAR_VARS)
LOCAL_RRO_THEME := DisplayCutoutEmulationDouble
-LOCAL_CERTIFICATE := platform
+
LOCAL_PRODUCT_MODULE := true
diff --git a/packages/overlays/DisplayCutoutEmulationNarrowOverlay/Android.mk b/packages/overlays/DisplayCutoutEmulationNarrowOverlay/Android.mk
index ae69e11..7458cb5 100644
--- a/packages/overlays/DisplayCutoutEmulationNarrowOverlay/Android.mk
+++ b/packages/overlays/DisplayCutoutEmulationNarrowOverlay/Android.mk
@@ -2,7 +2,7 @@
include $(CLEAR_VARS)
LOCAL_RRO_THEME := DisplayCutoutEmulationNarrow
-LOCAL_CERTIFICATE := platform
+
LOCAL_PRODUCT_MODULE := true
diff --git a/packages/overlays/DisplayCutoutEmulationTallOverlay/Android.mk b/packages/overlays/DisplayCutoutEmulationTallOverlay/Android.mk
index 7dcadfb..1a405e2 100644
--- a/packages/overlays/DisplayCutoutEmulationTallOverlay/Android.mk
+++ b/packages/overlays/DisplayCutoutEmulationTallOverlay/Android.mk
@@ -2,7 +2,7 @@
include $(CLEAR_VARS)
LOCAL_RRO_THEME := DisplayCutoutEmulationTall
-LOCAL_CERTIFICATE := platform
+
LOCAL_PRODUCT_MODULE := true
diff --git a/packages/overlays/DisplayCutoutEmulationWideOverlay/Android.mk b/packages/overlays/DisplayCutoutEmulationWideOverlay/Android.mk
index 3f7be73..3ebc540 100644
--- a/packages/overlays/DisplayCutoutEmulationWideOverlay/Android.mk
+++ b/packages/overlays/DisplayCutoutEmulationWideOverlay/Android.mk
@@ -2,7 +2,7 @@
include $(CLEAR_VARS)
LOCAL_RRO_THEME := DisplayCutoutEmulationWide
-LOCAL_CERTIFICATE := platform
+
LOCAL_PRODUCT_MODULE := true
diff --git a/packages/overlays/FontNotoSerifSourceOverlay/Android.mk b/packages/overlays/FontNotoSerifSourceOverlay/Android.mk
index 6f3c4f7..f4eedaf 100644
--- a/packages/overlays/FontNotoSerifSourceOverlay/Android.mk
+++ b/packages/overlays/FontNotoSerifSourceOverlay/Android.mk
@@ -18,7 +18,7 @@
include $(CLEAR_VARS)
LOCAL_RRO_THEME := FontNotoSerifSource
-LOCAL_CERTIFICATE := platform
+
LOCAL_PRODUCT_MODULE := true
LOCAL_SRC_FILES := $(call all-subdir-java-files)
diff --git a/packages/overlays/IconPackCircularAndroidOverlay/Android.mk b/packages/overlays/IconPackCircularAndroidOverlay/Android.mk
index 60f525b6..8f3baa5 100644
--- a/packages/overlays/IconPackCircularAndroidOverlay/Android.mk
+++ b/packages/overlays/IconPackCircularAndroidOverlay/Android.mk
@@ -17,7 +17,7 @@
include $(CLEAR_VARS)
LOCAL_RRO_THEME := IconPackCircularAndroid
-LOCAL_CERTIFICATE := platform
+
LOCAL_PRODUCT_MODULE := true
LOCAL_SRC_FILES := $(call all-subdir-java-files)
diff --git a/packages/overlays/IconPackCircularLauncherOverlay/Android.mk b/packages/overlays/IconPackCircularLauncherOverlay/Android.mk
index a5277fa..310bdef 100644
--- a/packages/overlays/IconPackCircularLauncherOverlay/Android.mk
+++ b/packages/overlays/IconPackCircularLauncherOverlay/Android.mk
@@ -18,7 +18,7 @@
include $(CLEAR_VARS)
LOCAL_RRO_THEME := IconPackCircularLauncher
-LOCAL_CERTIFICATE := platform
+
LOCAL_PRODUCT_MODULE := true
LOCAL_SRC_FILES := $(call all-subdir-java-files)
diff --git a/packages/overlays/IconPackCircularSettingsOverlay/Android.mk b/packages/overlays/IconPackCircularSettingsOverlay/Android.mk
index ad7324d..d067322 100644
--- a/packages/overlays/IconPackCircularSettingsOverlay/Android.mk
+++ b/packages/overlays/IconPackCircularSettingsOverlay/Android.mk
@@ -18,7 +18,7 @@
include $(CLEAR_VARS)
LOCAL_RRO_THEME := IconPackCircularSettings
-LOCAL_CERTIFICATE := platform
+
LOCAL_PRODUCT_MODULE := true
LOCAL_SRC_FILES := $(call all-subdir-java-files)
diff --git a/packages/overlays/IconPackCircularSystemUIOverlay/Android.mk b/packages/overlays/IconPackCircularSystemUIOverlay/Android.mk
index 711063d..5e0dcbe 100644
--- a/packages/overlays/IconPackCircularSystemUIOverlay/Android.mk
+++ b/packages/overlays/IconPackCircularSystemUIOverlay/Android.mk
@@ -18,7 +18,7 @@
include $(CLEAR_VARS)
LOCAL_RRO_THEME := IconPackCircularSystemUI
-LOCAL_CERTIFICATE := platform
+
LOCAL_PRODUCT_MODULE := true
LOCAL_SRC_FILES := $(call all-subdir-java-files)
diff --git a/packages/overlays/IconPackFilledAndroidOverlay/Android.mk b/packages/overlays/IconPackFilledAndroidOverlay/Android.mk
index e0db3a2..3036f7d 100644
--- a/packages/overlays/IconPackFilledAndroidOverlay/Android.mk
+++ b/packages/overlays/IconPackFilledAndroidOverlay/Android.mk
@@ -17,7 +17,7 @@
include $(CLEAR_VARS)
LOCAL_RRO_THEME := IconPackFilledAndroid
-LOCAL_CERTIFICATE := platform
+
LOCAL_PRODUCT_MODULE := true
LOCAL_SRC_FILES := $(call all-subdir-java-files)
diff --git a/packages/overlays/IconPackFilledLauncherOverlay/Android.mk b/packages/overlays/IconPackFilledLauncherOverlay/Android.mk
index d2e5b60..2460fa4 100644
--- a/packages/overlays/IconPackFilledLauncherOverlay/Android.mk
+++ b/packages/overlays/IconPackFilledLauncherOverlay/Android.mk
@@ -18,7 +18,7 @@
include $(CLEAR_VARS)
LOCAL_RRO_THEME := IconPackFilledLauncher
-LOCAL_CERTIFICATE := platform
+
LOCAL_PRODUCT_MODULE := true
LOCAL_SRC_FILES := $(call all-subdir-java-files)
diff --git a/packages/overlays/IconPackFilledSettingsOverlay/Android.mk b/packages/overlays/IconPackFilledSettingsOverlay/Android.mk
index 0443560..3cc071d 100644
--- a/packages/overlays/IconPackFilledSettingsOverlay/Android.mk
+++ b/packages/overlays/IconPackFilledSettingsOverlay/Android.mk
@@ -18,7 +18,7 @@
include $(CLEAR_VARS)
LOCAL_RRO_THEME := IconPackFilledSettings
-LOCAL_CERTIFICATE := platform
+
LOCAL_PRODUCT_MODULE := true
LOCAL_SRC_FILES := $(call all-subdir-java-files)
diff --git a/packages/overlays/IconPackFilledSystemUIOverlay/Android.mk b/packages/overlays/IconPackFilledSystemUIOverlay/Android.mk
index 2506132..f027692 100644
--- a/packages/overlays/IconPackFilledSystemUIOverlay/Android.mk
+++ b/packages/overlays/IconPackFilledSystemUIOverlay/Android.mk
@@ -18,7 +18,7 @@
include $(CLEAR_VARS)
LOCAL_RRO_THEME := IconPackFilledSystemUI
-LOCAL_CERTIFICATE := platform
+
LOCAL_PRODUCT_MODULE := true
LOCAL_SRC_FILES := $(call all-subdir-java-files)
diff --git a/packages/overlays/IconPackRoundedAndroidOverlay/Android.mk b/packages/overlays/IconPackRoundedAndroidOverlay/Android.mk
index 2937fb8..c6ad4ac 100644
--- a/packages/overlays/IconPackRoundedAndroidOverlay/Android.mk
+++ b/packages/overlays/IconPackRoundedAndroidOverlay/Android.mk
@@ -17,7 +17,7 @@
include $(CLEAR_VARS)
LOCAL_RRO_THEME := IconPackRoundedAndroid
-LOCAL_CERTIFICATE := platform
+
LOCAL_PRODUCT_MODULE := true
LOCAL_SRC_FILES := $(call all-subdir-java-files)
diff --git a/packages/overlays/IconPackRoundedLauncherOverlay/Android.mk b/packages/overlays/IconPackRoundedLauncherOverlay/Android.mk
index 7adfe3b..713e281 100644
--- a/packages/overlays/IconPackRoundedLauncherOverlay/Android.mk
+++ b/packages/overlays/IconPackRoundedLauncherOverlay/Android.mk
@@ -18,7 +18,7 @@
include $(CLEAR_VARS)
LOCAL_RRO_THEME := IconPackRoundedLauncher
-LOCAL_CERTIFICATE := platform
+
LOCAL_PRODUCT_MODULE := true
LOCAL_SRC_FILES := $(call all-subdir-java-files)
diff --git a/packages/overlays/IconPackRoundedSettingsOverlay/Android.mk b/packages/overlays/IconPackRoundedSettingsOverlay/Android.mk
index 44ac6dd..6c77519 100644
--- a/packages/overlays/IconPackRoundedSettingsOverlay/Android.mk
+++ b/packages/overlays/IconPackRoundedSettingsOverlay/Android.mk
@@ -18,7 +18,7 @@
include $(CLEAR_VARS)
LOCAL_RRO_THEME := IconPackRoundedSettings
-LOCAL_CERTIFICATE := platform
+
LOCAL_PRODUCT_MODULE := true
LOCAL_SRC_FILES := $(call all-subdir-java-files)
diff --git a/packages/overlays/IconPackRoundedSystemUIOverlay/Android.mk b/packages/overlays/IconPackRoundedSystemUIOverlay/Android.mk
index 2d34a54..4e21b41 100644
--- a/packages/overlays/IconPackRoundedSystemUIOverlay/Android.mk
+++ b/packages/overlays/IconPackRoundedSystemUIOverlay/Android.mk
@@ -18,7 +18,7 @@
include $(CLEAR_VARS)
LOCAL_RRO_THEME := IconPackRoundedSystemUI
-LOCAL_CERTIFICATE := platform
+
LOCAL_PRODUCT_MODULE := true
LOCAL_SRC_FILES := $(call all-subdir-java-files)
diff --git a/packages/overlays/IconShapeRoundedRectOverlay/Android.mk b/packages/overlays/IconShapeRoundedRectOverlay/Android.mk
index 08428d1..21cd011 100644
--- a/packages/overlays/IconShapeRoundedRectOverlay/Android.mk
+++ b/packages/overlays/IconShapeRoundedRectOverlay/Android.mk
@@ -18,7 +18,7 @@
include $(CLEAR_VARS)
LOCAL_RRO_THEME := IconShapeRoundedRect
-LOCAL_CERTIFICATE := platform
+
LOCAL_PRODUCT_MODULE := true
LOCAL_SRC_FILES := $(call all-subdir-java-files)
diff --git a/packages/overlays/IconShapeSquareOverlay/Android.mk b/packages/overlays/IconShapeSquareOverlay/Android.mk
index ceb745a..c872883 100644
--- a/packages/overlays/IconShapeSquareOverlay/Android.mk
+++ b/packages/overlays/IconShapeSquareOverlay/Android.mk
@@ -18,7 +18,7 @@
include $(CLEAR_VARS)
LOCAL_RRO_THEME := IconShapeSquare
-LOCAL_CERTIFICATE := platform
+
LOCAL_PRODUCT_MODULE := true
LOCAL_SRC_FILES := $(call all-subdir-java-files)
diff --git a/packages/overlays/IconShapeSquircleOverlay/Android.mk b/packages/overlays/IconShapeSquircleOverlay/Android.mk
index 34edc3b..fa5fe69 100644
--- a/packages/overlays/IconShapeSquircleOverlay/Android.mk
+++ b/packages/overlays/IconShapeSquircleOverlay/Android.mk
@@ -18,7 +18,7 @@
include $(CLEAR_VARS)
LOCAL_RRO_THEME := IconShapeSquircle
-LOCAL_CERTIFICATE := platform
+
LOCAL_PRODUCT_MODULE := true
LOCAL_SRC_FILES := $(call all-subdir-java-files)
diff --git a/packages/overlays/IconShapeTeardropOverlay/Android.mk b/packages/overlays/IconShapeTeardropOverlay/Android.mk
index 834a1c3..d5f01f3 100644
--- a/packages/overlays/IconShapeTeardropOverlay/Android.mk
+++ b/packages/overlays/IconShapeTeardropOverlay/Android.mk
@@ -18,7 +18,7 @@
include $(CLEAR_VARS)
LOCAL_RRO_THEME := IconShapeTeardrop
-LOCAL_CERTIFICATE := platform
+
LOCAL_PRODUCT_MODULE := true
LOCAL_SRC_FILES := $(call all-subdir-java-files)
diff --git a/packages/overlays/NavigationBarMode2ButtonOverlay/Android.mk b/packages/overlays/NavigationBarMode2ButtonOverlay/Android.mk
index 410d6d8..be86ef2 100644
--- a/packages/overlays/NavigationBarMode2ButtonOverlay/Android.mk
+++ b/packages/overlays/NavigationBarMode2ButtonOverlay/Android.mk
@@ -18,7 +18,7 @@
include $(CLEAR_VARS)
LOCAL_RRO_THEME := NavigationBarMode2Button
-LOCAL_CERTIFICATE := platform
+
LOCAL_SRC_FILES := $(call all-subdir-java-files)
diff --git a/packages/overlays/NavigationBarMode3ButtonOverlay/Android.mk b/packages/overlays/NavigationBarMode3ButtonOverlay/Android.mk
index 2bc9a6a..f44a362 100644
--- a/packages/overlays/NavigationBarMode3ButtonOverlay/Android.mk
+++ b/packages/overlays/NavigationBarMode3ButtonOverlay/Android.mk
@@ -18,7 +18,7 @@
include $(CLEAR_VARS)
LOCAL_RRO_THEME := NavigationBarMode3Button
-LOCAL_CERTIFICATE := platform
+
LOCAL_SRC_FILES := $(call all-subdir-java-files)
diff --git a/packages/overlays/NavigationBarModeGesturalOverlay/Android.mk b/packages/overlays/NavigationBarModeGesturalOverlay/Android.mk
index 5f7e0eb..02e2074 100644
--- a/packages/overlays/NavigationBarModeGesturalOverlay/Android.mk
+++ b/packages/overlays/NavigationBarModeGesturalOverlay/Android.mk
@@ -18,7 +18,7 @@
include $(CLEAR_VARS)
LOCAL_RRO_THEME := NavigationBarModeGestural
-LOCAL_CERTIFICATE := platform
+
LOCAL_SRC_FILES := $(call all-subdir-java-files)
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 9e73684d..9eda9db 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -33,7 +33,6 @@
import static com.android.server.autofill.Helper.sDebug;
import static com.android.server.autofill.Helper.sVerbose;
import static com.android.server.autofill.Helper.toArray;
-import static com.android.server.autofill.ViewState.STATE_RESTARTED_SESSION;
import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_RECEIVER_EXTRAS;
import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_STRUCTURE;
@@ -564,7 +563,8 @@
* Reads a new structure and then request a new fill response from the fill service.
*/
@GuardedBy("mLock")
- private void requestNewFillResponseLocked(int flags) {
+ private void requestNewFillResponseLocked(@NonNull ViewState viewState, int newState,
+ int flags) {
if (mForAugmentedAutofillOnly || (flags & FLAG_AUGMENTED_AUTOFILL_REQUEST) != 0) {
// TODO(b/122858578): log metrics
if (sVerbose) {
@@ -575,6 +575,7 @@
triggerAugmentedAutofillLocked();
return;
}
+ viewState.setState(newState);
int requestId;
@@ -2165,19 +2166,17 @@
@NonNull ViewState viewState, int flags) {
if ((flags & FLAG_MANUAL_REQUEST) != 0) {
if (sDebug) Slog.d(TAG, "Re-starting session on view " + id + " and flags " + flags);
- viewState.setState(STATE_RESTARTED_SESSION);
- requestNewFillResponseLocked(flags);
+ requestNewFillResponseLocked(viewState, ViewState.STATE_RESTARTED_SESSION, flags);
return;
}
// If it's not, then check if it it should start a partition.
if (shouldStartNewPartitionLocked(id)) {
if (sDebug) {
- Slog.d(TAG, "Starting partition for view id " + id + ": "
+ Slog.d(TAG, "Starting partition or augmented request for view id " + id + ": "
+ viewState.getStateAsString());
}
- viewState.setState(ViewState.STATE_STARTED_PARTITION);
- requestNewFillResponseLocked(flags);
+ requestNewFillResponseLocked(viewState, ViewState.STATE_STARTED_PARTITION, flags);
} else {
if (sVerbose) {
Slog.v(TAG, "Not starting new partition for view " + id + ": "
@@ -2283,8 +2282,7 @@
// View is triggering autofill.
mCurrentViewId = viewState.id;
viewState.update(value, virtualBounds, flags);
- viewState.setState(ViewState.STATE_STARTED_SESSION);
- requestNewFillResponseLocked(flags);
+ requestNewFillResponseLocked(viewState, ViewState.STATE_STARTED_SESSION, flags);
break;
case ACTION_VALUE_CHANGED:
if (mCompatMode && (viewState.getState() & ViewState.STATE_URL_BAR) != 0) {
@@ -2386,6 +2384,10 @@
Slog.v(TAG, "entered on virtual child " + id + ": " + virtualBounds);
}
+ // Update the view states first...
+ mCurrentViewId = viewState.id;
+ viewState.setCurrentValue(value);
+
if (mCompatMode && (viewState.getState() & ViewState.STATE_URL_BAR) != 0) {
if (sDebug) Slog.d(TAG, "Ignoring VIEW_ENTERED on URL BAR (id=" + id + ")");
return;
@@ -2397,10 +2399,6 @@
if (sDebug) Slog.d(TAG, "updateLocked(" + id + "): augmented-autofillable");
- // Update the view states first...
- mCurrentViewId = viewState.id;
- viewState.setCurrentValue(value);
-
// ...then trigger the augmented autofill UI
triggerAugmentedAutofillLocked();
return;
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index da91187..28bc348 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -66,6 +66,7 @@
import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.TelephonyIntents;
import com.android.internal.telephony.TelephonyPermissions;
+import com.android.internal.util.ArrayUtils;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.am.BatteryStatsService;
@@ -1164,36 +1165,33 @@
@Override
public void notifyCarrierNetworkChange(boolean active) {
- // only CarrierService with carrier privilege rule should have the permission.
- int subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
- try {
- subId = Arrays.stream(SubscriptionManager.from(mContext)
+ // only CarrierService with carrier privilege rule should have the permission
+ int[] subIds = Arrays.stream(SubscriptionManager.from(mContext)
.getActiveSubscriptionIdList())
- .filter(i -> TelephonyPermissions.checkCarrierPrivilegeForSubId(i))
- .findFirst().getAsInt();
- } catch (NoSuchElementException ex) {
+ .filter(i -> TelephonyPermissions.checkCarrierPrivilegeForSubId(i)).toArray();
+ if (ArrayUtils.isEmpty(subIds)) {
loge("notifyCarrierNetworkChange without carrier privilege");
- }
- // the active subId does not have carrier privilege.
- if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+ // the active subId does not have carrier privilege.
throw new SecurityException("notifyCarrierNetworkChange without carrier privilege");
}
- int phoneId = SubscriptionManager.getPhoneId(subId);
-
- if (VDBG) {
- log("notifyCarrierNetworkChange: active=" + active + "subId: " + subId);
- }
synchronized (mRecords) {
mCarrierNetworkChangeState = active;
- for (Record r : mRecords) {
- if (r.matchPhoneStateListenerEvent(
- PhoneStateListener.LISTEN_CARRIER_NETWORK_CHANGE) &&
- idMatch(r.subId, subId, phoneId)) {
- try {
- r.callback.onCarrierNetworkChange(active);
- } catch (RemoteException ex) {
- mRemoveList.add(r.binder);
+ for (int subId : subIds) {
+ int phoneId = SubscriptionManager.getPhoneId(subId);
+
+ if (VDBG) {
+ log("notifyCarrierNetworkChange: active=" + active + "subId: " + subId);
+ }
+ for (Record r : mRecords) {
+ if (r.matchPhoneStateListenerEvent(
+ PhoneStateListener.LISTEN_CARRIER_NETWORK_CHANGE) &&
+ idMatch(r.subId, subId, phoneId)) {
+ try {
+ r.callback.onCarrierNetworkChange(active);
+ } catch (RemoteException ex) {
+ mRemoveList.add(r.binder);
+ }
}
}
}
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index d5883bb..3a397cd 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -18,6 +18,7 @@
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER;
+import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
import android.annotation.NonNull;
import android.annotation.UserIdInt;
@@ -1801,7 +1802,8 @@
*
* @see #maybeQueueReadyJobsForExecutionLocked
*/
- private JobStatus getRescheduleJobForFailureLocked(JobStatus failureToReschedule) {
+ @VisibleForTesting
+ JobStatus getRescheduleJobForFailureLocked(JobStatus failureToReschedule) {
final long elapsedNowMillis = sElapsedRealtimeClock.millis();
final JobInfo job = failureToReschedule.getJob();
@@ -1848,6 +1850,10 @@
elapsedNowMillis + delayMillis,
JobStatus.NO_LATEST_RUNTIME, backoffAttempts,
failureToReschedule.getLastSuccessfulRunTime(), sSystemClock.millis());
+ if (job.isPeriodic()) {
+ newJob.setOriginalLatestRunTimeElapsed(
+ failureToReschedule.getOriginalLatestRunTimeElapsed());
+ }
for (int ic=0; ic<mControllers.size(); ic++) {
StateController controller = mControllers.get(ic);
controller.rescheduleForFailureLocked(newJob, failureToReschedule);
@@ -1868,23 +1874,41 @@
* @return A new job representing the execution criteria for this instantiation of the
* recurring job.
*/
- private JobStatus getRescheduleJobForPeriodic(JobStatus periodicToReschedule) {
+ @VisibleForTesting
+ JobStatus getRescheduleJobForPeriodic(JobStatus periodicToReschedule) {
final long elapsedNow = sElapsedRealtimeClock.millis();
- // Compute how much of the period is remaining.
- long runEarly = 0L;
+ final long newLatestRuntimeElapsed;
+ final long period = periodicToReschedule.getJob().getIntervalMillis();
+ final long latestRunTimeElapsed = periodicToReschedule.getOriginalLatestRunTimeElapsed();
+ final long flex = periodicToReschedule.getJob().getFlexMillis();
- // If this periodic was rescheduled it won't have a deadline.
- if (periodicToReschedule.hasDeadlineConstraint()) {
- runEarly = Math.max(periodicToReschedule.getLatestRunTimeElapsed() - elapsedNow, 0L);
+ if (elapsedNow > latestRunTimeElapsed) {
+ // The job ran past its expected run window. Have it count towards the current window
+ // and schedule a new job for the next window.
+ if (DEBUG) {
+ Slog.i(TAG, "Periodic job ran after its intended window.");
+ }
+ final long diffMs = (elapsedNow - latestRunTimeElapsed);
+ int numSkippedWindows = (int) (diffMs / period) + 1; // +1 to include original window
+ if (period != flex && diffMs > Math.min(30 * MINUTE_IN_MILLIS, (period - flex) / 2)) {
+ if (DEBUG) {
+ Slog.d(TAG, "Custom flex job ran too close to next window.");
+ }
+ // For custom flex periods, if the job was run too close to the next window,
+ // skip the next window and schedule for the following one.
+ numSkippedWindows += 1;
+ }
+ newLatestRuntimeElapsed = latestRunTimeElapsed + (period * numSkippedWindows);
+ } else {
+ newLatestRuntimeElapsed = latestRunTimeElapsed + period;
}
- long flex = periodicToReschedule.getJob().getFlexMillis();
- long period = periodicToReschedule.getJob().getIntervalMillis();
- long newLatestRuntimeElapsed = elapsedNow + runEarly + period;
- long newEarliestRunTimeElapsed = newLatestRuntimeElapsed - flex;
+
+ final long newEarliestRunTimeElapsed = newLatestRuntimeElapsed - flex;
if (DEBUG) {
Slog.v(TAG, "Rescheduling executed periodic. New execution window [" +
- newEarliestRunTimeElapsed/1000 + ", " + newLatestRuntimeElapsed/1000 + "]s");
+ newEarliestRunTimeElapsed / 1000 + ", " + newLatestRuntimeElapsed / 1000
+ + "]s");
}
return new JobStatus(periodicToReschedule, getCurrentHeartbeat(),
newEarliestRunTimeElapsed, newLatestRuntimeElapsed,
diff --git a/services/core/java/com/android/server/job/controllers/JobStatus.java b/services/core/java/com/android/server/job/controllers/JobStatus.java
index 48f21e4..fd20e11 100644
--- a/services/core/java/com/android/server/job/controllers/JobStatus.java
+++ b/services/core/java/com/android/server/job/controllers/JobStatus.java
@@ -161,6 +161,12 @@
*/
private final long latestRunTimeElapsedMillis;
+ /**
+ * Valid only for periodic jobs. The original latest point in the future at which this
+ * job was expected to run.
+ */
+ private long mOriginalLatestRunTimeElapsedMillis;
+
/** How many times this job has failed, used to compute back-off. */
private final int numFailures;
@@ -394,6 +400,7 @@
this.earliestRunTimeElapsedMillis = earliestRunTimeElapsedMillis;
this.latestRunTimeElapsedMillis = latestRunTimeElapsedMillis;
+ this.mOriginalLatestRunTimeElapsedMillis = latestRunTimeElapsedMillis;
this.numFailures = numFailures;
int requiredConstraints = job.getConstraintFlags();
@@ -871,6 +878,14 @@
return latestRunTimeElapsedMillis;
}
+ public long getOriginalLatestRunTimeElapsed() {
+ return mOriginalLatestRunTimeElapsedMillis;
+ }
+
+ public void setOriginalLatestRunTimeElapsed(long latestRunTimeElapsed) {
+ mOriginalLatestRunTimeElapsedMillis = latestRunTimeElapsed;
+ }
+
/**
* Return the fractional position of "now" within the "run time" window of
* this job.
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index c6f6c50a..19ff2c1 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -1164,6 +1164,15 @@
// Carrier might want to manage notifications themselves
final PersistableBundle config = mCarrierConfigManager.getConfigForSubId(subId);
+ if (!CarrierConfigManager.isConfigForIdentifiedCarrier(config)) {
+ if (LOGV) Slog.v(TAG, "isConfigForIdentifiedCarrier returned false");
+ // Don't show notifications until we confirm that the loaded config is from an
+ // identified carrier, which may want to manage their own notifications. This method
+ // should be called every time the carrier config changes anyways, and there's no
+ // reason to alert if there isn't a carrier.
+ return;
+ }
+
final boolean notifyWarning = getBooleanDefeatingNullable(config,
KEY_DATA_WARNING_NOTIFICATION_BOOL, true);
final boolean notifyLimit = getBooleanDefeatingNullable(config,
diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java
index ee07c7d..209ccda 100644
--- a/services/core/java/com/android/server/om/OverlayManagerService.java
+++ b/services/core/java/com/android/server/om/OverlayManagerService.java
@@ -720,6 +720,26 @@
}
@Override
+ public String[] getDefaultOverlayPackages() throws RemoteException {
+ try {
+ traceBegin(TRACE_TAG_RRO, "OMS#getDefaultOverlayPackages");
+ getContext().enforceCallingOrSelfPermission(
+ android.Manifest.permission.MODIFY_THEME_OVERLAY, null);
+
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ return mImpl.getDefaultOverlayPackages();
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ } finally {
+ traceEnd(TRACE_TAG_RRO);
+ }
+ }
+
+ @Override
public void onShellCommand(@NonNull final FileDescriptor in,
@NonNull final FileDescriptor out, @NonNull final FileDescriptor err,
@NonNull final String[] args, @NonNull final ShellCallback callback,
diff --git a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
index 3a84b1e..092dbc8 100644
--- a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
+++ b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
@@ -641,6 +641,10 @@
pw.println("Default overlays: " + TextUtils.join(";", mDefaultOverlays));
}
+ @NonNull String[] getDefaultOverlayPackages() {
+ return mDefaultOverlays;
+ }
+
List<String> getEnabledOverlayPackageNames(@NonNull final String targetPackageName,
final int userId) {
final List<OverlayInfo> overlays = mSettings.getOverlaysForTarget(targetPackageName,
diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java
index 5430f4c..497385f 100644
--- a/services/core/java/com/android/server/pm/ApexManager.java
+++ b/services/core/java/com/android/server/pm/ApexManager.java
@@ -22,22 +22,21 @@
import android.apex.ApexInfoList;
import android.apex.ApexSessionInfo;
import android.apex.IApexService;
-import android.content.BroadcastReceiver;
import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageParser;
import android.content.pm.PackageParser.PackageParserException;
+import android.os.HandlerThread;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.ServiceManager.ServiceNotFoundException;
+import android.os.SystemClock;
import android.sysprop.ApexProperties;
import android.util.Slog;
-import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.IndentingPrintWriter;
+import com.android.server.SystemService;
import java.io.File;
import java.io.PrintWriter;
@@ -46,75 +45,108 @@
import java.util.Collections;
import java.util.List;
import java.util.Map;
+import java.util.concurrent.CountDownLatch;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* ApexManager class handles communications with the apex service to perform operation and queries,
* as well as providing caching to avoid unnecessary calls to the service.
+ *
+ * @hide
*/
-class ApexManager {
- static final String TAG = "ApexManager";
- private final IApexService mApexService;
- private final Context mContext;
- private final Object mLock = new Object();
- @GuardedBy("mLock")
+public final class ApexManager extends SystemService {
+ private static final String TAG = "ApexManager";
+ private IApexService mApexService;
+
+ private final CountDownLatch mActivePackagesCacheLatch = new CountDownLatch(1);
private Map<String, PackageInfo> mActivePackagesCache;
- ApexManager(Context context) {
+ private final CountDownLatch mApexFilesCacheLatch = new CountDownLatch(1);
+ private ApexInfo[] mApexFiles;
+
+ public ApexManager(Context context) {
+ super(context);
+ }
+
+ @Override
+ public void onStart() {
try {
mApexService = IApexService.Stub.asInterface(
- ServiceManager.getServiceOrThrow("apexservice"));
+ ServiceManager.getServiceOrThrow("apexservice"));
} catch (ServiceNotFoundException e) {
throw new IllegalStateException("Required service apexservice not available");
}
- mContext = context;
+ publishLocalService(ApexManager.class, this);
+ HandlerThread oneShotThread = new HandlerThread("ApexManagerOneShotHandler");
+ oneShotThread.start();
+ oneShotThread.getThreadHandler().post(this::initSequence);
+ oneShotThread.quitSafely();
}
- void systemReady() {
- mContext.registerReceiver(new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- onBootCompleted();
- mContext.unregisterReceiver(this);
- }
- }, new IntentFilter(Intent.ACTION_BOOT_COMPLETED));
+ private void initSequence() {
+ populateApexFilesCache();
+ parseApexFiles();
}
- private void populateActivePackagesCacheIfNeeded() {
- synchronized (mLock) {
- if (mActivePackagesCache != null) {
- return;
- }
+ private void populateApexFilesCache() {
+ if (mApexFiles != null) {
+ return;
+ }
+ long startTimeMicros = SystemClock.currentTimeMicro();
+ Slog.i(TAG, "Starting to populate apex files cache");
+ try {
+ mApexFiles = mApexService.getActivePackages();
+ Slog.i(TAG, "IPC to apexd finished in " + (SystemClock.currentTimeMicro()
+ - startTimeMicros) + " μs");
+ } catch (RemoteException re) {
+ // TODO: make sure this error is propagated to system server.
+ Slog.e(TAG, "Unable to retrieve packages from apexservice: " + re.toString());
+ re.rethrowAsRuntimeException();
+ }
+ mApexFilesCacheLatch.countDown();
+ Slog.i(TAG, "Finished populating apex files cache in " + (SystemClock.currentTimeMicro()
+ - startTimeMicros) + " μs");
+ }
+
+ private void parseApexFiles() {
+ waitForLatch(mApexFilesCacheLatch);
+ if (mApexFiles == null) {
+ throw new IllegalStateException("mApexFiles must be populated");
+ }
+ long startTimeMicros = SystemClock.currentTimeMicro();
+ Slog.i(TAG, "Starting to parse apex files");
+ List<PackageInfo> list = new ArrayList<>();
+ // TODO: this can be parallelized.
+ for (ApexInfo ai : mApexFiles) {
try {
- List<PackageInfo> list = new ArrayList<>();
- final ApexInfo[] activePkgs = mApexService.getActivePackages();
- for (ApexInfo ai : activePkgs) {
- // If the device is using flattened APEX, don't report any APEX
- // packages since they won't be managed or updated by PackageManager.
- if ((new File(ai.packagePath)).isDirectory()) {
- break;
- }
- try {
- list.add(PackageParser.generatePackageInfoFromApex(
- new File(ai.packagePath), PackageManager.GET_META_DATA
- | PackageManager.GET_SIGNING_CERTIFICATES));
- } catch (PackageParserException pe) {
- throw new IllegalStateException("Unable to parse: " + ai, pe);
- }
+ // If the device is using flattened APEX, don't report any APEX
+ // packages since they won't be managed or updated by PackageManager.
+ if ((new File(ai.packagePath)).isDirectory()) {
+ break;
}
- mActivePackagesCache = list.stream().collect(
- Collectors.toMap(p -> p.packageName, Function.identity()));
- } catch (RemoteException re) {
- Slog.e(TAG, "Unable to retrieve packages from apexservice: " + re.toString());
- throw new RuntimeException(re);
+ list.add(PackageParser.generatePackageInfoFromApex(
+ new File(ai.packagePath), PackageManager.GET_META_DATA
+ | PackageManager.GET_SIGNING_CERTIFICATES));
+ } catch (PackageParserException pe) {
+ // TODO: make sure this error is propagated to system server.
+ throw new IllegalStateException("Unable to parse: " + ai, pe);
}
}
+ mActivePackagesCache = list.stream().collect(
+ Collectors.toMap(p -> p.packageName, Function.identity()));
+ mActivePackagesCacheLatch.countDown();
+ Slog.i(TAG, "Finished parsing apex files in " + (SystemClock.currentTimeMicro()
+ - startTimeMicros) + " μs");
}
/**
* Retrieves information about an active APEX package.
*
+ * <p>This method blocks caller thread until {@link #parseApexFiles()} succeeds. Note that in
+ * case {@link #parseApexFiles()}} throws an exception this method will never finish
+ * essentially putting device into a boot loop.
+ *
* @param packageName the package name to look for. Note that this is the package name reported
* in the APK container manifest (i.e. AndroidManifest.xml), which might
* differ from the one reported in the APEX manifest (i.e.
@@ -123,30 +155,43 @@
* is not found.
*/
@Nullable PackageInfo getActivePackage(String packageName) {
- populateActivePackagesCacheIfNeeded();
+ waitForLatch(mActivePackagesCacheLatch);
return mActivePackagesCache.get(packageName);
}
/**
* Retrieves information about all active APEX packages.
*
+ * <p>This method blocks caller thread until {@link #parseApexFiles()} succeeds. Note that in
+ * case {@link #parseApexFiles()}} throws an exception this method will never finish
+ * essentially putting device into a boot loop.
+ *
* @return a Collection of PackageInfo object, each one containing information about a different
* active package.
*/
Collection<PackageInfo> getActivePackages() {
- populateActivePackagesCacheIfNeeded();
+ waitForLatch(mActivePackagesCacheLatch);
return mActivePackagesCache.values();
}
/**
* Checks if {@code packageName} is an apex package.
*
+ * <p>This method blocks caller thread until {@link #populateApexFilesCache()} succeeds. Note
+ * that in case {@link #populateApexFilesCache()} throws an exception this method will never
+ * finish essentially putting device into a boot loop.
+ *
* @param packageName package to check.
* @return {@code true} if {@code packageName} is an apex package.
*/
boolean isApexPackage(String packageName) {
- populateActivePackagesCacheIfNeeded();
- return mActivePackagesCache.containsKey(packageName);
+ waitForLatch(mApexFilesCacheLatch);
+ for (ApexInfo ai : mApexFiles) {
+ if (ai.packageName.equals(packageName)) {
+ return true;
+ }
+ }
+ return false;
}
/**
@@ -274,6 +319,19 @@
}
/**
+ * Blocks current thread until {@code latch} has counted down to zero.
+ *
+ * @throws RuntimeException if thread was interrupted while waiting.
+ */
+ private void waitForLatch(CountDownLatch latch) {
+ try {
+ latch.await();
+ } catch (InterruptedException e) {
+ throw new RuntimeException("Interrupted waiting for cache to be populated", e);
+ }
+ }
+
+ /**
* Dumps various state information to the provided {@link PrintWriter} object.
*
* @param pw the {@link PrintWriter} object to send information to.
@@ -286,7 +344,7 @@
ipw.println("Active APEX packages:");
ipw.increaseIndent();
try {
- populateActivePackagesCacheIfNeeded();
+ waitForLatch(mActivePackagesCacheLatch);
for (PackageInfo pi : mActivePackagesCache.values()) {
if (packageName != null && !packageName.equals(pi.packageName)) {
continue;
@@ -331,8 +389,4 @@
ipw.println("Couldn't communicate with apexd.");
}
}
-
- public void onBootCompleted() {
- populateActivePackagesCacheIfNeeded();
- }
}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 74fb4b2..5f6e739 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -2050,6 +2050,13 @@
synchronized (mLock) {
assertCallerIsOwnerOrRootLocked();
+ if (isStagedAndInTerminalState()) {
+ // We keep the session in the database if it's in a finalized state. It will be
+ // removed by PackageInstallerService when the last update time is old enough.
+ // Also, in such cases cleanStageDir() has already been executed so no need to
+ // do it now.
+ return;
+ }
if (mCommitted && params.isStaged) {
synchronized (mLock) {
mDestroyed = true;
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index e08af6f..20d47ed 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -939,6 +939,7 @@
ComponentName mCustomResolverComponentName;
boolean mResolverReplaced = false;
+ boolean mOkToReplacePersistentPackages = false;
private final @Nullable ComponentName mIntentFilterVerifierComponent;
private final @Nullable IntentFilterVerifier<ActivityIntentInfo> mIntentFilterVerifier;
@@ -2374,6 +2375,8 @@
public PackageManagerService(Context context, Installer installer,
boolean factoryTest, boolean onlyCore) {
+ mApexManager = LocalServices.getService(ApexManager.class);
+
LockGuard.installLock(mPackages, LockGuard.INDEX_PACKAGES);
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "create package manager");
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_START,
@@ -2470,7 +2473,6 @@
mProtectedPackages = new ProtectedPackages(mContext);
- mApexManager = new ApexManager(context);
synchronized (mInstallLock) {
// writer
synchronized (mPackages) {
@@ -17324,7 +17326,8 @@
+ " target SDK " + oldTargetSdk + " does.");
}
// Prevent persistent apps from being updated
- if ((oldPackage.applicationInfo.flags & ApplicationInfo.FLAG_PERSISTENT) != 0) {
+ if (((oldPackage.applicationInfo.flags & ApplicationInfo.FLAG_PERSISTENT) != 0)
+ && !mOkToReplacePersistentPackages) {
throw new PrepareFailure(PackageManager.INSTALL_FAILED_INVALID_APK,
"Package " + oldPackage.packageName + " is a persistent app. "
+ "Persistent apps are not updateable.");
@@ -21462,7 +21465,6 @@
storage.registerListener(mStorageListener);
mInstallerService.systemReady();
- mApexManager.systemReady();
mPackageDexOptimizer.systemReady();
getStorageManagerInternal().addExternalStoragePolicy(
@@ -21505,10 +21507,12 @@
mModuleInfoProvider.systemReady();
+ mOkToReplacePersistentPackages = true;
// Installer service might attempt to install some packages that have been staged for
// installation on reboot. Make sure this is the last component to be call since the
// installation might require other components to be ready.
mInstallerService.restoreAndApplyStagedSessionIfNeeded();
+ mOkToReplacePersistentPackages = false;
}
public void waitForAppDataPrepared() {
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index a55ee5f..7bc9600 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -5810,10 +5810,8 @@
*/
Intent getSecondaryHomeIntent(String preferredPackage) {
final Intent intent = new Intent(mTopAction, mTopData != null ? Uri.parse(mTopData) : null);
- final boolean useSystemProvidedLauncher = mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_useSystemProvidedLauncherForSecondary);
- if (preferredPackage == null || useSystemProvidedLauncher) {
- // Using the component stored in config if no package name or forced.
+ if (preferredPackage == null) {
+ // Using the component stored in config if no package name.
final String secondaryHomeComponent = mContext.getResources().getString(
com.android.internal.R.string.config_secondaryHomeComponent);
intent.setComponent(ComponentName.unflattenFromString(secondaryHomeComponent));
diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index 7bc6776..4d37f1a 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -1860,13 +1860,14 @@
* Fail if the main interface fails to initialize
*/
if (gnssHal == nullptr) {
- ALOGE("Unable to Initialize GNSS HAL\n");
+ ALOGE("Unable to initialize GNSS HAL.");
return JNI_FALSE;
}
- sp<IGnssCallback> gnssCbIface = new GnssCallback();
-
Return<bool> result = false;
+
+ // Set top level IGnss.hal callback.
+ sp<IGnssCallback> gnssCbIface = new GnssCallback();
if (gnssHal_V2_0 != nullptr) {
result = gnssHal_V2_0->setCallback_2_0(gnssCbIface);
} else if (gnssHal_V1_1 != nullptr) {
@@ -1876,62 +1877,89 @@
}
if (!result.isOk() || !result) {
- ALOGE("SetCallback for Gnss Interface fails\n");
+ ALOGE("SetCallback for IGnss interface failed.");
return JNI_FALSE;
}
- sp<IGnssXtraCallback> gnssXtraCbIface = new GnssXtraCallback();
+ // Set IGnssXtra.hal callback.
if (gnssXtraIface == nullptr) {
- ALOGI("Unable to initialize GNSS Xtra interface\n");
+ ALOGI("Unable to initialize IGnssXtra interface.");
} else {
+ sp<IGnssXtraCallback> gnssXtraCbIface = new GnssXtraCallback();
result = gnssXtraIface->setCallback(gnssXtraCbIface);
if (!result.isOk() || !result) {
gnssXtraIface = nullptr;
- ALOGI("SetCallback for Gnss Xtra Interface fails\n");
+ ALOGI("SetCallback for IGnssXtra interface failed.");
}
}
+ // Set IAGnss.hal callback.
+ Return<void> agnssStatus;
if (agnssIface_V2_0 != nullptr) {
sp<IAGnssCallback_V2_0> aGnssCbIface = new AGnssCallback_V2_0();
- agnssIface_V2_0->setCallback(aGnssCbIface);
+ agnssStatus = agnssIface_V2_0->setCallback(aGnssCbIface);
} else if (agnssIface != nullptr) {
sp<IAGnssCallback_V1_0> aGnssCbIface = new AGnssCallback_V1_0();
- agnssIface->setCallback(aGnssCbIface);
+ agnssStatus = agnssIface->setCallback(aGnssCbIface);
} else {
- ALOGI("Unable to initialize AGnss interface\n");
+ ALOGI("Unable to initialize IAGnss interface.");
}
+ if (!agnssStatus.isOk()) {
+ ALOGI("SetCallback for IAGnss interface failed.");
+ }
+
+ // Set IGnssGeofencing.hal callback.
sp<IGnssGeofenceCallback> gnssGeofencingCbIface = new GnssGeofenceCallback();
if (gnssGeofencingIface != nullptr) {
- gnssGeofencingIface->setCallback(gnssGeofencingCbIface);
+ auto status = gnssGeofencingIface->setCallback(gnssGeofencingCbIface);
+ if (!status.isOk()) {
+ ALOGI("SetCallback for IGnssGeofencing interface failed.");
+ }
} else {
- ALOGI("Unable to initialize GNSS Geofencing interface\n");
+ ALOGI("Unable to initialize IGnssGeofencing interface.");
}
+ // Set IGnssNi.hal callback.
sp<IGnssNiCallback> gnssNiCbIface = new GnssNiCallback();
if (gnssNiIface != nullptr) {
- gnssNiIface->setCallback(gnssNiCbIface);
+ auto status = gnssNiIface->setCallback(gnssNiCbIface);
+ if (!status.isOk()) {
+ ALOGI("SetCallback for IGnssNi interface failed.");
+ }
} else {
- ALOGI("Unable to initialize GNSS NI interface\n");
+ ALOGI("Unable to initialize IGnssNi interface.");
}
+ // Set IAGnssRil.hal callback.
sp<IAGnssRilCallback> aGnssRilCbIface = new AGnssRilCallback();
if (agnssRilIface != nullptr) {
- agnssRilIface->setCallback(aGnssRilCbIface);
+ auto status = agnssRilIface->setCallback(aGnssRilCbIface);
+ if (!status.isOk()) {
+ ALOGI("SetCallback for IAGnssRil interface failed.");
+ }
} else {
- ALOGI("Unable to initialize AGnss Ril interface\n");
+ ALOGI("Unable to initialize IAGnssRil interface.");
}
+ // Set IGnssVisibilityControl.hal callback.
if (gnssVisibilityControlIface != nullptr) {
sp<IGnssVisibilityControlCallback> gnssVisibilityControlCbIface =
new GnssVisibilityControlCallback();
- gnssVisibilityControlIface->setCallback(gnssVisibilityControlCbIface);
+ result = gnssVisibilityControlIface->setCallback(gnssVisibilityControlCbIface);
+ if (!result.isOk() || !result) {
+ ALOGI("SetCallback for IGnssVisibilityControl interface failed.");
+ }
}
+ // Set IMeasurementCorrections.hal callback.
if (gnssCorrectionsIface != nullptr) {
sp<IMeasurementCorrectionsCallback> gnssCorrectionsIfaceCbIface =
new MeasurementCorrectionsCallback();
- gnssCorrectionsIface->setCallback(gnssCorrectionsIfaceCbIface);
+ result = gnssCorrectionsIface->setCallback(gnssCorrectionsIfaceCbIface);
+ if (!result.isOk() || !result) {
+ ALOGI("SetCallback for IMeasurementCorrections interface failed.");
+ }
}
return JNI_TRUE;
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index be7dd31..4ac8342 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -115,6 +115,7 @@
import com.android.server.os.BugreportManagerService;
import com.android.server.os.DeviceIdentifiersPolicyService;
import com.android.server.os.SchedulingPolicyService;
+import com.android.server.pm.ApexManager;
import com.android.server.pm.BackgroundDexOptService;
import com.android.server.pm.CrossProfileAppsService;
import com.android.server.pm.DynamicCodeLoggingService;
@@ -627,6 +628,12 @@
watchdog.start();
traceEnd();
+ // Start ApexManager as early as we can to give it enough time to call apexd and populate
+ // cache of known apex packages. Note that calling apexd will happen asynchronously.
+ traceBeginAndSlog("StartApexManager");
+ mSystemServiceManager.startService(ApexManager.class);
+ traceEnd();
+
Slog.i(TAG, "Reading configuration...");
final String TAG_SYSTEM_CONFIG = "ReadingSystemConfig";
traceBeginAndSlog(TAG_SYSTEM_CONFIG);
diff --git a/services/net/Android.bp b/services/net/Android.bp
index a44d835..ab11fe4 100644
--- a/services/net/Android.bp
+++ b/services/net/Android.bp
@@ -56,6 +56,7 @@
versions: [
"1",
"2",
+ "3",
],
}
diff --git a/services/net/aidl/networkstack/3/android/net/DhcpResultsParcelable.aidl b/services/net/aidl/networkstack/3/android/net/DhcpResultsParcelable.aidl
new file mode 100644
index 0000000..31891de
--- /dev/null
+++ b/services/net/aidl/networkstack/3/android/net/DhcpResultsParcelable.aidl
@@ -0,0 +1,9 @@
+package android.net;
+parcelable DhcpResultsParcelable {
+ android.net.StaticIpConfiguration baseConfiguration;
+ int leaseDuration;
+ int mtu;
+ String serverAddress;
+ String vendorInfo;
+ String serverHostName;
+}
diff --git a/services/net/aidl/networkstack/3/android/net/INetworkMonitor.aidl b/services/net/aidl/networkstack/3/android/net/INetworkMonitor.aidl
new file mode 100644
index 0000000..029968b
--- /dev/null
+++ b/services/net/aidl/networkstack/3/android/net/INetworkMonitor.aidl
@@ -0,0 +1,24 @@
+package android.net;
+interface INetworkMonitor {
+ oneway void start();
+ oneway void launchCaptivePortalApp();
+ oneway void notifyCaptivePortalAppFinished(int response);
+ oneway void setAcceptPartialConnectivity();
+ oneway void forceReevaluation(int uid);
+ oneway void notifyPrivateDnsChanged(in android.net.PrivateDnsConfigParcel config);
+ oneway void notifyDnsResponse(int returnCode);
+ oneway void notifyNetworkConnected(in android.net.LinkProperties lp, in android.net.NetworkCapabilities nc);
+ oneway void notifyNetworkDisconnected();
+ oneway void notifyLinkPropertiesChanged(in android.net.LinkProperties lp);
+ oneway void notifyNetworkCapabilitiesChanged(in android.net.NetworkCapabilities nc);
+ const int NETWORK_TEST_RESULT_VALID = 0;
+ const int NETWORK_TEST_RESULT_INVALID = 1;
+ const int NETWORK_TEST_RESULT_PARTIAL_CONNECTIVITY = 2;
+ const int NETWORK_VALIDATION_RESULT_VALID = 1;
+ const int NETWORK_VALIDATION_RESULT_PARTIAL = 2;
+ const int NETWORK_VALIDATION_PROBE_DNS = 4;
+ const int NETWORK_VALIDATION_PROBE_HTTP = 8;
+ const int NETWORK_VALIDATION_PROBE_HTTPS = 16;
+ const int NETWORK_VALIDATION_PROBE_FALLBACK = 32;
+ const int NETWORK_VALIDATION_PROBE_PRIVDNS = 64;
+}
diff --git a/services/net/aidl/networkstack/3/android/net/INetworkMonitorCallbacks.aidl b/services/net/aidl/networkstack/3/android/net/INetworkMonitorCallbacks.aidl
new file mode 100644
index 0000000..ee9871d
--- /dev/null
+++ b/services/net/aidl/networkstack/3/android/net/INetworkMonitorCallbacks.aidl
@@ -0,0 +1,8 @@
+package android.net;
+interface INetworkMonitorCallbacks {
+ oneway void onNetworkMonitorCreated(in android.net.INetworkMonitor networkMonitor);
+ oneway void notifyNetworkTested(int testResult, @nullable String redirectUrl);
+ oneway void notifyPrivateDnsConfigResolved(in android.net.PrivateDnsConfigParcel config);
+ oneway void showProvisioningNotification(String action, String packageName);
+ oneway void hideProvisioningNotification();
+}
diff --git a/services/net/aidl/networkstack/3/android/net/INetworkStackConnector.aidl b/services/net/aidl/networkstack/3/android/net/INetworkStackConnector.aidl
new file mode 100644
index 0000000..7da11e4
--- /dev/null
+++ b/services/net/aidl/networkstack/3/android/net/INetworkStackConnector.aidl
@@ -0,0 +1,7 @@
+package android.net;
+interface INetworkStackConnector {
+ oneway void makeDhcpServer(in String ifName, in android.net.dhcp.DhcpServingParamsParcel params, in android.net.dhcp.IDhcpServerCallbacks cb);
+ oneway void makeNetworkMonitor(in android.net.Network network, String name, in android.net.INetworkMonitorCallbacks cb);
+ oneway void makeIpClient(in String ifName, in android.net.ip.IIpClientCallbacks callbacks);
+ oneway void fetchIpMemoryStore(in android.net.IIpMemoryStoreCallbacks cb);
+}
diff --git a/services/net/aidl/networkstack/3/android/net/INetworkStackStatusCallback.aidl b/services/net/aidl/networkstack/3/android/net/INetworkStackStatusCallback.aidl
new file mode 100644
index 0000000..f6ca6f7
--- /dev/null
+++ b/services/net/aidl/networkstack/3/android/net/INetworkStackStatusCallback.aidl
@@ -0,0 +1,4 @@
+package android.net;
+interface INetworkStackStatusCallback {
+ oneway void onStatusAvailable(int statusCode);
+}
diff --git a/services/net/aidl/networkstack/3/android/net/InitialConfigurationParcelable.aidl b/services/net/aidl/networkstack/3/android/net/InitialConfigurationParcelable.aidl
new file mode 100644
index 0000000..c80a787
--- /dev/null
+++ b/services/net/aidl/networkstack/3/android/net/InitialConfigurationParcelable.aidl
@@ -0,0 +1,7 @@
+package android.net;
+parcelable InitialConfigurationParcelable {
+ android.net.LinkAddress[] ipAddresses;
+ android.net.IpPrefix[] directlyConnectedRoutes;
+ String[] dnsServers;
+ String gateway;
+}
diff --git a/services/net/aidl/networkstack/3/android/net/NattKeepalivePacketDataParcelable.aidl b/services/net/aidl/networkstack/3/android/net/NattKeepalivePacketDataParcelable.aidl
new file mode 100644
index 0000000..65de883
--- /dev/null
+++ b/services/net/aidl/networkstack/3/android/net/NattKeepalivePacketDataParcelable.aidl
@@ -0,0 +1,7 @@
+package android.net;
+parcelable NattKeepalivePacketDataParcelable {
+ byte[] srcAddress;
+ int srcPort;
+ byte[] dstAddress;
+ int dstPort;
+}
diff --git a/services/net/aidl/networkstack/3/android/net/PrivateDnsConfigParcel.aidl b/services/net/aidl/networkstack/3/android/net/PrivateDnsConfigParcel.aidl
new file mode 100644
index 0000000..2de790b
--- /dev/null
+++ b/services/net/aidl/networkstack/3/android/net/PrivateDnsConfigParcel.aidl
@@ -0,0 +1,5 @@
+package android.net;
+parcelable PrivateDnsConfigParcel {
+ String hostname;
+ String[] ips;
+}
diff --git a/services/net/aidl/networkstack/3/android/net/ProvisioningConfigurationParcelable.aidl b/services/net/aidl/networkstack/3/android/net/ProvisioningConfigurationParcelable.aidl
new file mode 100644
index 0000000..3a6c304
--- /dev/null
+++ b/services/net/aidl/networkstack/3/android/net/ProvisioningConfigurationParcelable.aidl
@@ -0,0 +1,15 @@
+package android.net;
+parcelable ProvisioningConfigurationParcelable {
+ boolean enableIPv4;
+ boolean enableIPv6;
+ boolean usingMultinetworkPolicyTracker;
+ boolean usingIpReachabilityMonitor;
+ int requestedPreDhcpActionMs;
+ android.net.InitialConfigurationParcelable initialConfig;
+ android.net.StaticIpConfiguration staticIpConfig;
+ android.net.apf.ApfCapabilities apfCapabilities;
+ int provisioningTimeoutMs;
+ int ipv6AddrGenMode;
+ android.net.Network network;
+ String displayName;
+}
diff --git a/services/net/aidl/networkstack/3/android/net/TcpKeepalivePacketDataParcelable.aidl b/services/net/aidl/networkstack/3/android/net/TcpKeepalivePacketDataParcelable.aidl
new file mode 100644
index 0000000..e121c06
--- /dev/null
+++ b/services/net/aidl/networkstack/3/android/net/TcpKeepalivePacketDataParcelable.aidl
@@ -0,0 +1,13 @@
+package android.net;
+parcelable TcpKeepalivePacketDataParcelable {
+ byte[] srcAddress;
+ int srcPort;
+ byte[] dstAddress;
+ int dstPort;
+ int seq;
+ int ack;
+ int rcvWnd;
+ int rcvWndScale;
+ int tos;
+ int ttl;
+}
diff --git a/services/net/aidl/networkstack/3/android/net/dhcp/DhcpServingParamsParcel.aidl b/services/net/aidl/networkstack/3/android/net/dhcp/DhcpServingParamsParcel.aidl
new file mode 100644
index 0000000..67193ae
--- /dev/null
+++ b/services/net/aidl/networkstack/3/android/net/dhcp/DhcpServingParamsParcel.aidl
@@ -0,0 +1,11 @@
+package android.net.dhcp;
+parcelable DhcpServingParamsParcel {
+ int serverAddr;
+ int serverAddrPrefixLength;
+ int[] defaultRouters;
+ int[] dnsServers;
+ int[] excludedAddrs;
+ long dhcpLeaseTimeSecs;
+ int linkMtu;
+ boolean metered;
+}
diff --git a/services/net/aidl/networkstack/3/android/net/dhcp/IDhcpServer.aidl b/services/net/aidl/networkstack/3/android/net/dhcp/IDhcpServer.aidl
new file mode 100644
index 0000000..9143158
--- /dev/null
+++ b/services/net/aidl/networkstack/3/android/net/dhcp/IDhcpServer.aidl
@@ -0,0 +1,10 @@
+package android.net.dhcp;
+interface IDhcpServer {
+ oneway void start(in android.net.INetworkStackStatusCallback cb);
+ oneway void updateParams(in android.net.dhcp.DhcpServingParamsParcel params, in android.net.INetworkStackStatusCallback cb);
+ oneway void stop(in android.net.INetworkStackStatusCallback cb);
+ const int STATUS_UNKNOWN = 0;
+ const int STATUS_SUCCESS = 1;
+ const int STATUS_INVALID_ARGUMENT = 2;
+ const int STATUS_UNKNOWN_ERROR = 3;
+}
diff --git a/services/net/aidl/networkstack/3/android/net/dhcp/IDhcpServerCallbacks.aidl b/services/net/aidl/networkstack/3/android/net/dhcp/IDhcpServerCallbacks.aidl
new file mode 100644
index 0000000..dcc4489
--- /dev/null
+++ b/services/net/aidl/networkstack/3/android/net/dhcp/IDhcpServerCallbacks.aidl
@@ -0,0 +1,4 @@
+package android.net.dhcp;
+interface IDhcpServerCallbacks {
+ oneway void onDhcpServerCreated(int statusCode, in android.net.dhcp.IDhcpServer server);
+}
diff --git a/services/net/aidl/networkstack/3/android/net/ip/IIpClient.aidl b/services/net/aidl/networkstack/3/android/net/ip/IIpClient.aidl
new file mode 100644
index 0000000..176a5ce
--- /dev/null
+++ b/services/net/aidl/networkstack/3/android/net/ip/IIpClient.aidl
@@ -0,0 +1,16 @@
+package android.net.ip;
+interface IIpClient {
+ oneway void completedPreDhcpAction();
+ oneway void confirmConfiguration();
+ oneway void readPacketFilterComplete(in byte[] data);
+ oneway void shutdown();
+ oneway void startProvisioning(in android.net.ProvisioningConfigurationParcelable req);
+ oneway void stop();
+ oneway void setTcpBufferSizes(in String tcpBufferSizes);
+ oneway void setHttpProxy(in android.net.ProxyInfo proxyInfo);
+ oneway void setMulticastFilter(boolean enabled);
+ oneway void addKeepalivePacketFilter(int slot, in android.net.TcpKeepalivePacketDataParcelable pkt);
+ oneway void removeKeepalivePacketFilter(int slot);
+ oneway void setL2KeyAndGroupHint(in String l2Key, in String groupHint);
+ oneway void addNattKeepalivePacketFilter(int slot, in android.net.NattKeepalivePacketDataParcelable pkt);
+}
diff --git a/services/net/aidl/networkstack/3/android/net/ip/IIpClientCallbacks.aidl b/services/net/aidl/networkstack/3/android/net/ip/IIpClientCallbacks.aidl
new file mode 100644
index 0000000..d6bc808
--- /dev/null
+++ b/services/net/aidl/networkstack/3/android/net/ip/IIpClientCallbacks.aidl
@@ -0,0 +1,16 @@
+package android.net.ip;
+interface IIpClientCallbacks {
+ oneway void onIpClientCreated(in android.net.ip.IIpClient ipClient);
+ oneway void onPreDhcpAction();
+ oneway void onPostDhcpAction();
+ oneway void onNewDhcpResults(in android.net.DhcpResultsParcelable dhcpResults);
+ oneway void onProvisioningSuccess(in android.net.LinkProperties newLp);
+ oneway void onProvisioningFailure(in android.net.LinkProperties newLp);
+ oneway void onLinkPropertiesChange(in android.net.LinkProperties newLp);
+ oneway void onReachabilityLost(in String logMsg);
+ oneway void onQuit();
+ oneway void installPacketFilter(in byte[] filter);
+ oneway void startReadPacketFilter();
+ oneway void setFallbackMulticastFilter(boolean enabled);
+ oneway void setNeighborDiscoveryOffload(boolean enable);
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
new file mode 100644
index 0000000..f7edf65
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
@@ -0,0 +1,437 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.job;
+
+import static android.text.format.DateUtils.HOUR_IN_MILLIS;
+import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.when;
+
+import android.app.ActivityManager;
+import android.app.ActivityManagerInternal;
+import android.app.IActivityManager;
+import android.app.job.JobInfo;
+import android.app.usage.UsageStatsManagerInternal;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
+import android.content.res.Resources;
+import android.net.ConnectivityManager;
+import android.net.NetworkPolicyManager;
+import android.os.BatteryManagerInternal;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.os.SystemClock;
+
+import com.android.server.AppStateTracker;
+import com.android.server.DeviceIdleController;
+import com.android.server.LocalServices;
+import com.android.server.job.controllers.JobStatus;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoSession;
+import org.mockito.quality.Strictness;
+
+import java.time.Clock;
+import java.time.Duration;
+import java.time.ZoneOffset;
+
+public class JobSchedulerServiceTest {
+ private JobSchedulerService mService;
+
+ private MockitoSession mMockingSession;
+ @Mock
+ private ActivityManagerInternal mActivityMangerInternal;
+ @Mock
+ private Context mContext;
+
+ private class TestJobSchedulerService extends JobSchedulerService {
+ TestJobSchedulerService(Context context) {
+ super(context);
+ }
+
+ @Override
+ public boolean isChainedAttributionEnabled() {
+ return false;
+ }
+ }
+
+ @Before
+ public void setUp() {
+ mMockingSession = mockitoSession()
+ .initMocks(this)
+ .strictness(Strictness.LENIENT)
+ .mockStatic(LocalServices.class)
+ .startMocking();
+
+ // Called in JobSchedulerService constructor.
+ when(mContext.getMainLooper()).thenReturn(Looper.getMainLooper());
+ doReturn(mActivityMangerInternal)
+ .when(() -> LocalServices.getService(ActivityManagerInternal.class));
+ doReturn(mock(UsageStatsManagerInternal.class))
+ .when(() -> LocalServices.getService(UsageStatsManagerInternal.class));
+ // Called in BackgroundJobsController constructor.
+ doReturn(mock(AppStateTracker.class))
+ .when(() -> LocalServices.getService(AppStateTracker.class));
+ // Called in BatteryController constructor.
+ doReturn(mock(BatteryManagerInternal.class))
+ .when(() -> LocalServices.getService(BatteryManagerInternal.class));
+ // Called in ConnectivityController constructor.
+ when(mContext.getSystemService(ConnectivityManager.class))
+ .thenReturn(mock(ConnectivityManager.class));
+ when(mContext.getSystemService(NetworkPolicyManager.class))
+ .thenReturn(mock(NetworkPolicyManager.class));
+ // Called in DeviceIdleJobsController constructor.
+ doReturn(mock(DeviceIdleController.LocalService.class))
+ .when(() -> LocalServices.getService(DeviceIdleController.LocalService.class));
+ // Used in JobStatus.
+ doReturn(mock(PackageManagerInternal.class))
+ .when(() -> LocalServices.getService(PackageManagerInternal.class));
+ // Called via IdleController constructor.
+ when(mContext.getPackageManager()).thenReturn(mock(PackageManager.class));
+ when(mContext.getResources()).thenReturn(mock(Resources.class));
+ // Called in QuotaController constructor.
+ IActivityManager activityManager = ActivityManager.getService();
+ spyOn(activityManager);
+ try {
+ doNothing().when(activityManager).registerUidObserver(any(), anyInt(), anyInt(), any());
+ } catch (RemoteException e) {
+ fail("registerUidObserver threw exception: " + e.getMessage());
+ }
+
+ JobSchedulerService.sSystemClock = Clock.fixed(Clock.systemUTC().instant(), ZoneOffset.UTC);
+ JobSchedulerService.sElapsedRealtimeClock =
+ Clock.fixed(SystemClock.elapsedRealtimeClock().instant(), ZoneOffset.UTC);
+
+ mService = new TestJobSchedulerService(mContext);
+ }
+
+ @After
+ public void tearDown() {
+ if (mMockingSession != null) {
+ mMockingSession.finishMocking();
+ }
+ }
+
+ private Clock getAdvancedClock(Clock clock, long incrementMs) {
+ return Clock.offset(clock, Duration.ofMillis(incrementMs));
+ }
+
+ private void advanceElapsedClock(long incrementMs) {
+ JobSchedulerService.sElapsedRealtimeClock = getAdvancedClock(
+ JobSchedulerService.sElapsedRealtimeClock, incrementMs);
+ }
+
+ private static JobInfo.Builder createJobInfo() {
+ return new JobInfo.Builder(351, new ComponentName("foo", "bar"));
+ }
+
+ private JobStatus createJobStatus(String testTag, JobInfo.Builder jobInfoBuilder) {
+ return JobStatus.createFromJobInfo(
+ jobInfoBuilder.build(), 1234, "com.android.test", 0, testTag);
+ }
+
+ /**
+ * Confirm that {@link JobSchedulerService#getRescheduleJobForPeriodic(JobStatus)} returns a job
+ * with the correct delay and deadline constraints if the periodic job is completed and
+ * rescheduled while run in its expected running window.
+ */
+ @Test
+ public void testGetRescheduleJobForPeriodic_insideWindow() {
+ final long now = sElapsedRealtimeClock.millis();
+ JobStatus job = createJobStatus("testGetRescheduleJobForPeriodic_insideWindow",
+ createJobInfo().setPeriodic(HOUR_IN_MILLIS));
+ final long nextWindowStartTime = now + HOUR_IN_MILLIS;
+ final long nextWindowEndTime = now + 2 * HOUR_IN_MILLIS;
+
+ JobStatus rescheduledJob = mService.getRescheduleJobForPeriodic(job);
+ assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime());
+ assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
+
+ advanceElapsedClock(10 * MINUTE_IN_MILLIS); // now + 10 minutes
+
+ rescheduledJob = mService.getRescheduleJobForPeriodic(job);
+ assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime());
+ assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
+
+ advanceElapsedClock(45 * MINUTE_IN_MILLIS); // now + 55 minutes
+
+ rescheduledJob = mService.getRescheduleJobForPeriodic(job);
+ assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime());
+ assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
+
+ advanceElapsedClock(4 * MINUTE_IN_MILLIS); // now + 59 minutes
+
+ rescheduledJob = mService.getRescheduleJobForPeriodic(job);
+ assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime());
+ assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
+ }
+
+ /**
+ * Confirm that {@link JobSchedulerService#getRescheduleJobForPeriodic(JobStatus)} returns a job
+ * with the correct delay and deadline constraints if the periodic job with a custom flex
+ * setting is completed and rescheduled while run in its expected running window.
+ */
+ @Test
+ public void testGetRescheduleJobForPeriodic_insideWindow_flex() {
+ JobStatus job = createJobStatus("testGetRescheduleJobForPeriodic_insideWindow_flex",
+ createJobInfo().setPeriodic(HOUR_IN_MILLIS, 30 * MINUTE_IN_MILLIS));
+ // First window starts 30 minutes from now.
+ advanceElapsedClock(30 * MINUTE_IN_MILLIS);
+ final long now = sElapsedRealtimeClock.millis();
+ final long nextWindowStartTime = now + HOUR_IN_MILLIS;
+ final long nextWindowEndTime = now + 90 * MINUTE_IN_MILLIS;
+
+ JobStatus rescheduledJob = mService.getRescheduleJobForPeriodic(job);
+ assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime());
+ assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
+
+ advanceElapsedClock(10 * MINUTE_IN_MILLIS); // now + 10 minutes
+
+ rescheduledJob = mService.getRescheduleJobForPeriodic(job);
+ assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime());
+ assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
+
+ advanceElapsedClock(15 * MINUTE_IN_MILLIS); // now + 25 minutes
+
+ rescheduledJob = mService.getRescheduleJobForPeriodic(job);
+ assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime());
+ assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
+
+ advanceElapsedClock(4 * MINUTE_IN_MILLIS); // now + 29 minutes
+
+ rescheduledJob = mService.getRescheduleJobForPeriodic(job);
+ assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime());
+ assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
+ }
+
+ /**
+ * Confirm that {@link JobSchedulerService#getRescheduleJobForPeriodic(JobStatus)} returns a job
+ * with the correct delay and deadline constraints if the periodic job failed but then ran
+ * successfully and was rescheduled while run in its expected running window.
+ */
+ @Test
+ public void testGetRescheduleJobForPeriodic_insideWindow_failedJob() {
+ final long now = sElapsedRealtimeClock.millis();
+ final long nextWindowStartTime = now + HOUR_IN_MILLIS;
+ final long nextWindowEndTime = now + 2 * HOUR_IN_MILLIS;
+ JobStatus job = createJobStatus("testGetRescheduleJobForPeriodic_insideWindow_failedJob",
+ createJobInfo().setPeriodic(HOUR_IN_MILLIS));
+ JobStatus failedJob = mService.getRescheduleJobForFailureLocked(job);
+
+ JobStatus rescheduledJob = mService.getRescheduleJobForPeriodic(failedJob);
+ assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime());
+ assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
+
+ advanceElapsedClock(5 * MINUTE_IN_MILLIS); // now + 5 minutes
+ failedJob = mService.getRescheduleJobForFailureLocked(job);
+ advanceElapsedClock(5 * MINUTE_IN_MILLIS); // now + 10 minutes
+
+ rescheduledJob = mService.getRescheduleJobForPeriodic(failedJob);
+ assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime());
+ assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
+
+ advanceElapsedClock(35 * MINUTE_IN_MILLIS); // now + 45 minutes
+ failedJob = mService.getRescheduleJobForFailureLocked(job);
+ advanceElapsedClock(10 * MINUTE_IN_MILLIS); // now + 55 minutes
+
+ rescheduledJob = mService.getRescheduleJobForPeriodic(failedJob);
+ assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime());
+ assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
+
+ advanceElapsedClock(2 * MINUTE_IN_MILLIS); // now + 57 minutes
+ failedJob = mService.getRescheduleJobForFailureLocked(job);
+ advanceElapsedClock(2 * MINUTE_IN_MILLIS); // now + 59 minutes
+
+ rescheduledJob = mService.getRescheduleJobForPeriodic(failedJob);
+ assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime());
+ assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
+ }
+
+ /**
+ * Confirm that {@link JobSchedulerService#getRescheduleJobForPeriodic(JobStatus)} returns a job
+ * with the correct delay and deadline constraints if the periodic job is completed and
+ * rescheduled when run after its expected running window.
+ */
+ @Test
+ public void testGetRescheduleJobForPeriodic_outsideWindow() {
+ JobStatus job = createJobStatus("testGetRescheduleJobForPeriodic_outsideWindow",
+ createJobInfo().setPeriodic(HOUR_IN_MILLIS));
+ long now = sElapsedRealtimeClock.millis();
+ long nextWindowStartTime = now + HOUR_IN_MILLIS;
+ long nextWindowEndTime = now + 2 * HOUR_IN_MILLIS;
+
+ advanceElapsedClock(HOUR_IN_MILLIS + MINUTE_IN_MILLIS);
+ // Say the job ran at the very end of its previous window. The intended JSS behavior is to
+ // have consistent windows, so the new window should start as soon as the previous window
+ // ended and end PERIOD time after the previous window ended.
+ JobStatus rescheduledJob = mService.getRescheduleJobForPeriodic(job);
+ assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime());
+ assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
+
+ advanceElapsedClock(2 * HOUR_IN_MILLIS);
+ // Say that the job ran at this point, possibly due to device idle.
+ // The next window should be consistent (start and end at the time it would have had the job
+ // run normally in previous windows).
+ nextWindowStartTime += 2 * HOUR_IN_MILLIS;
+ nextWindowEndTime += 2 * HOUR_IN_MILLIS;
+
+ rescheduledJob = mService.getRescheduleJobForPeriodic(job);
+ assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime());
+ assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
+ }
+
+ /**
+ * Confirm that {@link JobSchedulerService#getRescheduleJobForPeriodic(JobStatus)} returns a job
+ * with the correct delay and deadline constraints if the periodic job with a custom flex
+ * setting is completed and rescheduled when run after its expected running window.
+ */
+ @Test
+ public void testGetRescheduleJobForPeriodic_outsideWindow_flex() {
+ JobStatus job = createJobStatus("testGetRescheduleJobForPeriodic_outsideWindow_flex",
+ createJobInfo().setPeriodic(HOUR_IN_MILLIS, 30 * MINUTE_IN_MILLIS));
+ // First window starts 30 minutes from now.
+ advanceElapsedClock(30 * MINUTE_IN_MILLIS);
+ long now = sElapsedRealtimeClock.millis();
+ long nextWindowStartTime = now + HOUR_IN_MILLIS;
+ long nextWindowEndTime = now + 90 * MINUTE_IN_MILLIS;
+
+ advanceElapsedClock(31 * MINUTE_IN_MILLIS);
+ // Say the job ran at the very end of its previous window. The intended JSS behavior is to
+ // have consistent windows, so the new window should start as soon as the previous window
+ // ended and end PERIOD time after the previous window ended.
+ JobStatus rescheduledJob = mService.getRescheduleJobForPeriodic(job);
+ assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime());
+ assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
+
+ // 5 minutes before the start of the next window. It's too close to the next window, so the
+ // returned job should be for the window after.
+ advanceElapsedClock(24 * MINUTE_IN_MILLIS);
+ nextWindowStartTime += HOUR_IN_MILLIS;
+ nextWindowEndTime += HOUR_IN_MILLIS;
+ rescheduledJob = mService.getRescheduleJobForPeriodic(job);
+ assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime());
+ assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
+
+ advanceElapsedClock(2 * HOUR_IN_MILLIS + 10 * MINUTE_IN_MILLIS);
+ // Say that the job ran at this point, possibly due to device idle.
+ // The next window should be consistent (start and end at the time it would have had the job
+ // run normally in previous windows).
+ nextWindowStartTime += 2 * HOUR_IN_MILLIS;
+ nextWindowEndTime += 2 * HOUR_IN_MILLIS;
+
+ rescheduledJob = mService.getRescheduleJobForPeriodic(job);
+ assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime());
+ assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
+ }
+
+ /**
+ * Confirm that {@link JobSchedulerService#getRescheduleJobForPeriodic(JobStatus)} returns a job
+ * with the correct delay and deadline constraints if the periodic job failed but then ran
+ * successfully and was rescheduled when run after its expected running window.
+ */
+ @Test
+ public void testGetRescheduleJobForPeriodic_outsideWindow_failedJob() {
+ JobStatus job = createJobStatus("testGetRescheduleJobForPeriodic_outsideWindow_failedJob",
+ createJobInfo().setPeriodic(HOUR_IN_MILLIS));
+ JobStatus failedJob = mService.getRescheduleJobForFailureLocked(job);
+ long now = sElapsedRealtimeClock.millis();
+ long nextWindowStartTime = now + HOUR_IN_MILLIS;
+ long nextWindowEndTime = now + 2 * HOUR_IN_MILLIS;
+
+ advanceElapsedClock(HOUR_IN_MILLIS + MINUTE_IN_MILLIS);
+ // Say the job ran at the very end of its previous window. The intended JSS behavior is to
+ // have consistent windows, so the new window should start as soon as the previous window
+ // ended and end PERIOD time after the previous window ended.
+ JobStatus rescheduledJob = mService.getRescheduleJobForPeriodic(failedJob);
+ assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime());
+ assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
+
+ advanceElapsedClock(2 * HOUR_IN_MILLIS);
+ // Say that the job ran at this point, possibly due to device idle.
+ // The next window should be consistent (start and end at the time it would have had the job
+ // run normally in previous windows).
+ nextWindowStartTime += 2 * HOUR_IN_MILLIS;
+ nextWindowEndTime += 2 * HOUR_IN_MILLIS;
+
+ rescheduledJob = mService.getRescheduleJobForPeriodic(failedJob);
+ assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime());
+ assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
+ }
+
+ /**
+ * Confirm that {@link JobSchedulerService#getRescheduleJobForPeriodic(JobStatus)} returns a job
+ * with the correct delay and deadline constraints if the periodic job with a custom flex
+ * setting failed but then ran successfully and was rescheduled when run after its expected
+ * running window.
+ */
+ @Test
+ public void testGetRescheduleJobForPeriodic_outsideWindow_flex_failedJob() {
+ JobStatus job = createJobStatus(
+ "testGetRescheduleJobForPeriodic_outsideWindow_flex_failedJob",
+ createJobInfo().setPeriodic(HOUR_IN_MILLIS, 30 * MINUTE_IN_MILLIS));
+ JobStatus failedJob = mService.getRescheduleJobForFailureLocked(job);
+ // First window starts 30 minutes from now.
+ advanceElapsedClock(30 * MINUTE_IN_MILLIS);
+ long now = sElapsedRealtimeClock.millis();
+ long nextWindowStartTime = now + HOUR_IN_MILLIS;
+ long nextWindowEndTime = now + 90 * MINUTE_IN_MILLIS;
+
+ advanceElapsedClock(31 * MINUTE_IN_MILLIS);
+ // Say the job ran at the very end of its previous window. The intended JSS behavior is to
+ // have consistent windows, so the new window should start as soon as the previous window
+ // ended and end PERIOD time after the previous window ended.
+ JobStatus rescheduledJob = mService.getRescheduleJobForPeriodic(failedJob);
+ assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime());
+ assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
+
+ // 5 minutes before the start of the next window. It's too close to the next window, so the
+ // returned job should be for the window after.
+ advanceElapsedClock(24 * MINUTE_IN_MILLIS);
+ nextWindowStartTime += HOUR_IN_MILLIS;
+ nextWindowEndTime += HOUR_IN_MILLIS;
+ rescheduledJob = mService.getRescheduleJobForPeriodic(job);
+ assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime());
+ assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
+
+ advanceElapsedClock(2 * HOUR_IN_MILLIS);
+ // Say that the job ran at this point, possibly due to device idle.
+ // The next window should be consistent (start and end at the time it would have had the job
+ // run normally in previous windows).
+ nextWindowStartTime += 2 * HOUR_IN_MILLIS;
+ nextWindowEndTime += 2 * HOUR_IN_MILLIS;
+
+ rescheduledJob = mService.getRescheduleJobForPeriodic(failedJob);
+ assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime());
+ assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
index 99b827c..bdc46ec 100644
--- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
@@ -263,6 +263,7 @@
private static final int INVALID_CARRIER_CONFIG_VALUE = -9999;
private long mDefaultWarningBytes; // filled in with the actual default before tests are run
private long mDefaultLimitBytes; // filled in with the actual default before tests are run
+ private PersistableBundle mCarrierConfig = CarrierConfigManager.getDefaultConfig();
private static final int APP_ID_A = android.os.Process.FIRST_APPLICATION_UID + 4;
private static final int APP_ID_B = android.os.Process.FIRST_APPLICATION_UID + 8;
@@ -409,6 +410,9 @@
doNothing().when(mConnectivityManager)
.registerNetworkCallback(any(), mNetworkCallbackCaptor.capture());
+ // Create the expected carrier config
+ mCarrierConfig.putBoolean(CarrierConfigManager.KEY_CARRIER_CONFIG_APPLIED_BOOL, true);
+
// Prepare NPMS.
mService.systemReady(mService.networkScoreAndNetworkManagementServiceReady());
@@ -1086,6 +1090,25 @@
isA(Notification.class), eq(UserHandle.ALL));
}
+ // Push over warning, but with a config that isn't from an identified carrier
+ {
+ history.clear();
+ history.recordData(start, end,
+ new NetworkStats.Entry(DataUnit.MEGABYTES.toBytes(1799), 0L, 0L, 0L, 0));
+
+ reset(mTelephonyManager, mNetworkManager, mNotifManager);
+ expectMobileDefaults();
+ expectDefaultCarrierConfig();
+
+ mService.updateNetworks();
+
+ verify(mTelephonyManager, atLeastOnce()).setPolicyDataEnabled(true, TEST_SUB_ID);
+ verify(mNetworkManager, atLeastOnce()).setInterfaceQuota(TEST_IFACE,
+ DataUnit.MEGABYTES.toBytes(1800 - 1799));
+ // Since this isn't from the identified carrier, there should be no notifications
+ verify(mNotifManager, never()).notifyAsUser(any(), anyInt(), any(), any());
+ }
+
// Push over limit
{
history.clear();
@@ -1812,7 +1835,7 @@
private void expectNetworkState(boolean roaming) throws Exception {
when(mCarrierConfigManager.getConfigForSubId(eq(TEST_SUB_ID)))
- .thenReturn(CarrierConfigManager.getDefaultConfig());
+ .thenReturn(mCarrierConfig);
when(mConnManager.getAllNetworkState()).thenReturn(new NetworkState[] {
new NetworkState(buildNetworkInfo(),
buildLinkProperties(TEST_IFACE),
@@ -1821,10 +1844,16 @@
});
}
+ private void expectDefaultCarrierConfig() throws Exception {
+ when(mCarrierConfigManager.getConfigForSubId(eq(TEST_SUB_ID)))
+ .thenReturn(CarrierConfigManager.getDefaultConfig());
+ }
+
private void expectMobileDefaults() throws Exception {
when(mSubscriptionManager.getActiveSubscriptionIdList()).thenReturn(
new int[] { TEST_SUB_ID });
when(mTelephonyManager.getSubscriberId(TEST_SUB_ID)).thenReturn(TEST_IMSI);
+ doNothing().when(mTelephonyManager).setPolicyDataEnabled(anyBoolean(), anyInt());
expectNetworkState(false /* roaming */);
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/AnimatingAppWindowTokenRegistryTest.java b/services/tests/wmtests/src/com/android/server/wm/AnimatingAppWindowTokenRegistryTest.java
index 380f7c6..5cd649f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AnimatingAppWindowTokenRegistryTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AnimatingAppWindowTokenRegistryTest.java
@@ -27,6 +27,7 @@
import android.platform.test.annotations.Presubmit;
+import androidx.test.filters.FlakyTest;
import androidx.test.filters.SmallTest;
import org.junit.Before;
@@ -80,6 +81,7 @@
}
@Test
+ @FlakyTest(bugId = 131005232)
public void testContainerRemoved() {
final AppWindowToken window1 = createAppWindowToken(mDisplayContent,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java
index 83c0af9..777e4f4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java
@@ -38,6 +38,7 @@
import android.view.RemoteAnimationDefinition;
import android.view.RemoteAnimationTarget;
+import androidx.test.filters.FlakyTest;
import androidx.test.filters.SmallTest;
import org.junit.Test;
@@ -96,6 +97,7 @@
}
@Test
+ @FlakyTest(bugId = 131005232)
public void testModeChangeRemoteAnimatorNoSnapshot() {
// setup currently defaults to no snapshot.
setUpOnDisplay(mDisplayContent);
@@ -113,6 +115,7 @@
}
@Test
+ @FlakyTest(bugId = 131005232)
public void testCancelPendingChangeOnRemove() {
// setup currently defaults to no snapshot.
setUpOnDisplay(mDisplayContent);
@@ -132,6 +135,7 @@
}
@Test
+ @FlakyTest(bugId = 131005232)
public void testNoChangeWhenMoveDisplay() {
mDisplayContent.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
final DisplayContent dc1 = createNewDisplay(Display.STATE_ON);
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
index b1ffbbd..2d1dd39 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
@@ -31,6 +31,7 @@
import android.platform.test.annotations.Presubmit;
import android.view.WindowManager;
+import androidx.test.filters.FlakyTest;
import androidx.test.filters.SmallTest;
import org.junit.Before;
@@ -52,6 +53,7 @@
}
@Test
+ @FlakyTest(bugId = 131005232)
public void testTranslucentOpen() {
synchronized (mWm.mGlobalLock) {
final AppWindowToken behind = createAppWindowToken(mDisplayContent,
@@ -69,6 +71,7 @@
}
@Test
+ @FlakyTest(bugId = 131005232)
public void testTranslucentClose() {
synchronized (mWm.mGlobalLock) {
final AppWindowToken behind = createAppWindowToken(mDisplayContent,
@@ -84,6 +87,7 @@
}
@Test
+ @FlakyTest(bugId = 131005232)
public void testChangeIsNotOverwritten() {
synchronized (mWm.mGlobalLock) {
final AppWindowToken behind = createAppWindowToken(mDisplayContent,
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
index 6e09167..9cdea9a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
@@ -39,6 +39,7 @@
import org.junit.Before;
import org.junit.Test;
+import androidx.test.filters.FlakyTest;
import androidx.test.filters.SmallTest;
/**
@@ -78,6 +79,7 @@
}
@Test
+ @FlakyTest(bugId = 131005232)
public void testForceOverride() {
mWm.prepareAppTransition(TRANSIT_KEYGUARD_UNOCCLUDE, false /* alwaysKeepCurrent */);
mDc.prepareAppTransition(TRANSIT_ACTIVITY_OPEN,
@@ -93,6 +95,7 @@
}
@Test
+ @FlakyTest(bugId = 131005232)
public void testKeepKeyguard_withCrashing() {
mWm.prepareAppTransition(TRANSIT_KEYGUARD_GOING_AWAY, false /* alwaysKeepCurrent */);
mWm.prepareAppTransition(TRANSIT_CRASHING_ACTIVITY_CLOSE, false /* alwaysKeepCurrent */);
@@ -100,6 +103,7 @@
}
@Test
+ @FlakyTest(bugId = 131005232)
public void testAppTransitionStateForMultiDisplay() {
// Create 2 displays & presume both display the state is ON for ready to display & animate.
final DisplayContent dc1 = createNewDisplay(Display.STATE_ON);
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppWindowThumbnailTest.java b/services/tests/wmtests/src/com/android/server/wm/AppWindowThumbnailTest.java
index fa0c384..b8f8e21 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppWindowThumbnailTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppWindowThumbnailTest.java
@@ -28,6 +28,7 @@
import android.view.Surface;
import android.view.SurfaceControl;
+import androidx.test.filters.FlakyTest;
import androidx.test.filters.SmallTest;
import org.junit.Test;
@@ -53,6 +54,7 @@
}
@Test
+ @FlakyTest(bugId = 131005232)
public void testDestroy_nullsSurface() {
final AppWindowThumbnail t = buildThumbnail();
assertNotNull(t.getSurfaceControl());
diff --git a/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
index 196cc21..0110e94 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
@@ -39,6 +39,7 @@
import android.view.SurfaceSession;
import android.view.View;
+import androidx.test.filters.FlakyTest;
import androidx.test.filters.SmallTest;
import com.android.server.LocalServices;
@@ -146,16 +147,19 @@
}
@Test
+ @FlakyTest(bugId = 131005232)
public void testDragFlow() {
dragFlow(0, ClipData.newPlainText("label", "Test"), 0, 0);
}
@Test
+ @FlakyTest(bugId = 131005232)
public void testPerformDrag_NullDataWithGrantUri() {
dragFlow(View.DRAG_FLAG_GLOBAL | View.DRAG_FLAG_GLOBAL_URI_READ, null, 0, 0);
}
@Test
+ @FlakyTest(bugId = 131005232)
public void testPerformDrag_NullDataToOtherUser() {
final WindowState otherUsersWindow =
createDropTargetWindow("Other user's window", 1 * UserHandle.PER_USER_RANGE);
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java
index a0546d7..86ee75e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java
@@ -31,6 +31,7 @@
import android.view.InsetsSource;
import android.view.InsetsState;
+import androidx.test.filters.FlakyTest;
import androidx.test.filters.SmallTest;
import org.junit.Before;
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
index 2d906d1..9fce78b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
@@ -58,6 +58,7 @@
}
@Test
+ @FlakyTest(bugId = 131005232)
public void testStripForDispatch_notOwn() {
final WindowState topBar = createWindow(null, TYPE_APPLICATION, "parentWindow");
final WindowState app = createWindow(null, TYPE_APPLICATION, "parentWindow");
diff --git a/services/tests/wmtests/src/com/android/server/wm/PendingRemoteAnimationRegistryTest.java b/services/tests/wmtests/src/com/android/server/wm/PendingRemoteAnimationRegistryTest.java
index b769fce..103c3ab 100644
--- a/services/tests/wmtests/src/com/android/server/wm/PendingRemoteAnimationRegistryTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/PendingRemoteAnimationRegistryTest.java
@@ -24,6 +24,7 @@
import android.platform.test.annotations.Presubmit;
import android.view.RemoteAnimationAdapter;
+import androidx.test.filters.FlakyTest;
import androidx.test.filters.SmallTest;
import com.android.server.testutils.OffsettableClock;
@@ -73,6 +74,7 @@
}
@Test
+ @FlakyTest(bugId = 131005232)
public void testTimeout() {
mRegistry.addPendingAnimation("com.android.test", mAdapter);
mClock.fastForward(5000);
diff --git a/services/tests/wmtests/src/com/android/server/wm/PersisterQueueTests.java b/services/tests/wmtests/src/com/android/server/wm/PersisterQueueTests.java
index 4673992..027f903 100644
--- a/services/tests/wmtests/src/com/android/server/wm/PersisterQueueTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/PersisterQueueTests.java
@@ -110,6 +110,7 @@
}
@Test
+ @FlakyTest(bugId = 131005232)
public void testProcessOneItem_Flush() throws Exception {
mFactory.setExpectedProcessedItemNumber(1);
mListener.setExpectedOnPreProcessItemCallbackTimes(1);
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
index 6d27e6d..bac1ecd 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
@@ -31,7 +31,6 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.reset;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.server.wm.ActivityDisplay.POSITION_TOP;
@@ -56,7 +55,6 @@
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.ResolveInfo;
-import android.content.res.Resources;
import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
import android.util.Pair;
@@ -539,71 +537,6 @@
}
/**
- * Tests that the default secondary home activity is always picked when it is in forced by
- * config_useSystemProvidedLauncherForSecondary.
- */
- @Test
- public void testResolveSecondaryHomeActivityForced() {
- Resources resources = mContext.getResources();
- spyOn(resources);
- final String defaultSecondaryHome =
- "com.android.test/com.android.test.TestDefaultSecondaryHome";
- final ComponentName secondaryComp = ComponentName.unflattenFromString(defaultSecondaryHome);
- doReturn(defaultSecondaryHome).when(resources).getString(
- com.android.internal.R.string.config_secondaryHomeComponent);
- doReturn(true).when(resources).getBoolean(
- com.android.internal.R.bool.config_useSystemProvidedLauncherForSecondary);
-
- final Intent secondaryHomeIntent = mService.getSecondaryHomeIntent(null);
- assertEquals(secondaryComp, secondaryHomeIntent.getComponent());
-
- final ActivityInfo aInfoSecondary = new ActivityInfo();
- aInfoSecondary.name = secondaryComp.getClassName();
- aInfoSecondary.applicationInfo = new ApplicationInfo();
- aInfoSecondary.applicationInfo.packageName = secondaryComp.getPackageName();
- doReturn(aInfoSecondary).when(mRootActivityContainer).resolveHomeActivity(anyInt(),
- refEq(secondaryHomeIntent));
-
- final Intent homeIntent = mService.getHomeIntent();
- final ActivityInfo aInfoDefault = new ActivityInfo();
- aInfoDefault.name = "fakeHomeActivity";
- aInfoDefault.applicationInfo = new ApplicationInfo();
- aInfoDefault.applicationInfo.packageName = "fakeHomePackage";
- doReturn(aInfoDefault).when(mRootActivityContainer).resolveHomeActivity(anyInt(),
- refEq(homeIntent));
-
- // Let resolveActivities call to validate both main launcher and second launcher so that
- // resolveActivities call does not work as enabler for secondary.
- final List<ResolveInfo> resolutions1 = new ArrayList<>();
- final ResolveInfo resolveInfo1 = new ResolveInfo();
- resolveInfo1.activityInfo = new ActivityInfo();
- resolveInfo1.activityInfo.name = aInfoDefault.name;
- resolveInfo1.activityInfo.applicationInfo = aInfoDefault.applicationInfo;
- resolutions1.add(resolveInfo1);
- doReturn(resolutions1).when(mRootActivityContainer).resolveActivities(anyInt(),
- refEq(homeIntent));
- final List<ResolveInfo> resolutions2 = new ArrayList<>();
- final ResolveInfo resolveInfo2 = new ResolveInfo();
- resolveInfo2.activityInfo = new ActivityInfo();
- resolveInfo2.activityInfo.name = aInfoSecondary.name;
- resolveInfo2.activityInfo.applicationInfo = aInfoSecondary.applicationInfo;
- resolutions2.add(resolveInfo2);
- doReturn(resolutions2).when(mRootActivityContainer).resolveActivities(anyInt(),
- refEq(secondaryHomeIntent));
-
- doReturn(true).when(mRootActivityContainer).canStartHomeOnDisplay(
- any(), anyInt(), anyBoolean());
-
- final Pair<ActivityInfo, Intent> resolvedInfo = mRootActivityContainer
- .resolveSecondaryHomeActivity(0 /* userId */, 1 /* displayId */);
-
- assertEquals(secondaryComp.getClassName(), resolvedInfo.first.name);
- assertEquals(secondaryComp.getPackageName(),
- resolvedInfo.first.applicationInfo.packageName);
- assertEquals(aInfoSecondary.name, resolvedInfo.first.name);
- }
-
- /**
* Tests that secondary home should be selected if default home not support secondary displays
* or there is no matched activity in the same package as selected default home.
*/
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskPersisterTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskPersisterTest.java
index 9dfeadf..9fc1602 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskPersisterTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskPersisterTest.java
@@ -29,6 +29,8 @@
import android.platform.test.annotations.Presubmit;
import android.util.SparseBooleanArray;
+import androidx.test.filters.FlakyTest;
+
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -72,6 +74,7 @@
}
@Test
+ @FlakyTest(bugId = 131005232)
public void testTaskIdsPersistence() {
SparseBooleanArray taskIdsOnFile = new SparseBooleanArray();
for (int i = 0; i < 100; i++) {
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 6387334..69500d7 100755
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -3313,7 +3313,7 @@
});
sDefaults.putStringArray(KEY_EMERGENCY_NUMBER_PREFIX_STRING_ARRAY, new String[0]);
sDefaults.putBoolean(KEY_USE_USIM_BOOL, false);
- sDefaults.putBoolean(KEY_SHOW_WFC_LOCATION_PRIVACY_POLICY_BOOL, true);
+ sDefaults.putBoolean(KEY_SHOW_WFC_LOCATION_PRIVACY_POLICY_BOOL, false);
sDefaults.putBoolean(KEY_AUTO_CANCEL_CS_REJECT_NOTIFICATION, false);
sDefaults.putString(KEY_SMART_FORWARDING_CONFIG_COMPONENT_NAME_STRING, "");
sDefaults.putBoolean(KEY_ALWAYS_SHOW_PRIMARY_SIGNAL_BAR_IN_OPPORTUNISTIC_NETWORK_BOOLEAN,
diff --git a/telephony/java/android/telephony/PhoneStateListener.java b/telephony/java/android/telephony/PhoneStateListener.java
index 373c5d2..3ce28a4 100644
--- a/telephony/java/android/telephony/PhoneStateListener.java
+++ b/telephony/java/android/telephony/PhoneStateListener.java
@@ -38,6 +38,8 @@
import java.util.Map;
import java.util.concurrent.Executor;
+import dalvik.system.VMRuntime;
+
/**
* A listener class for monitoring changes in specific telephony states
* on the device, including service state, signal strength, message
@@ -400,8 +402,12 @@
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
public PhoneStateListener(Integer subId) {
this(subId, Looper.myLooper());
+ if (subId != null && VMRuntime.getRuntime().getTargetSdkVersion()
+ >= Build.VERSION_CODES.Q) {
+ throw new IllegalArgumentException("PhoneStateListener with subId: "
+ + subId + " is not supported, use default constructor");
+ }
}
-
/**
* Create a PhoneStateListener for the Phone using the specified subscription
* and non-null Looper.
@@ -410,6 +416,11 @@
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
public PhoneStateListener(Integer subId, Looper looper) {
this(subId, new HandlerExecutor(new Handler(looper)));
+ if (subId != null && VMRuntime.getRuntime().getTargetSdkVersion()
+ >= Build.VERSION_CODES.Q) {
+ throw new IllegalArgumentException("PhoneStateListener with subId: "
+ + subId + " is not supported, use default constructor");
+ }
}
/**
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index f0cad6a..903e533 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -108,6 +108,8 @@
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+import dalvik.system.VMRuntime;
+
/**
* Provides access to information about the telephony services on
* the device. Applications can use the methods in this class to
@@ -4863,18 +4865,22 @@
* Registers a listener object to receive notification of changes
* in specified telephony states.
* <p>
- * To register a listener, pass a {@link PhoneStateListener}
- * and specify at least one telephony state of interest in
- * the events argument.
+ * To register a listener, pass a {@link PhoneStateListener} and specify at least one telephony
+ * state of interest in the events argument.
*
- * At registration, and when a specified telephony state
- * changes, the telephony manager invokes the appropriate
- * callback method on the listener object and passes the
- * current (updated) values.
+ * At registration, and when a specified telephony state changes, the telephony manager invokes
+ * the appropriate callback method on the listener object and passes the current (updated)
+ * values.
* <p>
- * To unregister a listener, pass the listener object and set the
- * events argument to
+ * To un-register a listener, pass the listener object and set the events argument to
* {@link PhoneStateListener#LISTEN_NONE LISTEN_NONE} (0).
+ *
+ * If this TelephonyManager object has been created with {@link #createForSubscriptionId},
+ * applies to the given subId. Otherwise, applies to
+ * {@link SubscriptionManager#getDefaultSubscriptionId()}. To listen events for multiple subIds,
+ * pass a separate listener object to each TelephonyManager object created with
+ * {@link #createForSubscriptionId}.
+ *
* Note: if you call this method while in the middle of a binder transaction, you <b>must</b>
* call {@link android.os.Binder#clearCallingIdentity()} before calling this method. A
* {@link SecurityException} will be thrown otherwise.
@@ -4889,17 +4895,26 @@
if (mContext == null) return;
try {
boolean notifyNow = (getITelephony() != null);
- // If the listener has not explicitly set the subId (for example, created with the
- // default constructor), replace the subId so it will listen to the account the
- // telephony manager is created with.
- if (listener.mSubId == null) {
- listener.mSubId = mSubId;
- }
-
ITelephonyRegistry registry = getTelephonyRegistry();
if (registry != null) {
- registry.listenForSubscriber(listener.mSubId, getOpPackageName(),
+ int subId;
+ // subId from phonestatelistner is deprecated Q on forward, use the subId from
+ // TelephonyManager instance.
+ if (VMRuntime.getRuntime().getTargetSdkVersion() >= Build.VERSION_CODES.Q
+ || listener.mSubId == null) {
+ subId = mSubId;
+ } else {
+ subId = listener.mSubId;
+ }
+
+ registry.listenForSubscriber(subId, getOpPackageName(),
listener.callback, events, notifyNow);
+ // TODO: remove this once we remove PhoneStateListener constructor with subId.
+ if (events == PhoneStateListener.LISTEN_NONE) {
+ listener.mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+ } else {
+ listener.mSubId = subId;
+ }
} else {
Rlog.w(TAG, "telephony registry not ready.");
}