summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmds/incidentd/src/IncidentService.cpp3
-rw-r--r--cmds/statsd/src/StatsLogProcessor.h1
-rw-r--r--cmds/statsd/src/metrics/MetricProducer.cpp7
-rw-r--r--cmds/statsd/src/metrics/MetricProducer.h5
-rw-r--r--cmds/statsd/src/metrics/MetricsManager.h1
-rw-r--r--cmds/statsd/src/metrics/ValueMetricProducer.cpp6
-rw-r--r--cmds/statsd/tests/e2e/MetricActivation_e2e_test.cpp394
-rw-r--r--core/java/android/hardware/SensorAdditionalInfo.java48
-rw-r--r--core/java/android/os/Environment.java31
-rw-r--r--core/java/android/view/textclassifier/ActionsSuggestionsHelper.java10
-rw-r--r--core/java/android/view/textclassifier/intent/LabeledIntent.java6
-rw-r--r--core/java/com/android/internal/os/BinderDeathDispatcher.java147
-rw-r--r--core/java/com/android/internal/util/ArrayUtils.java4
-rw-r--r--core/tests/coretests/src/android/view/textclassifier/ActionsSuggestionsHelperTest.java30
-rw-r--r--core/tests/coretests/src/com/android/internal/os/BinderDeathDispatcherTest.java265
-rw-r--r--packages/SystemUI/src/com/android/systemui/ScreenDecorations.java115
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java62
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java21
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java12
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java53
-rw-r--r--services/core/java/com/android/server/AlarmManagerService.java26
-rw-r--r--services/core/java/com/android/server/content/ContentService.java53
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerService.java23
-rw-r--r--services/core/java/com/android/server/wm/ActivityStack.java36
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java10
-rw-r--r--services/tests/servicestests/src/com/android/server/content/ObserverNodeTest.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java54
-rw-r--r--telephony/java/android/telephony/TelephonyManager.java42
31 files changed, 1294 insertions, 203 deletions
diff --git a/cmds/incidentd/src/IncidentService.cpp b/cmds/incidentd/src/IncidentService.cpp
index a8b99ceecd7f..a52726396b53 100644
--- a/cmds/incidentd/src/IncidentService.cpp
+++ b/cmds/incidentd/src/IncidentService.cpp
@@ -564,7 +564,8 @@ status_t IncidentService::dump(int fd, const Vector<String16>& args) {
int skipped[] = SKIPPED_SECTIONS;
for (const Section** section = SECTION_LIST; *section; section++) {
const int id = (*section)->id;
- if (std::find(std::begin(skipped), std::end(skipped), id) == std::end(skipped)) {
+ if (std::find(std::begin(skipped), std::end(skipped), id) == std::end(skipped)
+ && !section_requires_specific_mention(id)) {
incidentArgs.addSection(id);
}
}
diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h
index ce75f78c04ea..f0db1b0128a1 100644
--- a/cmds/statsd/src/StatsLogProcessor.h
+++ b/cmds/statsd/src/StatsLogProcessor.h
@@ -272,6 +272,7 @@ private:
FRIEND_TEST(MetricActivationE2eTest, TestCountMetric);
FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithOneDeactivation);
FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithTwoDeactivations);
+ FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithSameDeactivation);
FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations);
FRIEND_TEST(DurationMetricE2eTest, TestOneBucket);
diff --git a/cmds/statsd/src/metrics/MetricProducer.cpp b/cmds/statsd/src/metrics/MetricProducer.cpp
index 8cbc9c45ca27..0e3cde5364b1 100644
--- a/cmds/statsd/src/metrics/MetricProducer.cpp
+++ b/cmds/statsd/src/metrics/MetricProducer.cpp
@@ -124,7 +124,8 @@ void MetricProducer::addActivation(int activationTrackerIndex, const ActivationT
std::make_shared<Activation>(activationType, ttl_seconds * NS_PER_SEC);
mEventActivationMap.emplace(activationTrackerIndex, activation);
if (-1 != deactivationTrackerIndex) {
- mEventDeactivationMap.emplace(deactivationTrackerIndex, activation);
+ auto& deactivationList = mEventDeactivationMap[deactivationTrackerIndex];
+ deactivationList.push_back(activation);
}
}
@@ -155,7 +156,9 @@ void MetricProducer::cancelEventActivationLocked(int deactivationTrackerIndex) {
if (it == mEventDeactivationMap.end()) {
return;
}
- it->second->state = ActivationState::kNotActive;
+ for (auto activationToCancelIt : it->second) {
+ activationToCancelIt->state = ActivationState::kNotActive;
+ }
}
void MetricProducer::loadActiveMetricLocked(const ActiveMetric& activeMetric,
diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h
index aa757618435c..09ad2903fa4c 100644
--- a/cmds/statsd/src/metrics/MetricProducer.h
+++ b/cmds/statsd/src/metrics/MetricProducer.h
@@ -407,8 +407,8 @@ protected:
// whether the metric producer is ready to generate metrics.
std::unordered_map<int, std::shared_ptr<Activation>> mEventActivationMap;
- // Maps index of atom matcher for deactivation to Activation struct.
- std::unordered_map<int, std::shared_ptr<Activation>> mEventDeactivationMap;
+ // Maps index of atom matcher for deactivation to a list of Activation structs.
+ std::unordered_map<int, std::vector<std::shared_ptr<Activation>>> mEventDeactivationMap;
bool mIsActive;
@@ -422,6 +422,7 @@ protected:
FRIEND_TEST(MetricActivationE2eTest, TestCountMetric);
FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithOneDeactivation);
FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithTwoDeactivations);
+ FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithSameDeactivation);
FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations);
FRIEND_TEST(StatsLogProcessorTest, TestActiveConfigMetricDiskWriteRead);
diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h
index 6fc2c1379645..8efca1e10de5 100644
--- a/cmds/statsd/src/metrics/MetricsManager.h
+++ b/cmds/statsd/src/metrics/MetricsManager.h
@@ -283,6 +283,7 @@ private:
FRIEND_TEST(MetricActivationE2eTest, TestCountMetric);
FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithOneDeactivation);
FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithTwoDeactivations);
+ FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithSameDeactivation);
FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations);
FRIEND_TEST(StatsLogProcessorTest, TestActiveConfigMetricDiskWriteRead);
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
index 01362b6b4fed..0e33a0f9f29b 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
@@ -109,7 +109,8 @@ ValueMetricProducer::ValueMetricProducer(
mMaxPullDelayNs(metric.max_pull_delay_sec() > 0 ? metric.max_pull_delay_sec() * NS_PER_SEC
: StatsdStats::kPullMaxDelayNs),
mSplitBucketForAppUpgrade(metric.split_bucket_for_app_upgrade()),
- mConditionTimer(mIsActive && mCondition == ConditionState::kTrue, timeBaseNs) {
+ // Condition timer will be set in prepareFirstBucketLocked.
+ mConditionTimer(false, timeBaseNs) {
int64_t bucketSizeMills = 0;
if (metric.has_bucket()) {
bucketSizeMills = TimeUnitToBucketSizeInMillisGuardrailed(key.GetUid(), metric.bucket());
@@ -175,6 +176,9 @@ void ValueMetricProducer::prepareFirstBucketLocked() {
if (mIsActive && mIsPulled && mCondition == ConditionState::kTrue && mUseDiff) {
pullAndMatchEventsLocked(mCurrentBucketStartTimeNs, mCondition);
}
+ // Now that activations are processed, start the condition timer if needed.
+ mConditionTimer.onConditionChanged(mIsActive && mCondition == ConditionState::kTrue,
+ mCurrentBucketStartTimeNs);
}
void ValueMetricProducer::onSlicedConditionMayChangeLocked(bool overallCondition,
diff --git a/cmds/statsd/tests/e2e/MetricActivation_e2e_test.cpp b/cmds/statsd/tests/e2e/MetricActivation_e2e_test.cpp
index d99d281afc68..f1b6029f0ab0 100644
--- a/cmds/statsd/tests/e2e/MetricActivation_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/MetricActivation_e2e_test.cpp
@@ -134,6 +134,42 @@ StatsdConfig CreateStatsdConfigWithTwoDeactivations() {
return config;
}
+StatsdConfig CreateStatsdConfigWithSameDeactivations() {
+ StatsdConfig config;
+ config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
+ auto saverModeMatcher = CreateBatterySaverModeStartAtomMatcher();
+ auto crashMatcher = CreateProcessCrashAtomMatcher();
+ auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher();
+ auto brightnessChangedMatcher = CreateScreenBrightnessChangedAtomMatcher();
+
+ *config.add_atom_matcher() = saverModeMatcher;
+ *config.add_atom_matcher() = crashMatcher;
+ *config.add_atom_matcher() = screenOnMatcher;
+ *config.add_atom_matcher() = brightnessChangedMatcher;
+
+ int64_t metricId = 123456;
+ auto countMetric = config.add_count_metric();
+ countMetric->set_id(metricId);
+ countMetric->set_what(crashMatcher.id());
+ countMetric->set_bucket(FIVE_MINUTES);
+ countMetric->mutable_dimensions_in_what()->set_field(
+ android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
+ countMetric->mutable_dimensions_in_what()->add_child()->set_field(1); // uid field
+
+ auto metric_activation1 = config.add_metric_activation();
+ metric_activation1->set_metric_id(metricId);
+ auto event_activation1 = metric_activation1->add_event_activation();
+ event_activation1->set_atom_matcher_id(saverModeMatcher.id());
+ event_activation1->set_ttl_seconds(60 * 6); // 6 minutes
+ event_activation1->set_deactivation_atom_matcher_id(brightnessChangedMatcher.id());
+ auto event_activation2 = metric_activation1->add_event_activation();
+ event_activation2->set_atom_matcher_id(screenOnMatcher.id());
+ event_activation2->set_ttl_seconds(60 * 2); // 2 minutes
+ event_activation2->set_deactivation_atom_matcher_id(brightnessChangedMatcher.id());
+
+ return config;
+}
+
StatsdConfig CreateStatsdConfigWithTwoMetricsTwoDeactivations() {
StatsdConfig config;
config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
@@ -467,7 +503,8 @@ TEST(MetricActivationE2eTest, TestCountMetricWithOneDeactivation) {
EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
EXPECT_EQ(eventDeactivationMap.size(), 1u);
EXPECT_TRUE(eventDeactivationMap.find(3) != eventDeactivationMap.end());
- EXPECT_EQ(eventDeactivationMap[3], eventActivationMap[0]);
+ EXPECT_EQ(eventDeactivationMap[3].size(), 1u);
+ EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
std::unique_ptr<LogEvent> event;
@@ -491,7 +528,7 @@ TEST(MetricActivationE2eTest, TestCountMetricWithOneDeactivation) {
EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
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[3][0], eventActivationMap[0]);
// First processed event.
event = CreateAppCrashEvent(222, bucketStartTimeNs + 15);
@@ -509,7 +546,7 @@ TEST(MetricActivationE2eTest, TestCountMetricWithOneDeactivation) {
EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive);
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[3][0], eventActivationMap[0]);
// 2nd processed event.
// The activation by screen_on event expires, but the one by battery save mode is still active.
@@ -523,7 +560,7 @@ TEST(MetricActivationE2eTest, TestCountMetricWithOneDeactivation) {
EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
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[3][0], eventActivationMap[0]);
// No new broadcast since the config should still be active.
EXPECT_EQ(broadcastCount, 1);
@@ -545,7 +582,7 @@ TEST(MetricActivationE2eTest, TestCountMetricWithOneDeactivation) {
EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
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[3][0], eventActivationMap[0]);
// Re-activate metric via screen on.
event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
@@ -562,7 +599,7 @@ TEST(MetricActivationE2eTest, TestCountMetricWithOneDeactivation) {
EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive);
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[3][0], eventActivationMap[0]);
// 4th processed event.
event = CreateAppCrashEvent(666, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1);
@@ -582,7 +619,7 @@ TEST(MetricActivationE2eTest, TestCountMetricWithOneDeactivation) {
EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive);
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[3][0], eventActivationMap[0]);
// 5th processed event.
event = CreateAppCrashEvent(777, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 40);
@@ -602,7 +639,7 @@ TEST(MetricActivationE2eTest, TestCountMetricWithOneDeactivation) {
EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive);
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[3][0], eventActivationMap[0]);
// Screen-on activation expired.
event = CreateAppCrashEvent(888, bucketStartTimeNs + NS_PER_SEC * 60 * 13);
@@ -618,7 +655,7 @@ TEST(MetricActivationE2eTest, TestCountMetricWithOneDeactivation) {
EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
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[3][0], eventActivationMap[0]);
event = CreateAppCrashEvent(999, bucketStartTimeNs + NS_PER_SEC * 60 * 14 + 1);
processor.OnLogEvent(event.get());
@@ -637,7 +674,7 @@ TEST(MetricActivationE2eTest, TestCountMetricWithOneDeactivation) {
EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
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[3][0], eventActivationMap[0]);
// Cancel battery saver mode activation.
event = CreateScreenBrightnessChangedEvent(140, bucketStartTimeNs + NS_PER_SEC * 60 * 16);
@@ -652,7 +689,7 @@ TEST(MetricActivationE2eTest, TestCountMetricWithOneDeactivation) {
EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
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[3][0], eventActivationMap[0]);
ConfigMetricsReportList reports;
vector<uint8_t> buffer;
@@ -790,8 +827,10 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoDeactivations) {
EXPECT_EQ(eventDeactivationMap.size(), 2u);
EXPECT_TRUE(eventDeactivationMap.find(3) != eventDeactivationMap.end());
EXPECT_TRUE(eventDeactivationMap.find(4) != eventDeactivationMap.end());
- EXPECT_EQ(eventDeactivationMap[3], eventActivationMap[0]);
- EXPECT_EQ(eventDeactivationMap[4], eventActivationMap[2]);
+ EXPECT_EQ(eventDeactivationMap[3].size(), 1u);
+ EXPECT_EQ(eventDeactivationMap[4].size(), 1u);
+ EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
+ EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]);
std::unique_ptr<LogEvent> event;
@@ -815,8 +854,8 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoDeactivations) {
EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
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_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
+ EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]);
// First processed event.
event = CreateAppCrashEvent(222, bucketStartTimeNs + 15);
@@ -834,8 +873,8 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoDeactivations) {
EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive);
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_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
+ EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]);
// 2nd processed event.
// The activation by screen_on event expires, but the one by battery save mode is still active.
@@ -849,8 +888,8 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoDeactivations) {
EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
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_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
+ EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]);
// No new broadcast since the config should still be active.
EXPECT_EQ(broadcastCount, 1);
@@ -872,8 +911,8 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoDeactivations) {
EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
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_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
+ EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]);
// Re-activate metric via screen on.
event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
@@ -890,8 +929,8 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoDeactivations) {
EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive);
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_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
+ EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]);
// 4th processed event.
event = CreateAppCrashEvent(666, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1);
@@ -911,8 +950,8 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoDeactivations) {
EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive);
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_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
+ EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]);
// 5th processed event.
event = CreateAppCrashEvent(777, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 40);
@@ -932,8 +971,8 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoDeactivations) {
EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
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_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
+ EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]);
// Screen-on activation expired.
event = CreateAppCrashEvent(888, bucketStartTimeNs + NS_PER_SEC * 60 * 13);
@@ -948,8 +987,8 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoDeactivations) {
EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
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_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
+ EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]);
event = CreateAppCrashEvent(999, bucketStartTimeNs + NS_PER_SEC * 60 * 14 + 1);
processor.OnLogEvent(event.get());
@@ -968,8 +1007,8 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoDeactivations) {
EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
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_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
+ EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]);
// Cancel battery saver mode and screen on activation.
event = CreateScreenBrightnessChangedEvent(140, bucketStartTimeNs + NS_PER_SEC * 60 * 16);
@@ -984,8 +1023,8 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoDeactivations) {
EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
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_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
+ EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]);
ConfigMetricsReportList reports;
vector<uint8_t> buffer;
@@ -1066,6 +1105,203 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoDeactivations) {
data.bucket_info(0).end_bucket_elapsed_nanos());
}
+TEST(MetricActivationE2eTest, TestCountMetricWithSameDeactivation) {
+ auto config = CreateStatsdConfigWithSameDeactivations();
+
+ int64_t bucketStartTimeNs = NS_PER_SEC * 10; // 10 secs
+ int64_t bucketSizeNs =
+ TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000LL * 1000LL;
+
+ int uid = 12345;
+ int64_t cfgId = 98765;
+ ConfigKey cfgKey(uid, cfgId);
+
+ sp<UidMap> m = new UidMap();
+ sp<StatsPullerManager> pullerManager = new StatsPullerManager();
+ sp<AlarmMonitor> anomalyAlarmMonitor;
+ sp<AlarmMonitor> subscriberAlarmMonitor;
+ vector<int64_t> activeConfigsBroadcast;
+
+ long timeBase1 = 1;
+ int broadcastCount = 0;
+ StatsLogProcessor processor(m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor,
+ bucketStartTimeNs, [](const ConfigKey& key) { return true; },
+ [&uid, &broadcastCount, &activeConfigsBroadcast](const int& broadcastUid,
+ const vector<int64_t>& activeConfigs) {
+ broadcastCount++;
+ EXPECT_EQ(broadcastUid, uid);
+ activeConfigsBroadcast.clear();
+ activeConfigsBroadcast.insert(activeConfigsBroadcast.end(),
+ activeConfigs.begin(), activeConfigs.end());
+ return true;
+ });
+
+ processor.OnConfigUpdated(bucketStartTimeNs, cfgKey, config);
+
+ EXPECT_EQ(processor.mMetricsManagers.size(), 1u);
+ sp<MetricsManager> metricsManager = processor.mMetricsManagers.begin()->second;
+ EXPECT_TRUE(metricsManager->isConfigValid());
+ EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1);
+ sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0];
+ auto& eventActivationMap = metricProducer->mEventActivationMap;
+ auto& eventDeactivationMap = metricProducer->mEventDeactivationMap;
+
+ EXPECT_FALSE(metricsManager->isActive());
+ EXPECT_FALSE(metricProducer->mIsActive);
+ // Two activations: one is triggered by battery saver mode (tracker index 0), the other is
+ // triggered by screen on event (tracker index 2).
+ EXPECT_EQ(eventActivationMap.size(), 2u);
+ 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]->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]->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());
+ EXPECT_EQ(eventDeactivationMap[3].size(), 2u);
+ EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
+ EXPECT_EQ(eventDeactivationMap[3][1], eventActivationMap[2]);
+ EXPECT_EQ(broadcastCount, 0);
+
+ std::unique_ptr<LogEvent> event;
+
+ // Event that should be ignored.
+ event = CreateAppCrashEvent(111, bucketStartTimeNs + 1);
+ processor.OnLogEvent(event.get());
+
+ // Activate metric via screen on for 2 minutes.
+ event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, bucketStartTimeNs + 10);
+ processor.OnLogEvent(event.get());
+ EXPECT_TRUE(metricsManager->isActive());
+ EXPECT_TRUE(metricProducer->mIsActive);
+ EXPECT_EQ(broadcastCount, 1);
+ EXPECT_EQ(activeConfigsBroadcast.size(), 1);
+ EXPECT_EQ(activeConfigsBroadcast[0], cfgId);
+ EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
+ EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive);
+ EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 10);
+
+ // 1st processed event.
+ event = CreateAppCrashEvent(222, bucketStartTimeNs + 15);
+ processor.OnLogEvent(event.get());
+
+ // Enable battery saver mode activation for 5 minutes.
+ event = CreateBatterySaverOnEvent(bucketStartTimeNs + NS_PER_SEC * 60 + 10);
+ processor.OnLogEvent(event.get());
+ EXPECT_TRUE(metricsManager->isActive());
+ EXPECT_TRUE(metricProducer->mIsActive);
+ EXPECT_EQ(broadcastCount, 1);
+ EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive);
+ EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 + 10);
+ EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive);
+ EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 10);
+
+ // 2nd processed event.
+ event = CreateAppCrashEvent(333, bucketStartTimeNs + NS_PER_SEC * 60 + 40);
+ processor.OnLogEvent(event.get());
+
+ // Cancel battery saver mode and screen on activation.
+ int64_t firstDeactivation = bucketStartTimeNs + NS_PER_SEC * 61;
+ event = CreateScreenBrightnessChangedEvent(64, firstDeactivation);
+ processor.OnLogEvent(event.get());
+ EXPECT_FALSE(metricsManager->isActive());
+ EXPECT_FALSE(metricProducer->mIsActive);
+ // New broadcast since the config is no longer active.
+ EXPECT_EQ(broadcastCount, 2);
+ EXPECT_EQ(activeConfigsBroadcast.size(), 0);
+ EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
+ EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
+
+ // Should be ignored
+ event = CreateAppCrashEvent(444, bucketStartTimeNs + NS_PER_SEC * 61 + 80);
+ processor.OnLogEvent(event.get());
+
+ // Re-enable battery saver mode activation.
+ event = CreateBatterySaverOnEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 15);
+ processor.OnLogEvent(event.get());
+ EXPECT_TRUE(metricsManager->isActive());
+ EXPECT_TRUE(metricProducer->mIsActive);
+ EXPECT_EQ(broadcastCount, 3);
+ EXPECT_EQ(activeConfigsBroadcast.size(), 1);
+ EXPECT_EQ(activeConfigsBroadcast[0], cfgId);
+ EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive);
+ EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 15);
+ EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
+
+ // 3rd processed event.
+ event = CreateAppCrashEvent(555, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 80);
+ processor.OnLogEvent(event.get());
+
+ // Cancel battery saver mode activation.
+ int64_t secondDeactivation = bucketStartTimeNs + NS_PER_SEC * 60 * 13;
+ event = CreateScreenBrightnessChangedEvent(140, secondDeactivation);
+ processor.OnLogEvent(event.get());
+ EXPECT_FALSE(metricsManager->isActive());
+ EXPECT_FALSE(metricProducer->mIsActive);
+ EXPECT_EQ(broadcastCount, 4);
+ EXPECT_EQ(activeConfigsBroadcast.size(), 0);
+ EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
+ EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
+
+ // Should be ignored.
+ event = CreateAppCrashEvent(666, bucketStartTimeNs + NS_PER_SEC * 60 * 13 + 80);
+ processor.OnLogEvent(event.get());
+
+ ConfigMetricsReportList reports;
+ vector<uint8_t> buffer;
+ processor.onDumpReport(cfgKey, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 1, false, true,
+ ADB_DUMP, FAST, &buffer);
+ EXPECT_TRUE(buffer.size() > 0);
+ EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+ backfillDimensionPath(&reports);
+ backfillStartEndTimestamp(&reports);
+ EXPECT_EQ(1, reports.reports_size());
+ EXPECT_EQ(1, reports.reports(0).metrics_size());
+ EXPECT_EQ(3, reports.reports(0).metrics(0).count_metrics().data_size());
+
+ StatsLogReport::CountMetricDataWrapper countMetrics;
+ sortMetricDataByDimensionsValue(
+ reports.reports(0).metrics(0).count_metrics(), &countMetrics);
+ EXPECT_EQ(3, countMetrics.data_size());
+
+ auto data = countMetrics.data(0);
+ EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
+ EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+ EXPECT_EQ(1 /* uid field */,
+ data.dimensions_in_what().value_tuple().dimensions_value(0).field());
+ EXPECT_EQ(222, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
+ EXPECT_EQ(1, data.bucket_info_size());
+ EXPECT_EQ(1, data.bucket_info(0).count());
+ EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
+ EXPECT_EQ(firstDeactivation, data.bucket_info(0).end_bucket_elapsed_nanos());
+
+ data = countMetrics.data(1);
+ EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
+ EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+ EXPECT_EQ(1 /* uid field */,
+ data.dimensions_in_what().value_tuple().dimensions_value(0).field());
+ EXPECT_EQ(333, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
+ EXPECT_EQ(1, data.bucket_info_size());
+ EXPECT_EQ(1, data.bucket_info(0).count());
+ EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
+ EXPECT_EQ(firstDeactivation, data.bucket_info(0).end_bucket_elapsed_nanos());
+
+ data = countMetrics.data(2);
+ EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
+ EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+ EXPECT_EQ(1 /* uid field */,
+ data.dimensions_in_what().value_tuple().dimensions_value(0).field());
+ EXPECT_EQ(555, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
+ EXPECT_EQ(1, data.bucket_info_size());
+ EXPECT_EQ(1, data.bucket_info(0).count());
+ // Partial bucket as metric is deactivated.
+ EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
+ EXPECT_EQ(secondDeactivation, data.bucket_info(0).end_bucket_elapsed_nanos());
+}
+
TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations) {
auto config = CreateStatsdConfigWithTwoMetricsTwoDeactivations();
@@ -1127,8 +1363,10 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations) {
EXPECT_EQ(eventDeactivationMap.size(), 2u);
EXPECT_TRUE(eventDeactivationMap.find(3) != eventDeactivationMap.end());
EXPECT_TRUE(eventDeactivationMap.find(4) != eventDeactivationMap.end());
- EXPECT_EQ(eventDeactivationMap[3], eventActivationMap[0]);
- EXPECT_EQ(eventDeactivationMap[4], eventActivationMap[2]);
+ EXPECT_EQ(eventDeactivationMap[3].size(), 1u);
+ EXPECT_EQ(eventDeactivationMap[4].size(), 1u);
+ EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
+ EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]);
EXPECT_EQ(eventActivationMap2.size(), 2u);
EXPECT_TRUE(eventActivationMap2.find(0) != eventActivationMap2.end());
@@ -1142,8 +1380,10 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations) {
EXPECT_EQ(eventDeactivationMap2.size(), 2u);
EXPECT_TRUE(eventDeactivationMap2.find(3) != eventDeactivationMap2.end());
EXPECT_TRUE(eventDeactivationMap2.find(4) != eventDeactivationMap2.end());
- EXPECT_EQ(eventDeactivationMap2[3], eventActivationMap2[0]);
- EXPECT_EQ(eventDeactivationMap2[4], eventActivationMap2[2]);
+ EXPECT_EQ(eventDeactivationMap[3].size(), 1u);
+ EXPECT_EQ(eventDeactivationMap[4].size(), 1u);
+ EXPECT_EQ(eventDeactivationMap2[3][0], eventActivationMap2[0]);
+ EXPECT_EQ(eventDeactivationMap2[4][0], eventActivationMap2[2]);
std::unique_ptr<LogEvent> event;
@@ -1170,8 +1410,8 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations) {
EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
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_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
+ EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]);
EXPECT_TRUE(metricProducer2->mIsActive);
EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kActive);
EXPECT_EQ(eventActivationMap2[0]->start_ns, bucketStartTimeNs + 10);
@@ -1179,8 +1419,8 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations) {
EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kNotActive);
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]);
+ EXPECT_EQ(eventDeactivationMap2[3][0], eventActivationMap2[0]);
+ EXPECT_EQ(eventDeactivationMap2[4][0], eventActivationMap2[2]);
// First processed event.
event = CreateAppCrashEvent(222, bucketStartTimeNs + 15);
@@ -1200,8 +1440,8 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations) {
EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive);
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_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
+ EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]);
EXPECT_TRUE(metricProducer2->mIsActive);
EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kActive);
EXPECT_EQ(eventActivationMap2[0]->start_ns, bucketStartTimeNs + 10);
@@ -1209,8 +1449,8 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations) {
EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kActive);
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]);
+ EXPECT_EQ(eventDeactivationMap2[3][0], eventActivationMap2[0]);
+ EXPECT_EQ(eventDeactivationMap2[4][0], eventActivationMap2[2]);
// 2nd processed event.
// The activation by screen_on event expires, but the one by battery save mode is still active.
@@ -1226,8 +1466,8 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations) {
EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
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_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
+ EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]);
EXPECT_TRUE(metricProducer2->mIsActive);
EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kActive);
EXPECT_EQ(eventActivationMap2[0]->start_ns, bucketStartTimeNs + 10);
@@ -1235,8 +1475,8 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations) {
EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kNotActive);
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]);
+ EXPECT_EQ(eventDeactivationMap2[3][0], eventActivationMap2[0]);
+ EXPECT_EQ(eventDeactivationMap2[4][0], eventActivationMap2[2]);
// No new broadcast since the config should still be active.
EXPECT_EQ(broadcastCount, 1);
@@ -1262,8 +1502,8 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations) {
EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
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_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
+ EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]);
EXPECT_FALSE(metricProducer2->mIsActive);
EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kNotActive);
EXPECT_EQ(eventActivationMap2[0]->start_ns, bucketStartTimeNs + 10);
@@ -1271,8 +1511,8 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations) {
EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kNotActive);
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]);
+ EXPECT_EQ(eventDeactivationMap2[3][0], eventActivationMap2[0]);
+ EXPECT_EQ(eventDeactivationMap2[4][0], eventActivationMap2[2]);
// Re-activate metric via screen on.
event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
@@ -1289,8 +1529,8 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations) {
EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive);
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_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
+ EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]);
EXPECT_TRUE(metricProducer2->mIsActive);
EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kNotActive);
EXPECT_EQ(eventActivationMap2[0]->start_ns, bucketStartTimeNs + 10);
@@ -1298,8 +1538,8 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations) {
EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kActive);
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]);
+ EXPECT_EQ(eventDeactivationMap2[3][0], eventActivationMap2[0]);
+ EXPECT_EQ(eventDeactivationMap2[4][0], eventActivationMap2[2]);
// 4th processed event.
event = CreateAppCrashEvent(666, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1);
@@ -1321,8 +1561,8 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations) {
EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive);
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_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
+ EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]);
EXPECT_TRUE(metricProducer2->mIsActive);
EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kActive);
EXPECT_EQ(eventActivationMap2[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15);
@@ -1330,8 +1570,8 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations) {
EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kActive);
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]);
+ EXPECT_EQ(eventDeactivationMap2[3][0], eventActivationMap2[0]);
+ EXPECT_EQ(eventDeactivationMap2[4][0], eventActivationMap2[2]);
// 5th processed event.
event = CreateAppCrashEvent(777, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 40);
@@ -1353,8 +1593,8 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations) {
EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
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_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
+ EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]);
EXPECT_FALSE(metricProducer2->mIsActive);
EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kNotActive);
EXPECT_EQ(eventActivationMap2[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15);
@@ -1362,8 +1602,8 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations) {
EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kNotActive);
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]);
+ EXPECT_EQ(eventDeactivationMap2[3][0], eventActivationMap2[0]);
+ EXPECT_EQ(eventDeactivationMap2[4][0], eventActivationMap2[2]);
// Screen-on activation expired.
event = CreateAppCrashEvent(888, bucketStartTimeNs + NS_PER_SEC * 60 * 13);
@@ -1380,8 +1620,8 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations) {
EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
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_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
+ EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]);
EXPECT_FALSE(metricProducer2->mIsActive);
EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kNotActive);
EXPECT_EQ(eventActivationMap2[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15);
@@ -1389,8 +1629,8 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations) {
EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kNotActive);
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]);
+ EXPECT_EQ(eventDeactivationMap2[3][0], eventActivationMap2[0]);
+ EXPECT_EQ(eventDeactivationMap2[4][0], eventActivationMap2[2]);
event = CreateAppCrashEvent(999, bucketStartTimeNs + NS_PER_SEC * 60 * 14 + 1);
processor.OnLogEvent(event.get());
@@ -1411,8 +1651,8 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations) {
EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
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_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
+ EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]);
EXPECT_TRUE(metricProducer2->mIsActive);
EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kActive);
EXPECT_EQ(eventActivationMap2[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15);
@@ -1420,8 +1660,8 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations) {
EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kNotActive);
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]);
+ EXPECT_EQ(eventDeactivationMap2[3][0], eventActivationMap2[0]);
+ EXPECT_EQ(eventDeactivationMap2[4][0], eventActivationMap2[2]);
// Cancel battery saver mode and screen on activation.
event = CreateScreenBrightnessChangedEvent(140, bucketStartTimeNs + NS_PER_SEC * 60 * 16);
@@ -1436,8 +1676,8 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations) {
EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
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_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
+ EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]);
EXPECT_FALSE(metricProducer2->mIsActive);
EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kNotActive);
EXPECT_EQ(eventActivationMap2[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15);
@@ -1445,8 +1685,8 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations) {
EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kNotActive);
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]);
+ EXPECT_EQ(eventDeactivationMap2[3][0], eventActivationMap2[0]);
+ EXPECT_EQ(eventDeactivationMap2[4][0], eventActivationMap2[2]);
ConfigMetricsReportList reports;
vector<uint8_t> buffer;
diff --git a/core/java/android/hardware/SensorAdditionalInfo.java b/core/java/android/hardware/SensorAdditionalInfo.java
index 5ff627f48017..12edc5eb7e33 100644
--- a/core/java/android/hardware/SensorAdditionalInfo.java
+++ b/core/java/android/hardware/SensorAdditionalInfo.java
@@ -119,12 +119,50 @@ public class SensorAdditionalInfo {
public static final int TYPE_VEC3_CALIBRATION = 0x10002;
/**
- * Sensor placement. Describes location and installation angle of the sensor device.
+ * Sensor placement.
*
- * Payload:
- * floatValues[0..11]: First 3 rows of homogeneous matrix in row major order that describes
- * the location and orientation of the sensor. Origin of reference will be the mobile device
- * geometric sensor. Reference frame is defined as the same as Android sensor frame.
+ * Provides the orientation and location of the sensor element in terms of the
+ * Android coordinate system. This data is given as a 3x4 matrix consisting of a 3x3 rotation
+ * matrix (R) concatenated with a 3x1 location vector (t). The rotation matrix provides the
+ * orientation of the Android device coordinate frame relative to the local coordinate frame of
+ * the sensor. Note that assuming the axes conventions of the sensor are the same as Android,
+ * this is the inverse of the matrix applied to raw samples read from the sensor to convert them
+ * into the Android representation. The location vector represents the translation from the
+ * origin of the Android sensor coordinate system to the geometric center of the sensor,
+ * specified in millimeters (mm).
+ * <p>
+ * <b>Payload</b>:
+ * <code>floatValues[0..11]</code>: 3x4 matrix in row major order [R; t]
+ * </p>
+ * <p>
+ * <b>Example</b>:
+ * This raw buffer: <code>{0, 1, 0, 0, -1, 0, 0, 10, 0, 0, 1, -2.5}</code><br>
+ * Corresponds to this 3x4 matrix:
+ * <table>
+ * <thead>
+ * <tr><td colspan="3">Orientation</td><td>Location</tr>
+ * </thead>
+ * <tbody>
+ * <tr><td>0</td><td>1</td><td>0</td><td>0</td></tr>
+ * <tr><td>-1</td><td>0</td><td>0</td><td>10</td></tr>
+ * <tr><td>0</td><td>0</td><td>1</td><td>-2.5</td></tr>
+ * </tbody>
+ * </table>
+ * The sensor is oriented such that:
+ * <ul>
+ * <li>The device X axis corresponds to the sensor's local -Y axis
+ * <li>The device Y axis corresponds to the sensor's local X axis
+ * <li>The device Z axis and sensor's local Z axis are equivalent
+ * </ul>
+ * In other words, if viewing the origin of the Android coordinate system from the positive
+ * Z direction, the device coordinate frame is to be rotated 90° counter-clockwise about the
+ * Z axis to align with the sensor's local coordinate frame. Equivalently, a vector in the
+ * Android coordinate frame may be multiplied with R to rotate it 90° clockwise (270°
+ * counter-clockwise), yielding its representation in the sensor's coordinate frame.
+ * Relative to the origin of the Android coordinate system, the physical center of the
+ * sensor is located 10mm in the positive Y direction, and 2.5mm in the negative Z
+ * direction.
+ * </p>
*/
public static final int TYPE_SENSOR_PLACEMENT = 0x10003;
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java
index 3716b3edaed4..0ee9a1192a5f 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -16,6 +16,7 @@
package android.os;
+import android.Manifest;
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.annotation.TestApi;
@@ -25,6 +26,7 @@ import android.app.AppOpsManager;
import android.app.admin.DevicePolicyManager;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.PackageManager;
import android.os.storage.StorageManager;
import android.os.storage.StorageVolume;
import android.provider.MediaStore;
@@ -1159,11 +1161,36 @@ public class Environment {
*/
public static boolean isExternalStorageLegacy(@NonNull File path) {
final Context context = AppGlobals.getInitialApplication();
+ final int uid = context.getApplicationInfo().uid;
+ if (Process.isIsolated(uid)) {
+ return false;
+ }
+
+ final PackageManager packageManager = context.getPackageManager();
+ if (packageManager.isInstantApp()) {
+ return false;
+ }
+
+ if (packageManager.checkPermission(Manifest.permission.WRITE_MEDIA_STORAGE,
+ context.getPackageName()) == PackageManager.PERMISSION_GRANTED) {
+ return true;
+ }
+
+ if (packageManager.checkPermission(Manifest.permission.INSTALL_PACKAGES,
+ context.getPackageName()) == PackageManager.PERMISSION_GRANTED) {
+ return true;
+ }
final AppOpsManager appOps = context.getSystemService(AppOpsManager.class);
+ final String[] packagesForUid = packageManager.getPackagesForUid(uid);
+ for (String packageName : packagesForUid) {
+ if (appOps.checkOpNoThrow(AppOpsManager.OP_REQUEST_INSTALL_PACKAGES,
+ uid, packageName) == AppOpsManager.MODE_ALLOWED) {
+ return true;
+ }
+ }
return appOps.checkOpNoThrow(AppOpsManager.OP_LEGACY_STORAGE,
- context.getApplicationInfo().uid,
- context.getOpPackageName()) == AppOpsManager.MODE_ALLOWED;
+ uid, context.getOpPackageName()) == AppOpsManager.MODE_ALLOWED;
}
static File getDirectory(String variableName, String defaultPath) {
diff --git a/core/java/android/view/textclassifier/ActionsSuggestionsHelper.java b/core/java/android/view/textclassifier/ActionsSuggestionsHelper.java
index 9c268f2d18a8..3ed48f655d47 100644
--- a/core/java/android/view/textclassifier/ActionsSuggestionsHelper.java
+++ b/core/java/android/view/textclassifier/ActionsSuggestionsHelper.java
@@ -19,7 +19,9 @@ package android.view.textclassifier;
import android.annotation.Nullable;
import android.app.Person;
import android.app.RemoteAction;
+import android.content.ComponentName;
import android.content.Context;
+import android.content.Intent;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Pair;
@@ -200,10 +202,12 @@ public final class ActionsSuggestionsHelper {
if (remoteAction == null) {
return null;
}
+ Intent actionIntent = ExtrasUtils.getActionIntent(conversationAction.getExtras());
+ ComponentName componentName = actionIntent.getComponent();
+ // Action without a component name will be considered as from the same app.
+ String packageName = componentName == null ? null : componentName.getPackageName();
return new Pair<>(
- conversationAction.getAction().getTitle().toString(),
- ExtrasUtils.getActionIntent(
- conversationAction.getExtras()).getComponent().getPackageName());
+ conversationAction.getAction().getTitle().toString(), packageName);
}
private static final class PersonEncoder {
diff --git a/core/java/android/view/textclassifier/intent/LabeledIntent.java b/core/java/android/view/textclassifier/intent/LabeledIntent.java
index b4bc8d39d562..30fc20ea86a1 100644
--- a/core/java/android/view/textclassifier/intent/LabeledIntent.java
+++ b/core/java/android/view/textclassifier/intent/LabeledIntent.java
@@ -118,14 +118,16 @@ public final class LabeledIntent {
return null;
}
Intent resolvedIntent = new Intent(intent);
- resolvedIntent.setComponent(new ComponentName(packageName, className));
resolvedIntent.putExtra(
TextClassifier.EXTRA_FROM_TEXT_CLASSIFIER,
getFromTextClassifierExtra(textLanguagesBundle));
-
boolean shouldShowIcon = false;
Icon icon = null;
if (!"android".equals(packageName)) {
+ // We only set the component name when the package name is not resolved to "android"
+ // to workaround a bug that explicit intent with component name == ResolverActivity
+ // can't be launched on keyguard.
+ resolvedIntent.setComponent(new ComponentName(packageName, className));
if (resolveInfo.activityInfo.getIconResource() != 0) {
icon = Icon.createWithResource(
packageName, resolveInfo.activityInfo.getIconResource());
diff --git a/core/java/com/android/internal/os/BinderDeathDispatcher.java b/core/java/com/android/internal/os/BinderDeathDispatcher.java
new file mode 100644
index 000000000000..0c93f7f160e4
--- /dev/null
+++ b/core/java/com/android/internal/os/BinderDeathDispatcher.java
@@ -0,0 +1,147 @@
+/*
+ * 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.internal.os;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.IBinder;
+import android.os.IBinder.DeathRecipient;
+import android.os.IInterface;
+import android.os.RemoteException;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.io.PrintWriter;
+
+/**
+ * Multiplexes multiple binder death recipients on the same binder objects, so that at the native
+ * level, we only need to keep track of one death recipient reference. This will help reduce the
+ * number of JNI strong references.
+ *
+ * test with: atest FrameworksCoreTests:BinderDeathDispatcherTest
+ */
+public class BinderDeathDispatcher<T extends IInterface> {
+ private static final String TAG = "BinderDeathDispatcher";
+
+ private final Object mLock = new Object();
+
+ @GuardedBy("mLock")
+ private final ArrayMap<IBinder, RecipientsInfo> mTargets = new ArrayMap<>();
+
+ @VisibleForTesting
+ class RecipientsInfo implements DeathRecipient {
+ final IBinder mTarget;
+
+ /**
+ * Recipient list. If it's null, {@link #mTarget} has already died, but in that case
+ * this RecipientsInfo instance is removed from {@link #mTargets}.
+ */
+ @GuardedBy("mLock")
+ @Nullable
+ ArraySet<DeathRecipient> mRecipients = new ArraySet<>();
+
+ private RecipientsInfo(IBinder target) {
+ mTarget = target;
+ }
+
+ @Override
+ public void binderDied() {
+ final ArraySet<DeathRecipient> copy;
+ synchronized (mLock) {
+ copy = mRecipients;
+ mRecipients = null;
+
+ // Also remove from the targets.
+ mTargets.remove(mTarget);
+ }
+ if (copy == null) {
+ return;
+ }
+ // Let's call it without holding the lock.
+ final int size = copy.size();
+ for (int i = 0; i < size; i++) {
+ copy.valueAt(i).binderDied();
+ }
+ }
+ }
+
+ /**
+ * Add a {@code recipient} to the death recipient list on {@code target}.
+ *
+ * @return # of recipients in the recipient list, including {@code recipient}. Or, -1
+ * if {@code target} is already dead, in which case recipient's
+ * {@link DeathRecipient#binderDied} won't be called.
+ */
+ public int linkToDeath(@NonNull T target, @NonNull DeathRecipient recipient) {
+ final IBinder ib = target.asBinder();
+ synchronized (mLock) {
+ RecipientsInfo info = mTargets.get(ib);
+ if (info == null) {
+ info = new RecipientsInfo(ib);
+
+ // First recipient; need to link to death.
+ try {
+ ib.linkToDeath(info, 0);
+ } catch (RemoteException e) {
+ return -1; // Already dead.
+ }
+ mTargets.put(ib, info);
+ }
+ info.mRecipients.add(recipient);
+ return info.mRecipients.size();
+ }
+ }
+
+ public void unlinkToDeath(@NonNull T target, @NonNull DeathRecipient recipient) {
+ final IBinder ib = target.asBinder();
+
+ synchronized (mLock) {
+ final RecipientsInfo info = mTargets.get(ib);
+ if (info == null) {
+ return;
+ }
+ if (info.mRecipients.remove(recipient) && info.mRecipients.size() == 0) {
+ info.mTarget.unlinkToDeath(info, 0);
+ mTargets.remove(info.mTarget);
+ }
+ }
+ }
+
+ public void dump(PrintWriter pw, String indent) {
+ synchronized (mLock) {
+ pw.print(indent);
+ pw.print("# of watched binders: ");
+ pw.println(mTargets.size());
+
+ pw.print(indent);
+ pw.print("# of death recipients: ");
+ int n = 0;
+ for (RecipientsInfo info : mTargets.values()) {
+ n += info.mRecipients.size();
+ }
+ pw.println(n);
+ }
+ }
+
+ @VisibleForTesting
+ public ArrayMap<IBinder, RecipientsInfo> getTargetsForTest() {
+ return mTargets;
+ }
+}
diff --git a/core/java/com/android/internal/util/ArrayUtils.java b/core/java/com/android/internal/util/ArrayUtils.java
index e8691fa5e23e..b73ecd1974aa 100644
--- a/core/java/com/android/internal/util/ArrayUtils.java
+++ b/core/java/com/android/internal/util/ArrayUtils.java
@@ -756,4 +756,8 @@ public class ArrayUtils {
return String.valueOf(value);
}
}
+
+ public static @Nullable <T> T firstOrNull(T[] items) {
+ return items.length > 0 ? items[0] : null;
+ }
}
diff --git a/core/tests/coretests/src/android/view/textclassifier/ActionsSuggestionsHelperTest.java b/core/tests/coretests/src/android/view/textclassifier/ActionsSuggestionsHelperTest.java
index db5f82a0a373..80bce264c11b 100644
--- a/core/tests/coretests/src/android/view/textclassifier/ActionsSuggestionsHelperTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/ActionsSuggestionsHelperTest.java
@@ -208,6 +208,36 @@ public class ActionsSuggestionsHelperTest {
assertThat(conversationActions.get(2).getAction()).isNull();
}
+ @Test
+ public void testDeduplicateActions_nullComponent() {
+ Bundle phoneExtras = new Bundle();
+ Intent phoneIntent = new Intent(Intent.ACTION_DIAL);
+ ExtrasUtils.putActionIntent(phoneExtras, phoneIntent);
+ PendingIntent pendingIntent = PendingIntent.getActivity(
+ InstrumentationRegistry.getTargetContext(),
+ 0,
+ phoneIntent,
+ 0);
+ Icon icon = Icon.createWithData(new byte[0], 0, 0);
+ ConversationAction action =
+ new ConversationAction.Builder(ConversationAction.TYPE_CALL_PHONE)
+ .setAction(new RemoteAction(icon, "label", "1", pendingIntent))
+ .setExtras(phoneExtras)
+ .build();
+ ConversationAction actionWithSameLabel =
+ new ConversationAction.Builder(ConversationAction.TYPE_CALL_PHONE)
+ .setAction(new RemoteAction(
+ icon, "label", "2", pendingIntent))
+ .setExtras(phoneExtras)
+ .build();
+
+ List<ConversationAction> conversationActions =
+ ActionsSuggestionsHelper.removeActionsWithDuplicates(
+ Arrays.asList(action, actionWithSameLabel));
+
+ assertThat(conversationActions).isEmpty();
+ }
+
public void createLabeledIntentResult_null() {
ActionsSuggestionsModel.ActionSuggestion nativeSuggestion =
new ActionsSuggestionsModel.ActionSuggestion(
diff --git a/core/tests/coretests/src/com/android/internal/os/BinderDeathDispatcherTest.java b/core/tests/coretests/src/com/android/internal/os/BinderDeathDispatcherTest.java
new file mode 100644
index 000000000000..59148870fa3c
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/os/BinderDeathDispatcherTest.java
@@ -0,0 +1,265 @@
+/*
+ * 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.internal.os;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.os.DeadObjectException;
+import android.os.IBinder;
+import android.os.IBinder.DeathRecipient;
+import android.os.IInterface;
+import android.os.Parcel;
+import android.os.RemoteException;
+import android.os.ResultReceiver;
+import android.os.ShellCallback;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.FileDescriptor;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class BinderDeathDispatcherTest {
+ private static class MyTarget implements IInterface, IBinder {
+ public boolean isAlive = true;
+ public DeathRecipient mRecipient;
+
+ @Override
+ public String getInterfaceDescriptor() throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public boolean pingBinder() {
+ return false;
+ }
+
+ @Override
+ public boolean isBinderAlive() {
+ return isAlive;
+ }
+
+ @Override
+ public IInterface queryLocalInterface(String descriptor) {
+ return null;
+ }
+
+ @Override
+ public void dump(FileDescriptor fd, String[] args) throws RemoteException {
+
+ }
+
+ @Override
+ public void dumpAsync(FileDescriptor fd, String[] args) throws RemoteException {
+
+ }
+
+ @Override
+ public void shellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
+ String[] args, ShellCallback shellCallback, ResultReceiver resultReceiver)
+ throws RemoteException {
+
+ }
+
+ @Override
+ public boolean transact(int code, Parcel data, Parcel reply, int flags)
+ throws RemoteException {
+ return false;
+ }
+
+ @Override
+ public void linkToDeath(DeathRecipient recipient, int flags) throws RemoteException {
+ // In any situation, a single binder object should only have at most one death
+ // recipient.
+ assertThat(mRecipient).isNull();
+
+ if (!isAlive) {
+ throw new DeadObjectException();
+ }
+
+ mRecipient = recipient;
+ }
+
+ @Override
+ public boolean unlinkToDeath(DeathRecipient recipient, int flags) {
+ if (!isAlive) {
+ return false;
+ }
+ assertThat(mRecipient).isSameAs(recipient);
+ mRecipient = null;
+ return true;
+ }
+
+ @Override
+ public IBinder asBinder() {
+ return this;
+ }
+
+ public void die() {
+ isAlive = false;
+ if (mRecipient != null) {
+ mRecipient.binderDied();
+ }
+ mRecipient = null;
+ }
+
+ public boolean hasDeathRecipient() {
+ return mRecipient != null;
+ }
+ }
+
+ @Test
+ public void testRegisterAndUnregister() {
+ BinderDeathDispatcher<MyTarget> d = new BinderDeathDispatcher<>();
+
+ MyTarget t1 = new MyTarget();
+ MyTarget t2 = new MyTarget();
+ MyTarget t3 = new MyTarget();
+
+ DeathRecipient r1 = mock(DeathRecipient.class);
+ DeathRecipient r2 = mock(DeathRecipient.class);
+ DeathRecipient r3 = mock(DeathRecipient.class);
+ DeathRecipient r4 = mock(DeathRecipient.class);
+ DeathRecipient r5 = mock(DeathRecipient.class);
+
+ // Start hooking up.
+
+ // Link 3 recipients to t1 -- only one real recipient will be set.
+ assertThat(d.linkToDeath(t1, r1)).isEqualTo(1);
+ assertThat(d.getTargetsForTest().size()).isEqualTo(1);
+
+ assertThat(d.linkToDeath(t1, r2)).isEqualTo(2);
+ assertThat(d.linkToDeath(t1, r3)).isEqualTo(3);
+ assertThat(d.getTargetsForTest().size()).isEqualTo(1);
+
+ // Unlink two -- the real recipient is still set.
+ d.unlinkToDeath(t1, r1);
+ d.unlinkToDeath(t1, r2);
+
+ assertThat(t1.hasDeathRecipient()).isTrue();
+ assertThat(d.getTargetsForTest().size()).isEqualTo(1);
+
+ // Unlink the last one. The real recipient is also unlinked.
+ d.unlinkToDeath(t1, r3);
+ assertThat(t1.hasDeathRecipient()).isFalse();
+ assertThat(d.getTargetsForTest().size()).isEqualTo(0);
+
+ // Set recipients to t1, t2 and t3. t3 has two.
+ assertThat(d.linkToDeath(t1, r1)).isEqualTo(1);
+ assertThat(d.linkToDeath(t2, r1)).isEqualTo(1);
+ assertThat(d.linkToDeath(t3, r1)).isEqualTo(1);
+ assertThat(d.linkToDeath(t3, r2)).isEqualTo(2);
+
+
+ // They should all have a real recipient.
+ assertThat(t1.hasDeathRecipient()).isTrue();
+ assertThat(t2.hasDeathRecipient()).isTrue();
+ assertThat(t3.hasDeathRecipient()).isTrue();
+
+ assertThat(d.getTargetsForTest().size()).isEqualTo(3);
+
+ // Unlink r1 from t3. t3 still has r2, so it should still have a real recipient.
+ d.unlinkToDeath(t3, r1);
+ assertThat(t1.hasDeathRecipient()).isTrue();
+ assertThat(t2.hasDeathRecipient()).isTrue();
+ assertThat(t3.hasDeathRecipient()).isTrue();
+ assertThat(d.getTargetsForTest().size()).isEqualTo(3);
+
+ // Unlink r2 from t3. Now t3 has no real recipient.
+ d.unlinkToDeath(t3, r2);
+ assertThat(t3.hasDeathRecipient()).isFalse();
+ assertThat(d.getTargetsForTest().size()).isEqualTo(2);
+ }
+
+ @Test
+ public void testRegisterAndKill() {
+ BinderDeathDispatcher<MyTarget> d = new BinderDeathDispatcher<>();
+
+ MyTarget t1 = new MyTarget();
+ MyTarget t2 = new MyTarget();
+ MyTarget t3 = new MyTarget();
+
+ DeathRecipient r1 = mock(DeathRecipient.class);
+ DeathRecipient r2 = mock(DeathRecipient.class);
+ DeathRecipient r3 = mock(DeathRecipient.class);
+ DeathRecipient r4 = mock(DeathRecipient.class);
+ DeathRecipient r5 = mock(DeathRecipient.class);
+
+ // Hook them up.
+
+ d.linkToDeath(t1, r1);
+ d.linkToDeath(t1, r2);
+ d.linkToDeath(t1, r3);
+
+ // r4 is linked then unlinked. It shouldn't be notified.
+ d.linkToDeath(t1, r4);
+ d.unlinkToDeath(t1, r4);
+
+ d.linkToDeath(t2, r1);
+
+ d.linkToDeath(t3, r3);
+ d.linkToDeath(t3, r5);
+
+ assertThat(d.getTargetsForTest().size()).isEqualTo(3);
+
+ // Kill the targets.
+
+ t1.die();
+ verify(r1, times(1)).binderDied();
+ verify(r2, times(1)).binderDied();
+ verify(r3, times(1)).binderDied();
+ verify(r4, times(0)).binderDied();
+ verify(r5, times(0)).binderDied();
+
+ assertThat(d.getTargetsForTest().size()).isEqualTo(2);
+
+ reset(r1, r2, r3, r4, r5);
+
+ t2.die();
+ verify(r1, times(1)).binderDied();
+ verify(r2, times(0)).binderDied();
+ verify(r3, times(0)).binderDied();
+ verify(r4, times(0)).binderDied();
+ verify(r5, times(0)).binderDied();
+
+ assertThat(d.getTargetsForTest().size()).isEqualTo(1);
+
+ reset(r1, r2, r3, r4, r5);
+
+ t3.die();
+ verify(r1, times(0)).binderDied();
+ verify(r2, times(0)).binderDied();
+ verify(r3, times(1)).binderDied();
+ verify(r4, times(0)).binderDied();
+ verify(r5, times(1)).binderDied();
+
+ assertThat(d.getTargetsForTest().size()).isEqualTo(0);
+
+ // Try to register to a dead object -> should return -1.
+ assertThat(d.linkToDeath(t1, r1)).isEqualTo(-1);
+
+ assertThat(d.getTargetsForTest().size()).isEqualTo(0);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
index 5e192193a87a..305558fd9efa 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
@@ -25,6 +25,9 @@ import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_M
import static com.android.systemui.tuner.TunablePadding.FLAG_END;
import static com.android.systemui.tuner.TunablePadding.FLAG_START;
+import android.animation.Animator;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
import android.annotation.Dimension;
import android.app.ActivityManager;
import android.app.Fragment;
@@ -49,6 +52,7 @@ import android.os.SystemProperties;
import android.provider.Settings.Secure;
import android.util.DisplayMetrics;
import android.util.Log;
+import android.util.MathUtils;
import android.view.DisplayCutout;
import android.view.DisplayInfo;
import android.view.Gravity;
@@ -60,12 +64,8 @@ import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.view.ViewTreeObserver;
import android.view.WindowManager;
-import android.view.animation.Animation;
-import android.view.animation.AnimationSet;
-import android.view.animation.OvershootInterpolator;
+import android.view.animation.Interpolator;
import android.view.animation.PathInterpolator;
-import android.view.animation.ScaleAnimation;
-import android.view.animation.TranslateAnimation;
import android.widget.FrameLayout;
import android.widget.ImageView;
@@ -77,8 +77,10 @@ import com.android.systemui.fragments.FragmentHostManager;
import com.android.systemui.fragments.FragmentHostManager.FragmentListener;
import com.android.systemui.plugins.qs.QS;
import com.android.systemui.qs.SecureSetting;
+import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.statusbar.phone.CollapsedStatusBarFragment;
import com.android.systemui.statusbar.phone.NavigationBarTransitions;
+import com.android.systemui.statusbar.phone.NavigationModeController;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.tuner.TunablePadding;
import com.android.systemui.tuner.TunerService;
@@ -125,6 +127,7 @@ public class ScreenDecorations extends SystemUI implements Tunable,
private Handler mHandler;
private boolean mAssistHintBlocked = false;
private boolean mIsReceivingNavBarColor = false;
+ private boolean mInGesturalMode;
/**
* Converts a set of {@link Rect}s into a {@link Region}
@@ -149,6 +152,58 @@ public class ScreenDecorations extends SystemUI implements Tunable,
mHandler.post(this::startOnScreenDecorationsThread);
setupStatusBarPaddingIfNeeded();
putComponent(ScreenDecorations.class, this);
+ mInGesturalMode = QuickStepContract.isGesturalMode(
+ Dependency.get(NavigationModeController.class)
+ .addListener(this::handleNavigationModeChange));
+ }
+
+ @VisibleForTesting
+ void handleNavigationModeChange(int navigationMode) {
+ if (!mHandler.getLooper().isCurrentThread()) {
+ mHandler.post(() -> handleNavigationModeChange(navigationMode));
+ return;
+ }
+ boolean inGesturalMode = QuickStepContract.isGesturalMode(navigationMode);
+ if (mInGesturalMode != inGesturalMode) {
+ mInGesturalMode = inGesturalMode;
+
+ if (mInGesturalMode && mOverlay == null) {
+ setupDecorations();
+ if (mOverlay != null) {
+ updateLayoutParams();
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns an animator that animates the given view from start to end over durationMs. Start and
+ * end represent total animation progress: 0 is the start, 1 is the end, 1.1 would be an
+ * overshoot.
+ */
+ Animator getHandleAnimator(View view, float start, float end, boolean isLeft, long durationMs,
+ Interpolator interpolator) {
+ // Note that lerp does allow overshoot, in cases where start and end are outside of [0,1].
+ float scaleStart = MathUtils.lerp(2f, 1f, start);
+ float scaleEnd = MathUtils.lerp(2f, 1f, end);
+ Animator scaleX = ObjectAnimator.ofFloat(view, View.SCALE_X, scaleStart, scaleEnd);
+ Animator scaleY = ObjectAnimator.ofFloat(view, View.SCALE_Y, scaleStart, scaleEnd);
+ float translationStart = MathUtils.lerp(0.2f, 0f, start);
+ float translationEnd = MathUtils.lerp(0.2f, 0f, end);
+ int xDirection = isLeft ? -1 : 1;
+ Animator translateX = ObjectAnimator.ofFloat(view, View.TRANSLATION_X,
+ xDirection * translationStart * view.getWidth(),
+ xDirection * translationEnd * view.getWidth());
+ Animator translateY = ObjectAnimator.ofFloat(view, View.TRANSLATION_Y,
+ translationStart * view.getHeight(), translationEnd * view.getHeight());
+
+ AnimatorSet set = new AnimatorSet();
+ set.play(scaleX).with(scaleY);
+ set.play(scaleX).with(translateX);
+ set.play(scaleX).with(translateY);
+ set.setDuration(durationMs);
+ set.setInterpolator(interpolator);
+ return set;
}
private void fade(View view, boolean fadeIn, boolean isLeft) {
@@ -157,23 +212,23 @@ public class ScreenDecorations extends SystemUI implements Tunable,
view.setAlpha(1f);
view.setVisibility(View.VISIBLE);
- AnimationSet anim = new AnimationSet(true);
- anim.setDuration(900);
-
- Animation scaleAnimation = new ScaleAnimation(2f, 1f, 2f, 1f,
- ScaleAnimation.RELATIVE_TO_SELF, 0.5f, ScaleAnimation.RELATIVE_TO_SELF, 0.5f);
- anim.addAnimation(scaleAnimation);
- anim.setInterpolator(new PathInterpolator(0.02f, 0.44f, 0.67f, 1.00f));
-
- Animation translateAnimation = new TranslateAnimation(
- TranslateAnimation.RELATIVE_TO_SELF, isLeft ? -0.2f : 0.2f,
- TranslateAnimation.RELATIVE_TO_SELF,
- 0f,
- TranslateAnimation.RELATIVE_TO_SELF, 0.2f, TranslateAnimation.RELATIVE_TO_SELF,
- 0f);
- anim.addAnimation(translateAnimation);
- anim.setInterpolator(new OvershootInterpolator());
- view.startAnimation(anim);
+ // A piecewise spring-like interpolation.
+ // End value in one animator call must match the start value in the next, otherwise
+ // there will be a discontinuity.
+ AnimatorSet anim = new AnimatorSet();
+ Animator first = getHandleAnimator(view, 0, 1.1f, isLeft, 750,
+ new PathInterpolator(0, 0.45f, .67f, 1f));
+ Interpolator secondInterpolator = new PathInterpolator(0.33f, 0, 0.67f, 1f);
+ Animator second = getHandleAnimator(view, 1.1f, 0.97f, isLeft, 400,
+ secondInterpolator);
+ Animator third = getHandleAnimator(view, 0.97f, 1.02f, isLeft, 400,
+ secondInterpolator);
+ Animator fourth = getHandleAnimator(view, 1.02f, 1f, isLeft, 400,
+ secondInterpolator);
+ anim.play(first).before(second);
+ anim.play(second).before(third);
+ anim.play(third).before(fourth);
+ anim.start();
} else {
view.animate().cancel();
view.animate().setDuration(400).alpha(0f);
@@ -232,6 +287,7 @@ public class ScreenDecorations extends SystemUI implements Tunable,
break;
}
}
+ updateWindowVisibilities();
}
/**
@@ -256,11 +312,15 @@ public class ScreenDecorations extends SystemUI implements Tunable,
return thread.getThreadHandler();
}
+ private boolean shouldHostHandles() {
+ return mInGesturalMode;
+ }
+
private void startOnScreenDecorationsThread() {
mRotation = RotationUtils.getExactRotation(mContext);
mWindowManager = mContext.getSystemService(WindowManager.class);
updateRoundedCornerRadii();
- if (hasRoundedCorners() || shouldDrawCutout()) {
+ if (hasRoundedCorners() || shouldDrawCutout() || shouldHostHandles()) {
setupDecorations();
}
@@ -565,7 +625,10 @@ public class ScreenDecorations extends SystemUI implements Tunable,
boolean visibleForCutout = shouldDrawCutout()
&& overlay.findViewById(R.id.display_cutout).getVisibility() == View.VISIBLE;
boolean visibleForRoundedCorners = hasRoundedCorners();
- overlay.setVisibility(visibleForCutout || visibleForRoundedCorners
+ boolean visibleForHandles = overlay.findViewById(R.id.assist_hint_left).getVisibility()
+ == View.VISIBLE || overlay.findViewById(R.id.assist_hint_right).getVisibility()
+ == View.VISIBLE;
+ overlay.setVisibility(visibleForCutout || visibleForRoundedCorners || visibleForHandles
? View.VISIBLE : View.GONE);
}
@@ -688,10 +751,6 @@ public class ScreenDecorations extends SystemUI implements Tunable,
setSize(mOverlay.findViewById(R.id.right), sizeTop);
setSize(mBottomOverlay.findViewById(R.id.left), sizeBottom);
setSize(mBottomOverlay.findViewById(R.id.right), sizeBottom);
- setSize(mOverlay.findViewById(R.id.assist_hint_left), sizeTop * 2);
- setSize(mOverlay.findViewById(R.id.assist_hint_right), sizeTop * 2);
- setSize(mBottomOverlay.findViewById(R.id.assist_hint_left), sizeBottom * 2);
- setSize(mBottomOverlay.findViewById(R.id.assist_hint_right), sizeBottom * 2);
}
});
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java
index 5717a54fd8a0..18b8a9c859b1 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java
@@ -228,6 +228,10 @@ public abstract class BiometricDialogView extends LinearLayout {
showTryAgainButton(false /* show */);
mCallback.onTryAgainPressed();
});
+
+ // Must set these in order for the back button events to be received.
+ mLayout.setFocusableInTouchMode(true);
+ mLayout.requestFocus();
}
public void onSaveState(Bundle bundle) {
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
index 3ec6cb78ecc1..b05058a92650 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
@@ -125,6 +125,7 @@ public class PipTouchHandler {
private int mImeOffset;
private boolean mIsShelfShowing;
private int mShelfHeight;
+ private int mMovementBoundsExtraOffsets;
private float mSavedSnapFraction = -1f;
private boolean mSendingHoverAccessibilityEvents;
private boolean mMovementWithinMinimize;
@@ -262,7 +263,7 @@ public class PipTouchHandler {
mShelfHeight = shelfHeight;
}
- public void onMovementBoundsChanged(Rect insetBounds, Rect normalBounds, Rect animatingBounds,
+ public void onMovementBoundsChanged(Rect insetBounds, Rect normalBounds, Rect curBounds,
boolean fromImeAdjustment, boolean fromShelfAdjustment, int displayRotation) {
final int bottomOffset = mIsImeShowing ? mImeHeight : 0;
@@ -283,6 +284,12 @@ public class PipTouchHandler {
mSnapAlgorithm.getMovementBounds(mExpandedBounds, insetBounds, expandedMovementBounds,
bottomOffset);
+ // The extra offset does not really affect the movement bounds, but are applied based on the
+ // current state (ime showing, or shelf offset) when we need to actually shift
+ int extraOffset = Math.max(
+ mIsImeShowing ? mImeOffset : 0,
+ !mIsImeShowing && mIsShelfShowing ? mShelfHeight : 0);
+
// If this is from an IME or shelf adjustment, then we should move the PiP so that it is not
// occluded by the IME or shelf.
if (fromImeAdjustment || fromShelfAdjustment) {
@@ -290,41 +297,19 @@ public class PipTouchHandler {
// Defer the update of the current movement bounds until after the user finishes
// touching the screen
} else {
- final int adjustedOffset = Math.max(mIsImeShowing ? mImeHeight + mImeOffset : 0,
- mIsShelfShowing ? mShelfHeight : 0);
- Rect normalAdjustedBounds = new Rect();
- mSnapAlgorithm.getMovementBounds(mNormalBounds, insetBounds, normalAdjustedBounds,
- adjustedOffset);
- Rect expandedAdjustedBounds = new Rect();
- mSnapAlgorithm.getMovementBounds(mExpandedBounds, insetBounds,
- expandedAdjustedBounds, adjustedOffset);
- final Rect toAdjustedBounds = mMenuState == MENU_STATE_FULL
- ? expandedAdjustedBounds
- : normalAdjustedBounds;
- final Rect toMovementBounds = mMenuState == MENU_STATE_FULL
- ? expandedMovementBounds
- : normalMovementBounds;
-
- // If the PIP window needs to shift to right above shelf/IME and it's already above
- // that, don't move the PIP window.
- if (toAdjustedBounds.bottom < mMovementBounds.bottom
- && animatingBounds.top < toAdjustedBounds.bottom) {
- return;
- }
-
- // If the PIP window needs to shift down due to dismissal of shelf/IME but it's way
- // above the position as if shelf/IME shows, don't move the PIP window.
- int movementBoundsAdjustment = toMovementBounds.bottom - mMovementBounds.bottom;
- int offsetAdjustment = fromImeAdjustment ? mImeOffset : mShelfHeight;
- final float bottomOffsetBufferInPx = BOTTOM_OFFSET_BUFFER_DP
+ final float offsetBufferPx = BOTTOM_OFFSET_BUFFER_DP
* mContext.getResources().getDisplayMetrics().density;
- if (toAdjustedBounds.bottom >= mMovementBounds.bottom
- && animatingBounds.top + Math.round(bottomOffsetBufferInPx)
- < toAdjustedBounds.bottom - movementBoundsAdjustment - offsetAdjustment) {
- return;
+ final Rect toMovementBounds = mMenuState == MENU_STATE_FULL
+ ? new Rect(expandedMovementBounds)
+ : new Rect(normalMovementBounds);
+ final int prevBottom = mMovementBounds.bottom - mMovementBoundsExtraOffsets;
+ final int toBottom = toMovementBounds.bottom < toMovementBounds.top
+ ? toMovementBounds.bottom
+ : toMovementBounds.bottom - extraOffset;
+ if ((Math.min(prevBottom, toBottom) - offsetBufferPx) <= curBounds.top
+ && curBounds.top <= (Math.max(prevBottom, toBottom) + offsetBufferPx)) {
+ mMotionHelper.animateToOffset(curBounds, toBottom - curBounds.top);
}
-
- animateToOffset(animatingBounds, toAdjustedBounds);
}
}
@@ -335,6 +320,7 @@ public class PipTouchHandler {
mDisplayRotation = displayRotation;
mInsetBounds.set(insetBounds);
updateMovementBounds(mMenuState);
+ mMovementBoundsExtraOffsets = extraOffset;
// If we have a deferred resize, apply it now
if (mDeferResizeToNormalBoundsUntilRotation == displayRotation) {
@@ -346,14 +332,6 @@ public class PipTouchHandler {
}
}
- private void animateToOffset(Rect animatingBounds, Rect toAdjustedBounds) {
- int offset = toAdjustedBounds.bottom - animatingBounds.top;
- // In landscape mode, PIP window can go offset while launching IME. We want to align the
- // the top of the PIP window with the top of the movement bounds in that case.
- offset += Math.max(0, mMovementBounds.top - animatingBounds.top);
- mMotionHelper.animateToOffset(animatingBounds, offset);
- }
-
private void onRegistrationChanged(boolean isRegistered) {
mAccessibilityManager.setPictureInPictureActionReplacingConnection(isRegistered
? new PipAccessibilityInteractionConnection(mMotionHelper,
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 0f6740d44d04..7c6c556b5241 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
@@ -569,16 +569,21 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G
switch (behavior) {
case BEHAVIOR_ALERTING:
- alert.setSelected(true);
- silence.setSelected(false);
mPriorityDescriptionView.setVisibility(VISIBLE);
mSilentDescriptionView.setVisibility(GONE);
+ post(() -> {
+ alert.setSelected(true);
+ silence.setSelected(false);
+ });
break;
case BEHAVIOR_SILENT:
- alert.setSelected(false);
- silence.setSelected(true);
+
mSilentDescriptionView.setVisibility(VISIBLE);
mPriorityDescriptionView.setVisibility(GONE);
+ post(() -> {
+ alert.setSelected(false);
+ silence.setSelected(true);
+ });
break;
default:
throw new IllegalArgumentException("Unrecognized alerting behavior: " + behavior);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java
index 05a86fa9d7ea..38ff468304b7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java
@@ -152,6 +152,8 @@ public class EdgeBackGestureHandler implements DisplayListener {
private WindowManager.LayoutParams mEdgePanelLp;
private final Rect mSamplingRect = new Rect();
private RegionSamplingHelper mRegionSamplingHelper;
+ private int mLeftInset;
+ private int mRightInset;
public EdgeBackGestureHandler(Context context, OverviewProxyService overviewProxyService) {
final Resources res = context.getResources();
@@ -299,7 +301,7 @@ public class EdgeBackGestureHandler implements DisplayListener {
return false;
}
- if (x > mEdgeWidth && x < (mDisplaySize.x - mEdgeWidth)) {
+ if (x > mEdgeWidth + mLeftInset && x < (mDisplaySize.x - mEdgeWidth - mRightInset)) {
return false;
}
boolean isInExcludedRegion = mExcludeRegion.contains(x, y);
@@ -325,7 +327,7 @@ public class EdgeBackGestureHandler implements DisplayListener {
// Verify if this is in within the touch region and we aren't in immersive mode, and
// either the bouncer is showing or the notification panel is hidden
int stateFlags = mOverviewProxyService.getSystemUiStateFlags();
- mIsOnLeftEdge = ev.getX() <= mEdgeWidth;
+ mIsOnLeftEdge = ev.getX() <= mEdgeWidth + mLeftInset;
mAllowGesture = !QuickStepContract.isBackGestureDisabled(stateFlags)
&& isWithinTouchRegion((int) ev.getX(), (int) ev.getY());
if (mAllowGesture) {
@@ -400,7 +402,7 @@ public class EdgeBackGestureHandler implements DisplayListener {
private void updateSamplingRect() {
int top = mEdgePanelLp.y;
- int left = mIsOnLeftEdge ? 0 : mDisplaySize.x - mEdgePanelLp.width;
+ int left = mIsOnLeftEdge ? mLeftInset : mDisplaySize.x - mRightInset - mEdgePanelLp.width;
int right = left + mEdgePanelLp.width;
int bottom = top + mEdgePanelLp.height;
mSamplingRect.set(left, top, right, bottom);
@@ -442,6 +444,11 @@ public class EdgeBackGestureHandler implements DisplayListener {
InputManager.getInstance().injectInputEvent(ev, InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
}
+ public void setInsets(int leftInset, int rightInset) {
+ mLeftInset = leftInset;
+ mRightInset = rightInset;
+ }
+
class SysUiInputEventReceiver extends InputEventReceiver {
SysUiInputEventReceiver(InputChannel channel, Looper looper) {
super(channel, looper);
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 3ec16090ea87..90bce391c68d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -238,7 +238,19 @@ public class NavigationBarView extends FrameLayout implements
info.setTouchableInsets(InternalInsetsInfo.TOUCHABLE_INSETS_FRAME);
return;
}
+
info.setTouchableInsets(InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
+ ButtonDispatcher imeSwitchButton = getImeSwitchButton();
+ if (imeSwitchButton.getVisibility() == VISIBLE) {
+ // If the IME is not up, but the ime switch button is visible, then make sure that
+ // button is touchable
+ int[] loc = new int[2];
+ View buttonView = imeSwitchButton.getCurrentView();
+ buttonView.getLocationInWindow(loc);
+ info.touchableRegion.set(loc[0], loc[1], loc[0] + buttonView.getWidth(),
+ loc[1] + buttonView.getHeight());
+ return;
+ }
info.touchableRegion.setEmpty();
};
@@ -1095,8 +1107,13 @@ public class NavigationBarView extends FrameLayout implements
@Override
public WindowInsets onApplyWindowInsets(WindowInsets insets) {
- setPadding(insets.getSystemWindowInsetLeft(), insets.getSystemWindowInsetTop(),
- insets.getSystemWindowInsetRight(), insets.getSystemWindowInsetBottom());
+ int leftInset = insets.getSystemWindowInsetLeft();
+ int rightInset = insets.getSystemWindowInsetRight();
+ setPadding(leftInset, insets.getSystemWindowInsetTop(), rightInset,
+ insets.getSystemWindowInsetBottom());
+ // we're passing the insets onto the gesture handler since the back arrow is only
+ // conditionally added and doesn't always get all the insets.
+ mEdgeBackGestureHandler.setInsets(leftInset, rightInset);
return super.onApplyWindowInsets(insets);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index e79aa454aa62..5fff054b3bc2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -1520,6 +1520,8 @@ public class StatusBar extends SystemUI implements DemoMode,
@Override // UnlockMethodCache.OnUnlockMethodChangedListener
public void onUnlockMethodStateChanged() {
+ // Unlock method state changed. Notify KeguardMonitor
+ updateKeyguardState();
logStateToEventlog();
}
@@ -3416,9 +3418,7 @@ public class StatusBar extends SystemUI implements DemoMode,
checkBarModes();
updateScrimController();
mPresenter.updateMediaMetaData(false, mState != StatusBarState.KEYGUARD);
- mKeyguardMonitor.notifyKeyguardState(mStatusBarKeyguardViewManager.isShowing(),
- mUnlockMethodCache.isMethodSecure(),
- mStatusBarKeyguardViewManager.isOccluded());
+ updateKeyguardState();
Trace.endSection();
}
@@ -3457,6 +3457,12 @@ public class StatusBar extends SystemUI implements DemoMode,
mStatusBarStateController.setIsDozing(dozing);
}
+ private void updateKeyguardState() {
+ mKeyguardMonitor.notifyKeyguardState(mStatusBarKeyguardViewManager.isShowing(),
+ mUnlockMethodCache.isMethodSecure(),
+ mStatusBarKeyguardViewManager.isOccluded());
+ }
+
public void onActivationReset() {
mKeyguardIndicationController.hideTransientIndication();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
index 64ab060e14a2..3b5e12c4ef96 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
@@ -45,6 +45,7 @@ import android.testing.TestableLooper.RunWithLooper;
import android.view.Display;
import android.view.View;
import android.view.WindowManager;
+import android.view.WindowManagerPolicyConstants;
import androidx.test.filters.SmallTest;
@@ -52,6 +53,7 @@ import com.android.systemui.R.dimen;
import com.android.systemui.ScreenDecorations.TunablePaddingTagListener;
import com.android.systemui.fragments.FragmentHostManager;
import com.android.systemui.fragments.FragmentService;
+import com.android.systemui.statusbar.phone.NavigationModeController;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.StatusBarWindowView;
import com.android.systemui.tuner.TunablePadding;
@@ -78,6 +80,7 @@ public class ScreenDecorationsTest extends SysuiTestCase {
private TunerService mTunerService;
private StatusBarWindowView mView;
private TunablePaddingService mTunablePaddingService;
+ private NavigationModeController mNavigationModeController;
@Before
public void setup() {
@@ -87,6 +90,8 @@ public class ScreenDecorationsTest extends SysuiTestCase {
mTunablePaddingService = mDependency.injectMockDependency(TunablePaddingService.class);
mTunerService = mDependency.injectMockDependency(TunerService.class);
mFragmentService = mDependency.injectMockDependency(FragmentService.class);
+ mNavigationModeController = mDependency.injectMockDependency(
+ NavigationModeController.class);
mStatusBar = mock(StatusBar.class);
mWindowManager = mock(WindowManager.class);
@@ -208,6 +213,54 @@ public class ScreenDecorationsTest extends SysuiTestCase {
}
@Test
+ public void testAssistHandles() {
+ mContext.getOrCreateTestableResources().addOverride(
+ com.android.internal.R.bool.config_fillMainBuiltInDisplayCutout, false);
+ mContext.getOrCreateTestableResources().addOverride(
+ com.android.internal.R.dimen.rounded_corner_radius, 0);
+ mContext.getOrCreateTestableResources().addOverride(
+ com.android.internal.R.dimen.rounded_corner_radius_top, 0);
+ mContext.getOrCreateTestableResources().addOverride(
+ com.android.internal.R.dimen.rounded_corner_radius_bottom, 0);
+ mContext.getOrCreateTestableResources()
+ .addOverride(dimen.rounded_corner_content_padding, 0);
+ when(mNavigationModeController.addListener(any())).thenReturn(
+ WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL);
+
+ mScreenDecorations.start();
+
+ // Add 2 windows for rounded corners (top and bottom).
+ verify(mWindowManager, times(2)).addView(any(), any());
+ }
+
+ @Test
+ public void testDelayedAssistHandles() {
+ mContext.getOrCreateTestableResources().addOverride(
+ com.android.internal.R.bool.config_fillMainBuiltInDisplayCutout, false);
+ mContext.getOrCreateTestableResources().addOverride(
+ com.android.internal.R.dimen.rounded_corner_radius, 0);
+ mContext.getOrCreateTestableResources().addOverride(
+ com.android.internal.R.dimen.rounded_corner_radius_top, 0);
+ mContext.getOrCreateTestableResources().addOverride(
+ com.android.internal.R.dimen.rounded_corner_radius_bottom, 0);
+ mContext.getOrCreateTestableResources()
+ .addOverride(dimen.rounded_corner_content_padding, 0);
+ when(mNavigationModeController.addListener(any())).thenReturn(
+ WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON);
+
+ mScreenDecorations.start();
+
+ // No handles and no corners
+ verify(mWindowManager, never()).addView(any(), any());
+
+ mScreenDecorations.handleNavigationModeChange(
+ WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL);
+
+ // Add 2 windows for rounded corners (top and bottom).
+ verify(mWindowManager, times(2)).addView(any(), any());
+ }
+
+ @Test
public void hasRoundedCornerOverlayFlagSet() {
assertThat(mScreenDecorations.getWindowLayoutParams().privateFlags
& PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY,
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index d0edaaaf7619..b9679133b739 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -210,6 +210,7 @@ class AlarmManagerService extends SystemService {
IAlarmListener mTimeTickTrigger;
PendingIntent mDateChangeSender;
Random mRandom;
+ PendingIntent.CancelListener mOperationCancelListener;
boolean mInteractive = true;
long mNonInteractiveStartTime;
long mNonInteractiveTime;
@@ -1497,6 +1498,7 @@ class AlarmManagerService extends SystemService {
synchronized (mLock) {
mHandler = new AlarmHandler();
+ mOperationCancelListener = (intent) -> removeImpl(intent, null);
mConstants = new Constants(mHandler);
mAppWakeupHistory = new AppWakeupHistory(Constants.DEFAULT_APP_STANDBY_WINDOW);
@@ -1748,7 +1750,9 @@ class AlarmManagerService extends SystemService {
} else {
maxElapsed = triggerElapsed + windowLength;
}
-
+ if (operation != null) {
+ operation.registerCancelListener(mOperationCancelListener);
+ }
synchronized (mLock) {
if (DEBUG_BATCH) {
Slog.v(TAG, "set(" + operation + ") : type=" + type
@@ -1761,6 +1765,8 @@ class AlarmManagerService extends SystemService {
"Maximum limit of concurrent alarms " + mConstants.MAX_ALARMS_PER_UID
+ " reached for uid: " + UserHandle.formatUid(callingUid)
+ ", callingPackage: " + callingPackage;
+ mHandler.obtainMessage(AlarmHandler.UNREGISTER_CANCEL_LISTENER,
+ operation).sendToTarget();
// STOPSHIP (b/128866264): Just to catch breakages. Remove before final release.
Slog.wtf(TAG, errorMsg);
throw new IllegalStateException(errorMsg);
@@ -1782,6 +1788,8 @@ class AlarmManagerService extends SystemService {
if (ActivityManager.getService().isAppStartModeDisabled(callingUid, callingPackage)) {
Slog.w(TAG, "Not setting alarm from " + callingUid + ":" + a
+ " -- package not allowed to start");
+ mHandler.obtainMessage(AlarmHandler.UNREGISTER_CANCEL_LISTENER,
+ operation).sendToTarget();
return;
}
} catch (RemoteException e) {
@@ -2136,10 +2144,11 @@ class AlarmManagerService extends SystemService {
Slog.w(TAG, "remove() with no intent or listener");
return;
}
-
synchronized (mLock) {
removeLocked(operation, listener);
}
+ mHandler.obtainMessage(AlarmHandler.UNREGISTER_CANCEL_LISTENER,
+ operation).sendToTarget();
}
@Override
@@ -4179,6 +4188,7 @@ class AlarmManagerService extends SystemService {
public static final int APP_STANDBY_BUCKET_CHANGED = 5;
public static final int APP_STANDBY_PAROLE_CHANGED = 6;
public static final int REMOVE_FOR_STOPPED = 7;
+ public static final int UNREGISTER_CANCEL_LISTENER = 8;
AlarmHandler() {
super(Looper.myLooper());
@@ -4261,6 +4271,13 @@ class AlarmManagerService extends SystemService {
}
break;
+ case UNREGISTER_CANCEL_LISTENER:
+ final PendingIntent pi = (PendingIntent) msg.obj;
+ if (pi != null) {
+ pi.unregisterCancelListener(mOperationCancelListener);
+ }
+ break;
+
default:
// nope, just ignore it
break;
@@ -4716,6 +4733,11 @@ class AlarmManagerService extends SystemService {
Intent.EXTRA_ALARM_COUNT, alarm.count),
mDeliveryTracker, mHandler, null,
allowWhileIdle ? mIdleOptions : null);
+ if (alarm.repeatInterval == 0) {
+ // Keep the listener for repeating alarms until they get cancelled
+ mHandler.obtainMessage(AlarmHandler.UNREGISTER_CANCEL_LISTENER,
+ alarm.operation).sendToTarget();
+ }
} catch (PendingIntent.CanceledException e) {
if (alarm.repeatInterval > 0) {
// This IntentSender is no longer valid, but this
diff --git a/services/core/java/com/android/server/content/ContentService.java b/services/core/java/com/android/server/content/ContentService.java
index 998ee1ed9ebe..7824a0ac3194 100644
--- a/services/core/java/com/android/server/content/ContentService.java
+++ b/services/core/java/com/android/server/content/ContentService.java
@@ -22,6 +22,7 @@ import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
+import android.app.AppGlobals;
import android.app.AppOpsManager;
import android.app.job.JobInfo;
import android.content.BroadcastReceiver;
@@ -56,6 +57,7 @@ import android.os.ShellCallback;
import android.os.UserHandle;
import android.text.TextUtils;
import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.Log;
import android.util.Pair;
import android.util.Slog;
@@ -63,6 +65,7 @@ import android.util.SparseArray;
import android.util.SparseIntArray;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.os.BinderDeathDispatcher;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.IndentingPrintWriter;
@@ -83,6 +86,9 @@ public final class ContentService extends IContentService.Stub {
static final String TAG = "ContentService";
static final boolean DEBUG = false;
+ /** Do a WTF if a single observer is registered more than this times. */
+ private static final int TOO_MANY_OBSERVERS_THRESHOLD = 1000;
+
public static class Lifecycle extends SystemService {
private ContentService mService;
@@ -135,6 +141,12 @@ public final class ContentService extends IContentService.Stub {
private SyncManager mSyncManager = null;
private final Object mSyncManagerLock = new Object();
+ private static final BinderDeathDispatcher<IContentObserver> sObserverDeathDispatcher =
+ new BinderDeathDispatcher<>();
+
+ @GuardedBy("sObserverLeakDetectedUid")
+ private static final ArraySet<Integer> sObserverLeakDetectedUid = new ArraySet<>(0);
+
/**
* Map from userId to providerPackageName to [clientPackageName, uri] to
* value. This structure is carefully optimized to keep invalidation logic
@@ -236,6 +248,13 @@ public final class ContentService extends IContentService.Stub {
pw.println();
pw.print(" Total number of nodes: "); pw.println(counts[0]);
pw.print(" Total number of observers: "); pw.println(counts[1]);
+
+ sObserverDeathDispatcher.dump(pw, " ");
+ }
+ synchronized (sObserverLeakDetectedUid) {
+ pw.println();
+ pw.print("Observer leaking UIDs: ");
+ pw.println(sObserverLeakDetectedUid.toString());
}
synchronized (mCache) {
@@ -1345,18 +1364,40 @@ public final class ContentService extends IContentService.Stub {
private final Object observersLock;
public ObserverEntry(IContentObserver o, boolean n, Object observersLock,
- int _uid, int _pid, int _userHandle) {
+ int _uid, int _pid, int _userHandle, Uri uri) {
this.observersLock = observersLock;
observer = o;
uid = _uid;
pid = _pid;
userHandle = _userHandle;
notifyForDescendants = n;
- try {
- observer.asBinder().linkToDeath(this, 0);
- } catch (RemoteException e) {
+
+ final int entries = sObserverDeathDispatcher.linkToDeath(observer, this);
+ if (entries == -1) {
binderDied();
+ } else if (entries == TOO_MANY_OBSERVERS_THRESHOLD) {
+ boolean alreadyDetected;
+
+ synchronized (sObserverLeakDetectedUid) {
+ alreadyDetected = sObserverLeakDetectedUid.contains(uid);
+ if (!alreadyDetected) {
+ sObserverLeakDetectedUid.add(uid);
+ }
+ }
+ if (!alreadyDetected) {
+ String caller = null;
+ try {
+ caller = ArrayUtils.firstOrNull(AppGlobals.getPackageManager()
+ .getPackagesForUid(uid));
+ } catch (RemoteException ignore) {
+ }
+ Slog.wtf(TAG, "Observer registered too many times. Leak? cpid=" + pid
+ + " cuid=" + uid
+ + " cpkg=" + caller
+ + " url=" + uri);
+ }
}
+
}
@Override
@@ -1454,7 +1495,7 @@ public final class ContentService extends IContentService.Stub {
// If this is the leaf node add the observer
if (index == countUriSegments(uri)) {
mObservers.add(new ObserverEntry(observer, notifyForDescendants, observersLock,
- uid, pid, userHandle));
+ uid, pid, userHandle, uri));
return;
}
@@ -1498,7 +1539,7 @@ public final class ContentService extends IContentService.Stub {
if (entry.observer.asBinder() == observerBinder) {
mObservers.remove(i);
// We no longer need to listen for death notifications. Remove it.
- observerBinder.unlinkToDeath(entry, 0);
+ sObserverDeathDispatcher.unlinkToDeath(observer, entry);
break;
}
}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index bd8859401856..0032e9a8ea51 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -617,7 +617,9 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
mStagingManager.createSession(session);
}
- mCallbacks.notifySessionCreated(session.sessionId, session.userId);
+ if ((session.params.installFlags & PackageManager.INSTALL_DRY_RUN) == 0) {
+ mCallbacks.notifySessionCreated(session.sessionId, session.userId);
+ }
writeSessionsAsync();
return sessionId;
}
@@ -1210,16 +1212,25 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
class InternalCallback {
public void onSessionBadgingChanged(PackageInstallerSession session) {
- mCallbacks.notifySessionBadgingChanged(session.sessionId, session.userId);
+ if ((session.params.installFlags & PackageManager.INSTALL_DRY_RUN) == 0) {
+ mCallbacks.notifySessionBadgingChanged(session.sessionId, session.userId);
+ }
+
writeSessionsAsync();
}
public void onSessionActiveChanged(PackageInstallerSession session, boolean active) {
- mCallbacks.notifySessionActiveChanged(session.sessionId, session.userId, active);
+ if ((session.params.installFlags & PackageManager.INSTALL_DRY_RUN) == 0) {
+ mCallbacks.notifySessionActiveChanged(session.sessionId, session.userId,
+ active);
+ }
}
public void onSessionProgressChanged(PackageInstallerSession session, float progress) {
- mCallbacks.notifySessionProgressChanged(session.sessionId, session.userId, progress);
+ if ((session.params.installFlags & PackageManager.INSTALL_DRY_RUN) == 0) {
+ mCallbacks.notifySessionProgressChanged(session.sessionId, session.userId,
+ progress);
+ }
}
public void onStagedSessionChanged(PackageInstallerSession session) {
@@ -1232,7 +1243,9 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
}
public void onSessionFinished(final PackageInstallerSession session, boolean success) {
- mCallbacks.notifySessionFinished(session.sessionId, session.userId, success);
+ if ((session.params.installFlags & PackageManager.INSTALL_DRY_RUN) == 0) {
+ mCallbacks.notifySessionFinished(session.sessionId, session.userId, success);
+ }
mInstallHandler.post(new Runnable() {
@Override
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index 8e05f7405be7..5ba47d8433cc 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -2639,7 +2639,7 @@ class ActivityStack extends ConfigurationContainer {
if (!hasRunningActivity) {
// There are no activities left in the stack, let's look somewhere else.
- return resumeTopActivityInNextFocusableStack(prev, options, "noMoreActivities");
+ return resumeNextFocusableActivityWhenStackIsEmpty(prev, options);
}
next.delayedResume = false;
@@ -3040,21 +3040,33 @@ class ActivityStack extends ConfigurationContainer {
return true;
}
- private boolean resumeTopActivityInNextFocusableStack(ActivityRecord prev,
- ActivityOptions options, String reason) {
- final ActivityStack nextFocusedStack = adjustFocusToNextFocusableStack(reason);
- if (nextFocusedStack != null) {
- // Try to move focus to the next visible stack with a running activity if this
- // stack is not covering the entire screen or is on a secondary display (with no home
- // stack).
- return mRootActivityContainer.resumeFocusedStacksTopActivities(nextFocusedStack, prev,
- null /* targetOptions */);
+ /**
+ * Resume the next eligible activity in a focusable stack when this one does not have any
+ * running activities left. The focus will be adjusted to the next focusable stack and
+ * top running activities will be resumed in all focusable stacks. However, if the current stack
+ * is a home stack - we have to keep it focused, start and resume a home activity on the current
+ * display instead to make sure that the display is not empty.
+ */
+ private boolean resumeNextFocusableActivityWhenStackIsEmpty(ActivityRecord prev,
+ ActivityOptions options) {
+ final String reason = "noMoreActivities";
+
+ if (!isActivityTypeHome()) {
+ final ActivityStack nextFocusedStack = adjustFocusToNextFocusableStack(reason);
+ if (nextFocusedStack != null) {
+ // Try to move focus to the next visible stack with a running activity if this
+ // stack is not covering the entire screen or is on a secondary display with no home
+ // stack.
+ return mRootActivityContainer.resumeFocusedStacksTopActivities(nextFocusedStack,
+ prev, null /* targetOptions */);
+ }
}
- // Let's just start up the Launcher...
+ // If the current stack is a home stack, or if focus didn't switch to a different stack -
+ // just start up the Launcher...
ActivityOptions.abort(options);
if (DEBUG_STATES) Slog.d(TAG_STATES,
- "resumeTopActivityInNextFocusableStack: " + reason + ", go home");
+ "resumeNextFocusableActivityWhenStackIsEmpty: " + reason + ", go home");
return mRootActivityContainer.resumeHomeActivity(prev, reason, mDisplayId);
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java
index cdcdf9195ad5..1e29ed6ba5f3 100644
--- a/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java
@@ -1032,6 +1032,16 @@ public class AlarmManagerServiceTest {
assertEquals(-1, mService.mAlarmsPerUid.get(TEST_CALLING_UID, -1));
}
+ @Test
+ public void alarmCountOnPendingIntentCancel() {
+ final PendingIntent pi = getNewMockPendingIntent();
+ setTestAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 123, pi);
+ verify(pi).registerCancelListener(mService.mOperationCancelListener);
+ assertEquals(1, mService.mAlarmsPerUid.get(TEST_CALLING_UID));
+ mService.mOperationCancelListener.onCancelled(pi);
+ assertEquals(0, mService.mAlarmsPerUid.get(TEST_CALLING_UID));
+ }
+
@After
public void tearDown() {
if (mMockingSession != null) {
diff --git a/services/tests/servicestests/src/com/android/server/content/ObserverNodeTest.java b/services/tests/servicestests/src/com/android/server/content/ObserverNodeTest.java
index 62b0ca805c1b..891ca74a545f 100644
--- a/services/tests/servicestests/src/com/android/server/content/ObserverNodeTest.java
+++ b/services/tests/servicestests/src/com/android/server/content/ObserverNodeTest.java
@@ -30,7 +30,7 @@ import com.android.server.content.ContentService.ObserverCall;
import com.android.server.content.ContentService.ObserverNode;
/**
- * bit FrameworksServicesTests:com.android.server.content.ObserverNodeTest
+ * atest FrameworksServicesTests:com.android.server.content.ObserverNodeTest
*/
@SmallTest
public class ObserverNodeTest extends AndroidTestCase {
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 c77e25fabf19..8d2c3dd80538 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
@@ -16,6 +16,7 @@
package com.android.server.wm;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
@@ -35,6 +36,7 @@ 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;
import static com.android.server.wm.ActivityStack.REMOVE_TASK_MODE_DESTROYING;
+import static com.android.server.wm.ActivityStackSupervisor.ON_TOP;
import static com.android.server.wm.RootActivityContainer.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE;
import static org.junit.Assert.assertEquals;
@@ -397,6 +399,58 @@ public class RootActivityContainerTests extends ActivityTestsBase {
}
/**
+ * Verify that home activity will be started on a display even if another display has a
+ * focusable activity.
+ */
+ @Test
+ public void testResumeFocusedStacksStartsHomeActivity_NoActivities() {
+ mFullscreenStack.remove();
+ mService.mRootActivityContainer.getActivityDisplay(DEFAULT_DISPLAY).getHomeStack().remove();
+ mService.mRootActivityContainer.getActivityDisplay(DEFAULT_DISPLAY)
+ .createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP);
+
+ doReturn(true).when(mRootActivityContainer).resumeHomeActivity(any(), any(), anyInt());
+
+ mService.setBooted(true);
+
+ // Trigger resume on all displays
+ mRootActivityContainer.resumeFocusedStacksTopActivities();
+
+ // Verify that home activity was started on the default display
+ verify(mRootActivityContainer).resumeHomeActivity(any(), any(), eq(DEFAULT_DISPLAY));
+ }
+
+ /**
+ * Verify that home activity will be started on a display even if another display has a
+ * focusable activity.
+ */
+ @Test
+ public void testResumeFocusedStacksStartsHomeActivity_ActivityOnSecondaryScreen() {
+ mFullscreenStack.remove();
+ mService.mRootActivityContainer.getActivityDisplay(DEFAULT_DISPLAY).getHomeStack().remove();
+ mService.mRootActivityContainer.getActivityDisplay(DEFAULT_DISPLAY)
+ .createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP);
+
+ // Create an activity on secondary display.
+ final TestActivityDisplay secondDisplay = addNewActivityDisplayAt(
+ ActivityDisplay.POSITION_TOP);
+ final ActivityStack stack = secondDisplay.createStack(WINDOWING_MODE_FULLSCREEN,
+ ACTIVITY_TYPE_STANDARD, true /* onTop */);
+ final TaskRecord task = new TaskBuilder(mSupervisor).setStack(stack).build();
+ new ActivityBuilder(mService).setTask(task).build();
+
+ doReturn(true).when(mRootActivityContainer).resumeHomeActivity(any(), any(), anyInt());
+
+ mService.setBooted(true);
+
+ // Trigger resume on all displays
+ mRootActivityContainer.resumeFocusedStacksTopActivities();
+
+ // Verify that home activity was started on the default display
+ verify(mRootActivityContainer).resumeHomeActivity(any(), any(), eq(DEFAULT_DISPLAY));
+ }
+
+ /**
* Verify that a lingering transition is being executed in case the activity to be resumed is
* already resumed
*/
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index d6011b946139..b7b511e5eb4b 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -1498,6 +1498,48 @@ public class TelephonyManager {
*/
public static final int EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_ALL = 4;
+ /**
+ * Integer intent extra to be used with {@link #ACTION_PRIMARY_SUBSCRIPTION_LIST_CHANGED}
+ * to indicate if the SIM combination in DSDS has limitation or compatible issue.
+ * e.g. two CDMA SIMs may disrupt each other's voice call in certain scenarios.
+ *
+ * @hide
+ */
+ public static final String EXTRA_SIM_COMBINATION_WARNING_TYPE =
+ "android.telephony.extra.SIM_COMBINATION_WARNING_TYPE";
+
+ /** @hide */
+ @IntDef({
+ EXTRA_SIM_COMBINATION_WARNING_TYPE_NONE,
+ EXTRA_SIM_COMBINATION_WARNING_TYPE_DUAL_CDMA
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface SimCombinationWarningType{}
+
+ /**
+ * Used as an int value for {@link #EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE}
+ * to indicate there's no SIM combination warning.
+ * @hide
+ */
+ public static final int EXTRA_SIM_COMBINATION_WARNING_TYPE_NONE = 0;
+
+ /**
+ * Used as an int value for {@link #EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE}
+ * to indicate two active SIMs are both CDMA hence there might be functional limitation.
+ * @hide
+ */
+ public static final int EXTRA_SIM_COMBINATION_WARNING_TYPE_DUAL_CDMA = 1;
+
+ /**
+ * String intent extra to be used with {@link #ACTION_PRIMARY_SUBSCRIPTION_LIST_CHANGED}
+ * to indicate what's the name of SIM combination it has limitation or compatible issue.
+ * e.g. two CDMA SIMs may disrupt each other's voice call in certain scenarios, and the
+ * name will be "operator1 & operator2".
+ *
+ * @hide
+ */
+ public static final String EXTRA_SIM_COMBINATION_NAMES =
+ "android.telephony.extra.SIM_COMBINATION_NAMES";
//
//
// Device Info