summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmds/statsd/Android.bp2
-rw-r--r--cmds/statsd/src/condition/StateConditionTracker.cpp207
-rw-r--r--cmds/statsd/src/condition/StateConditionTracker.h117
-rw-r--r--cmds/statsd/src/metrics/metrics_manager_util.cpp56
-rw-r--r--cmds/statsd/src/metrics/metrics_manager_util.h2
-rw-r--r--cmds/statsd/tests/condition/StateConditionTracker_test.cpp113
-rw-r--r--cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/Utils.java49
-rw-r--r--cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/localdrive/LocalDrive.java106
-rw-r--r--cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java62
-rw-r--r--core/java/android/hardware/display/DisplayManager.java30
-rw-r--r--core/java/android/hardware/display/DisplayManagerGlobal.java26
-rw-r--r--core/java/android/hardware/display/IDisplayManager.aidl7
-rw-r--r--core/java/android/hardware/display/VirtualDisplayConfig.aidl19
-rw-r--r--core/java/android/hardware/display/VirtualDisplayConfig.java491
-rw-r--r--core/java/android/view/ViewRootImpl.java5
-rw-r--r--core/java/android/view/WindowManagerImpl.java4
-rw-r--r--core/java/com/android/internal/policy/DecorContext.java52
-rw-r--r--core/java/com/android/internal/policy/DividerSnapAlgorithm.java16
-rw-r--r--core/java/com/android/internal/widget/ConversationLayout.java3
-rw-r--r--core/res/res/values/config.xml4
-rw-r--r--core/res/res/values/symbols.xml1
-rw-r--r--core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java50
-rw-r--r--core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java50
-rw-r--r--core/tests/coretests/src/com/android/internal/policy/DecorContextTest.java32
-rw-r--r--media/java/android/media/projection/MediaProjection.java50
-rw-r--r--packages/SystemUI/res/layout/bubble_overflow_activity.xml2
-rw-r--r--packages/SystemUI/res/layout/bubble_overflow_view.xml1
-rw-r--r--packages/SystemUI/res/values/dimens.xml6
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/DynamicChildBindController.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.java60
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionListener.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpBindController.java95
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpViewBinder.java112
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationAlertingManager.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java46
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifBindPipeline.java71
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifBindPipelineLogger.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java50
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java22
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindParams.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/OnHeadsUpChangedListener.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/ExpandHelperTest.java7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/AboveShelfObserverTest.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/DynamicChildBindControllerTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.java39
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java30
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java18
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotifBindPipelineTest.java9
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManagerTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java21
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/RowContentBindStageTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapperTest.java7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapperTest.java7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapperTest.java7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java11
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java20
-rw-r--r--services/autofill/java/com/android/server/autofill/Session.java67
-rw-r--r--services/core/java/com/android/server/display/DisplayDevice.java8
-rw-r--r--services/core/java/com/android/server/display/DisplayManagerService.java54
-rw-r--r--services/core/java/com/android/server/display/VirtualDisplayAdapter.java48
-rw-r--r--services/core/java/com/android/server/vr/Vr2dDisplay.java9
-rw-r--r--services/core/java/com/android/server/wm/TaskDisplayArea.java22
-rw-r--r--services/core/java/com/android/server/wm/TaskOrganizerController.java6
-rw-r--r--services/core/java/com/android/server/wm/WindowStateAnimator.java18
-rw-r--r--services/core/java/com/android/server/wm/WindowSurfaceController.java1
-rw-r--r--services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java107
-rw-r--r--telephony/java/android/telephony/CellIdentityLte.java6
-rw-r--r--telephony/java/android/telephony/CellIdentityTdscdma.java7
-rw-r--r--telephony/java/android/telephony/CellIdentityWcdma.java6
91 files changed, 1816 insertions, 937 deletions
diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp
index 20f9e76b4436..d3d7e1d483e8 100644
--- a/cmds/statsd/Android.bp
+++ b/cmds/statsd/Android.bp
@@ -56,7 +56,6 @@ cc_defaults {
"src/condition/condition_util.cpp",
"src/condition/ConditionWizard.cpp",
"src/condition/SimpleConditionTracker.cpp",
- "src/condition/StateConditionTracker.cpp",
"src/config/ConfigKey.cpp",
"src/config/ConfigListener.cpp",
"src/config/ConfigManager.cpp",
@@ -315,7 +314,6 @@ cc_test {
"tests/condition/CombinationConditionTracker_test.cpp",
"tests/condition/ConditionTimer_test.cpp",
"tests/condition/SimpleConditionTracker_test.cpp",
- "tests/condition/StateConditionTracker_test.cpp",
"tests/ConfigManager_test.cpp",
"tests/e2e/Alarm_e2e_test.cpp",
"tests/e2e/Anomaly_count_e2e_test.cpp",
diff --git a/cmds/statsd/src/condition/StateConditionTracker.cpp b/cmds/statsd/src/condition/StateConditionTracker.cpp
deleted file mode 100644
index d19a1761ac00..000000000000
--- a/cmds/statsd/src/condition/StateConditionTracker.cpp
+++ /dev/null
@@ -1,207 +0,0 @@
-/*
- * Copyright 2018, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#define DEBUG false // STOPSHIP if true
-#include "Log.h"
-
-#include "StateConditionTracker.h"
-#include "guardrail/StatsdStats.h"
-
-namespace android {
-namespace os {
-namespace statsd {
-
-using std::vector;
-
-StateConditionTracker::StateConditionTracker(const ConfigKey& key, const int64_t& id, const int index,
- const SimplePredicate& simplePredicate,
- const unordered_map<int64_t, int>& trackerNameIndexMap,
- const vector<Matcher> primaryKeys)
- : ConditionTracker(id, index), mConfigKey(key), mPrimaryKeys(primaryKeys) {
- if (simplePredicate.has_start()) {
- auto pair = trackerNameIndexMap.find(simplePredicate.start());
- if (pair == trackerNameIndexMap.end()) {
- ALOGW("Start matcher %lld not found in the config", (long long)simplePredicate.start());
- return;
- }
- mStartLogMatcherIndex = pair->second;
- mTrackerIndex.insert(mStartLogMatcherIndex);
- } else {
- ALOGW("Condition %lld must have a start matcher", (long long)id);
- return;
- }
-
- if (simplePredicate.has_dimensions()) {
- translateFieldMatcher(simplePredicate.dimensions(), &mOutputDimensions);
- if (mOutputDimensions.size() > 0) {
- mSliced = true;
- mDimensionTag = mOutputDimensions[0].mMatcher.getTag();
- } else {
- ALOGW("Condition %lld has invalid dimensions", (long long)id);
- return;
- }
- } else {
- ALOGW("Condition %lld being a state tracker, but has no dimension", (long long)id);
- return;
- }
-
- if (simplePredicate.initial_value() == SimplePredicate_InitialValue_FALSE) {
- mInitialValue = ConditionState::kFalse;
- } else {
- mInitialValue = ConditionState::kUnknown;
- }
-
- mNonSlicedConditionState = mInitialValue;
- mInitialized = true;
-}
-
-StateConditionTracker::~StateConditionTracker() {
- VLOG("~StateConditionTracker()");
-}
-
-bool StateConditionTracker::init(const vector<Predicate>& allConditionConfig,
- const vector<sp<ConditionTracker>>& allConditionTrackers,
- const unordered_map<int64_t, int>& conditionIdIndexMap,
- vector<bool>& stack) {
- return mInitialized;
-}
-
-void StateConditionTracker::dumpState() {
- VLOG("StateConditionTracker %lld DUMP:", (long long)mConditionId);
- for (const auto& value : mSlicedState) {
- VLOG("\t%s -> %s", value.first.toString().c_str(), value.second.toString().c_str());
- }
- VLOG("Last Changed to True: ");
- for (const auto& value : mLastChangedToTrueDimensions) {
- VLOG("%s", value.toString().c_str());
- }
- VLOG("Last Changed to False: ");
- for (const auto& value : mLastChangedToFalseDimensions) {
- VLOG("%s", value.toString().c_str());
- }
-}
-
-bool StateConditionTracker::hitGuardRail(const HashableDimensionKey& newKey) {
- if (mSlicedState.find(newKey) != mSlicedState.end()) {
- // if the condition is not sliced or the key is not new, we are good!
- return false;
- }
- // 1. Report the tuple count if the tuple count > soft limit
- if (mSlicedState.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) {
- size_t newTupleCount = mSlicedState.size() + 1;
- StatsdStats::getInstance().noteConditionDimensionSize(mConfigKey, mConditionId, newTupleCount);
- // 2. Don't add more tuples, we are above the allowed threshold. Drop the data.
- if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) {
- ALOGE("Predicate %lld dropping data for dimension key %s",
- (long long)mConditionId, newKey.toString().c_str());
- return true;
- }
- }
- return false;
-}
-
-void StateConditionTracker::evaluateCondition(const LogEvent& event,
- const vector<MatchingState>& eventMatcherValues,
- const vector<sp<ConditionTracker>>& mAllConditions,
- vector<ConditionState>& conditionCache,
- vector<bool>& conditionChangedCache) {
- mLastChangedToTrueDimensions.clear();
- mLastChangedToFalseDimensions.clear();
- if (conditionCache[mIndex] != ConditionState::kNotEvaluated) {
- // it has been evaluated.
- VLOG("Yes, already evaluated, %lld %d", (long long)mConditionId, conditionCache[mIndex]);
- return;
- }
-
- if (mStartLogMatcherIndex >= 0 &&
- eventMatcherValues[mStartLogMatcherIndex] != MatchingState::kMatched) {
- conditionCache[mIndex] =
- mSlicedState.size() > 0 ? ConditionState::kTrue : ConditionState::kFalse;
- conditionChangedCache[mIndex] = false;
- return;
- }
-
- VLOG("StateConditionTracker evaluate event %s", event.ToString().c_str());
-
- // Primary key can exclusive fields must be simple fields. so there won't be more than
- // one keys matched.
- HashableDimensionKey primaryKey;
- HashableDimensionKey state;
- if ((mPrimaryKeys.size() > 0 && !filterValues(mPrimaryKeys, event.getValues(), &primaryKey)) ||
- !filterValues(mOutputDimensions, event.getValues(), &state)) {
- ALOGE("Failed to filter fields in the event?? panic now!");
- conditionCache[mIndex] =
- mSlicedState.size() > 0 ? ConditionState::kTrue : ConditionState::kFalse;
- conditionChangedCache[mIndex] = false;
- return;
- }
- hitGuardRail(primaryKey);
-
- VLOG("StateConditionTracker: key %s state %s", primaryKey.toString().c_str(), state.toString().c_str());
-
- auto it = mSlicedState.find(primaryKey);
- if (it == mSlicedState.end()) {
- mSlicedState[primaryKey] = state;
- conditionCache[mIndex] = ConditionState::kTrue;
- mLastChangedToTrueDimensions.insert(state);
- conditionChangedCache[mIndex] = true;
- } else if (!(it->second == state)) {
- mLastChangedToFalseDimensions.insert(it->second);
- mLastChangedToTrueDimensions.insert(state);
- mSlicedState[primaryKey] = state;
- conditionCache[mIndex] = ConditionState::kTrue;
- conditionChangedCache[mIndex] = true;
- } else {
- conditionCache[mIndex] = ConditionState::kTrue;
- conditionChangedCache[mIndex] = false;
- }
-
- if (DEBUG) {
- dumpState();
- }
- return;
-}
-
-void StateConditionTracker::isConditionMet(
- const ConditionKey& conditionParameters, const vector<sp<ConditionTracker>>& allConditions,
- const bool isPartialLink,
- vector<ConditionState>& conditionCache) const {
- if (conditionCache[mIndex] != ConditionState::kNotEvaluated) {
- // it has been evaluated.
- VLOG("Yes, already evaluated, %lld %d", (long long)mConditionId, conditionCache[mIndex]);
- return;
- }
-
- const auto pair = conditionParameters.find(mConditionId);
- if (pair == conditionParameters.end()) {
- if (mSlicedState.size() > 0) {
- conditionCache[mIndex] = ConditionState::kTrue;
- } else {
- conditionCache[mIndex] = ConditionState::kUnknown;
- }
- return;
- }
-
- const auto& primaryKey = pair->second;
- conditionCache[mIndex] = mInitialValue;
- auto it = mSlicedState.find(primaryKey);
- if (it != mSlicedState.end()) {
- conditionCache[mIndex] = ConditionState::kTrue;
- }
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/condition/StateConditionTracker.h b/cmds/statsd/src/condition/StateConditionTracker.h
deleted file mode 100644
index 0efe1fb3fcb2..000000000000
--- a/cmds/statsd/src/condition/StateConditionTracker.h
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * Copyright 2018, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#pragma once
-
-#include <gtest/gtest_prod.h>
-#include "ConditionTracker.h"
-#include "config/ConfigKey.h"
-#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
-#include "stats_util.h"
-
-namespace android {
-namespace os {
-namespace statsd {
-
-class StateConditionTracker : public virtual ConditionTracker {
-public:
- StateConditionTracker(const ConfigKey& key, const int64_t& id, const int index,
- const SimplePredicate& simplePredicate,
- const std::unordered_map<int64_t, int>& trackerNameIndexMap,
- const vector<Matcher> primaryKeys);
-
- ~StateConditionTracker();
-
- bool init(const std::vector<Predicate>& allConditionConfig,
- const std::vector<sp<ConditionTracker>>& allConditionTrackers,
- const std::unordered_map<int64_t, int>& conditionIdIndexMap,
- std::vector<bool>& stack) override;
-
- void evaluateCondition(const LogEvent& event,
- const std::vector<MatchingState>& eventMatcherValues,
- const std::vector<sp<ConditionTracker>>& mAllConditions,
- std::vector<ConditionState>& conditionCache,
- std::vector<bool>& changedCache) override;
-
- /**
- * Note: dimensionFields will be ignored in StateConditionTracker, because we demand metrics
- * must take the entire dimension fields from StateConditionTracker. This is to make implementation
- * simple and efficient.
- *
- * For example: wakelock duration by uid process states:
- * dimension in condition must be {uid, process state}.
- */
- void isConditionMet(const ConditionKey& conditionParameters,
- const std::vector<sp<ConditionTracker>>& allConditions,
- const bool isPartialLink,
- std::vector<ConditionState>& conditionCache) const override;
-
- virtual const std::set<HashableDimensionKey>* getChangedToTrueDimensions(
- const std::vector<sp<ConditionTracker>>& allConditions) const {
- return &mLastChangedToTrueDimensions;
- }
-
- virtual const std::set<HashableDimensionKey>* getChangedToFalseDimensions(
- const std::vector<sp<ConditionTracker>>& allConditions) const {
- return &mLastChangedToFalseDimensions;
- }
-
- bool IsChangedDimensionTrackable() const override { return true; }
-
- bool IsSimpleCondition() const override { return true; }
-
- bool equalOutputDimensions(
- const std::vector<sp<ConditionTracker>>& allConditions,
- const vector<Matcher>& dimensions) const override {
- return equalDimensions(mOutputDimensions, dimensions);
- }
-
- void getTrueSlicedDimensions(
- const std::vector<sp<ConditionTracker>>& allConditions,
- std::set<HashableDimensionKey>* dimensions) const override {
- for (const auto& itr : mSlicedState) {
- dimensions->insert(itr.second);
- }
- }
-
-private:
- const ConfigKey mConfigKey;
-
- // The index of the LogEventMatcher which defines the start.
- int mStartLogMatcherIndex;
-
- std::set<HashableDimensionKey> mLastChangedToTrueDimensions;
- std::set<HashableDimensionKey> mLastChangedToFalseDimensions;
-
- std::vector<Matcher> mOutputDimensions;
- std::vector<Matcher> mPrimaryKeys;
-
- ConditionState mInitialValue;
-
- int mDimensionTag;
-
- void dumpState();
-
- bool hitGuardRail(const HashableDimensionKey& newKey);
-
- // maps from [primary_key] to [primary_key, exclusive_state].
- std::unordered_map<HashableDimensionKey, HashableDimensionKey> mSlicedState;
-
- FRIEND_TEST(StateConditionTrackerTest, TestStateChange);
-};
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.cpp b/cmds/statsd/src/metrics/metrics_manager_util.cpp
index 0d0788e05e0a..2fcb13b709f9 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.cpp
+++ b/cmds/statsd/src/metrics/metrics_manager_util.cpp
@@ -26,7 +26,6 @@
#include "MetricProducer.h"
#include "condition/CombinationConditionTracker.h"
#include "condition/SimpleConditionTracker.h"
-#include "condition/StateConditionTracker.h"
#include "external/StatsPullerManager.h"
#include "matchers/CombinationLogMatchingTracker.h"
#include "matchers/EventMatcherWizard.h"
@@ -283,49 +282,6 @@ bool initLogTrackers(const StatsdConfig& config, const UidMap& uidMap,
return true;
}
-/**
- * A StateConditionTracker is built from a SimplePredicate which has only "start", and no "stop"
- * or "stop_all". The start must be an atom matcher that matches a state atom. It must
- * have dimension, the dimension must be the state atom's primary fields plus exclusive state
- * field. For example, the StateConditionTracker is used in tracking UidProcessState and ScreenState.
- *
- */
-bool isStateConditionTracker(const SimplePredicate& simplePredicate, vector<Matcher>* primaryKeys) {
- // 1. must not have "stop". must have "dimension"
- if (!simplePredicate.has_stop() && simplePredicate.has_dimensions()) {
- auto it = android::util::AtomsInfo::kStateAtomsFieldOptions.find(
- simplePredicate.dimensions().field());
- // 2. must be based on a state atom.
- if (it != android::util::AtomsInfo::kStateAtomsFieldOptions.end()) {
- // 3. dimension must be primary fields + state field IN ORDER
- size_t expectedDimensionCount = it->second.primaryFields.size() + 1;
- vector<Matcher> dimensions;
- translateFieldMatcher(simplePredicate.dimensions(), &dimensions);
- if (dimensions.size() != expectedDimensionCount) {
- return false;
- }
- // 3.1 check the primary fields first.
- size_t index = 0;
- for (const auto& field : it->second.primaryFields) {
- Matcher matcher = getSimpleMatcher(it->first, field);
- if (!(matcher == dimensions[index])) {
- return false;
- }
- primaryKeys->push_back(matcher);
- index++;
- }
- Matcher stateFieldMatcher =
- getSimpleMatcher(it->first, it->second.exclusiveField);
- // 3.2 last dimension should be the exclusive field.
- if (!(dimensions.back() == stateFieldMatcher)) {
- return false;
- }
- return true;
- }
- }
- return false;
-} // namespace statsd
-
bool initConditions(const ConfigKey& key, const StatsdConfig& config,
const unordered_map<int64_t, int>& logTrackerMap,
unordered_map<int64_t, int>& conditionTrackerMap,
@@ -341,16 +297,8 @@ bool initConditions(const ConfigKey& key, const StatsdConfig& config,
int index = allConditionTrackers.size();
switch (condition.contents_case()) {
case Predicate::ContentsCase::kSimplePredicate: {
- vector<Matcher> primaryKeys;
- if (isStateConditionTracker(condition.simple_predicate(), &primaryKeys)) {
- allConditionTrackers.push_back(new StateConditionTracker(key, condition.id(), index,
- condition.simple_predicate(),
- logTrackerMap, primaryKeys));
- } else {
- allConditionTrackers.push_back(new SimpleConditionTracker(
- key, condition.id(), index, condition.simple_predicate(),
- logTrackerMap));
- }
+ allConditionTrackers.push_back(new SimpleConditionTracker(
+ key, condition.id(), index, condition.simple_predicate(), logTrackerMap));
break;
}
case Predicate::ContentsCase::kCombination: {
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.h b/cmds/statsd/src/metrics/metrics_manager_util.h
index a8ccc6289b9a..6af7a9adca20 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.h
+++ b/cmds/statsd/src/metrics/metrics_manager_util.h
@@ -132,8 +132,6 @@ bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, UidMap&
vector<int>& metricsWithActivation,
std::set<int64_t>& noReportMetricIds);
-bool isStateConditionTracker(const SimplePredicate& simplePredicate, std::vector<Matcher>* primaryKeys);
-
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/cmds/statsd/tests/condition/StateConditionTracker_test.cpp b/cmds/statsd/tests/condition/StateConditionTracker_test.cpp
deleted file mode 100644
index 86b50ae82ff4..000000000000
--- a/cmds/statsd/tests/condition/StateConditionTracker_test.cpp
+++ /dev/null
@@ -1,113 +0,0 @@
-// Copyright (C) 2017 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "src/condition/StateConditionTracker.h"
-#include "tests/statsd_test_util.h"
-
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-#include <stdio.h>
-#include <numeric>
-#include <vector>
-
-using std::map;
-using std::unordered_map;
-using std::vector;
-
-#ifdef __ANDROID__
-namespace android {
-namespace os {
-namespace statsd {
-
-const int kUidProcTag = 27;
-
-SimplePredicate getUidProcStatePredicate() {
- SimplePredicate simplePredicate;
- simplePredicate.set_start(StringToId("UidProcState"));
-
- simplePredicate.mutable_dimensions()->set_field(kUidProcTag);
- simplePredicate.mutable_dimensions()->add_child()->set_field(1);
- simplePredicate.mutable_dimensions()->add_child()->set_field(2);
-
- simplePredicate.set_count_nesting(false);
- return simplePredicate;
-}
-
-// TODO(b/149590301): Update these tests to use new socket schema.
-//void makeUidProcStateEvent(int32_t uid, int32_t state, LogEvent* event) {
-// event->write(uid);
-// event->write(state);
-// event->init();
-//}
-//
-//TEST(StateConditionTrackerTest, TestStateChange) {
-// int uid1 = 111;
-// int uid2 = 222;
-//
-// int state1 = 1001;
-// int state2 = 1002;
-// unordered_map<int64_t, int> trackerNameIndexMap;
-// trackerNameIndexMap[StringToId("UidProcState")] = 0;
-// vector<Matcher> primaryFields;
-// primaryFields.push_back(getSimpleMatcher(kUidProcTag, 1));
-// StateConditionTracker tracker(ConfigKey(12, 123), 123, 0, getUidProcStatePredicate(),
-// trackerNameIndexMap, primaryFields);
-//
-// LogEvent event(kUidProcTag, 0 /*timestamp*/);
-// makeUidProcStateEvent(uid1, state1, &event);
-//
-// vector<MatchingState> matcherState;
-// matcherState.push_back(MatchingState::kMatched);
-// vector<sp<ConditionTracker>> allPredicates;
-// vector<ConditionState> conditionCache(1, ConditionState::kNotEvaluated);
-// vector<bool> changedCache(1, false);
-//
-// tracker.evaluateCondition(event, matcherState, allPredicates, conditionCache, changedCache);
-// EXPECT_EQ(1ULL, tracker.mLastChangedToTrueDimensions.size());
-// EXPECT_EQ(0ULL, tracker.mLastChangedToFalseDimensions.size());
-// EXPECT_TRUE(changedCache[0]);
-//
-// changedCache[0] = false;
-// conditionCache[0] = ConditionState::kNotEvaluated;
-// tracker.evaluateCondition(event, matcherState, allPredicates, conditionCache, changedCache);
-// EXPECT_EQ(0ULL, tracker.mLastChangedToTrueDimensions.size());
-// EXPECT_EQ(0ULL, tracker.mLastChangedToFalseDimensions.size());
-// EXPECT_FALSE(changedCache[0]);
-//
-// LogEvent event2(kUidProcTag, 0 /*timestamp*/);
-// makeUidProcStateEvent(uid1, state2, &event2);
-//
-// changedCache[0] = false;
-// conditionCache[0] = ConditionState::kNotEvaluated;
-// tracker.evaluateCondition(event2, matcherState, allPredicates, conditionCache, changedCache);
-// EXPECT_EQ(1ULL, tracker.mLastChangedToTrueDimensions.size());
-// EXPECT_EQ(1ULL, tracker.mLastChangedToFalseDimensions.size());
-// EXPECT_TRUE(changedCache[0]);
-//
-// LogEvent event3(kUidProcTag, 0 /*timestamp*/);
-// makeUidProcStateEvent(uid2, state1, &event3);
-// changedCache[0] = false;
-// conditionCache[0] = ConditionState::kNotEvaluated;
-// tracker.evaluateCondition(event3, matcherState, allPredicates, conditionCache, changedCache);
-// EXPECT_EQ(1ULL, tracker.mLastChangedToTrueDimensions.size());
-// EXPECT_EQ(0ULL, tracker.mLastChangedToFalseDimensions.size());
-// EXPECT_TRUE(changedCache[0]);
-//}
-
-} // namespace statsd
-} // namespace os
-} // namespace android
-#else
-GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
diff --git a/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/Utils.java b/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/Utils.java
index a381f9c4560a..2909048da7ea 100644
--- a/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/Utils.java
+++ b/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/Utils.java
@@ -17,16 +17,23 @@ package com.android.statsd.shelltools;
import com.android.os.StatsLog.ConfigMetricsReportList;
+import com.google.common.io.Files;
+
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
+import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.List;
import java.util.logging.ConsoleHandler;
import java.util.logging.Formatter;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.Logger;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
/**
* Utilities for local use of statsd.
@@ -80,7 +87,8 @@ public class Utils {
* @throws InterruptedException
*/
public static ConfigMetricsReportList getReportList(long configId, boolean clearData,
- boolean useShellUid, Logger logger) throws IOException, InterruptedException {
+ boolean useShellUid, Logger logger, String deviceSerial)
+ throws IOException, InterruptedException {
try {
File outputFile = File.createTempFile("statsdret", ".bin");
outputFile.deleteOnExit();
@@ -88,6 +96,8 @@ public class Utils {
outputFile,
logger,
"adb",
+ "-s",
+ deviceSerial,
"shell",
CMD_DUMP_REPORT,
useShellUid ? SHELL_UID : "",
@@ -117,12 +127,14 @@ public class Utils {
* @throws IOException
* @throws InterruptedException
*/
- public static void logAppBreadcrumb(int label, int state, Logger logger)
+ public static void logAppBreadcrumb(int label, int state, Logger logger, String deviceSerial)
throws IOException, InterruptedException {
runCommand(
null,
logger,
"adb",
+ "-s",
+ deviceSerial,
"shell",
CMD_LOG_APP_BREADCRUMB,
String.valueOf(label),
@@ -145,13 +157,14 @@ public class Utils {
* Algorithm: true if (sdk >= minSdk) || (sdk == minSdk-1 && codeName.startsWith(minCodeName))
* If all else fails, assume it will work (letting future commands deal with any errors).
*/
- public static boolean isAcceptableStatsd(Logger logger, int minSdk, String minCodename) {
+ public static boolean isAcceptableStatsd(Logger logger, int minSdk, String minCodename,
+ String deviceSerial) {
BufferedReader in = null;
try {
File outFileSdk = File.createTempFile("shelltools_sdk", "tmp");
outFileSdk.deleteOnExit();
runCommand(outFileSdk, logger,
- "adb", "shell", "getprop", "ro.build.version.sdk");
+ "adb", "-s", deviceSerial, "shell", "getprop", "ro.build.version.sdk");
in = new BufferedReader(new InputStreamReader(new FileInputStream(outFileSdk)));
// If NullPointerException/NumberFormatException/etc., just catch and return true.
int sdk = Integer.parseInt(in.readLine().trim());
@@ -162,7 +175,7 @@ public class Utils {
File outFileCode = File.createTempFile("shelltools_codename", "tmp");
outFileCode.deleteOnExit();
runCommand(outFileCode, logger,
- "adb", "shell", "getprop", "ro.build.version.codename");
+ "adb", "-s", deviceSerial, "shell", "getprop", "ro.build.version.codename");
in = new BufferedReader(new InputStreamReader(new FileInputStream(outFileCode)));
return in.readLine().startsWith(minCodename);
} else {
@@ -190,4 +203,30 @@ public class Utils {
return record.getMessage() + "\n";
}
}
+
+ /**
+ * Parse the result of "adb devices" to return the list of connected devices.
+ * @param logger Logger to log error messages
+ * @return List of the serial numbers of the connected devices.
+ */
+ public static List<String> getDeviceSerials(Logger logger) {
+ try {
+ ArrayList<String> devices = new ArrayList<>();
+ File outFile = File.createTempFile("device_serial", "tmp");
+ outFile.deleteOnExit();
+ Utils.runCommand(outFile, logger, "adb", "devices");
+ List<String> outputLines = Files.readLines(outFile, Charset.defaultCharset());
+ Pattern regex = Pattern.compile("^(.*)\tdevice$");
+ for (String line : outputLines) {
+ Matcher m = regex.matcher(line);
+ if (m.find()) {
+ devices.add(m.group(1));
+ }
+ }
+ return devices;
+ } catch (Exception ex) {
+ logger.log(Level.SEVERE, "Failed to list connected devices: " + ex.getMessage());
+ }
+ return null;
+ }
}
diff --git a/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/localdrive/LocalDrive.java b/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/localdrive/LocalDrive.java
index 2eb46605b28d..7db514180b9a 100644
--- a/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/localdrive/LocalDrive.java
+++ b/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/localdrive/LocalDrive.java
@@ -26,6 +26,8 @@ import com.google.protobuf.TextFormat;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
+import java.util.List;
+import java.util.logging.Level;
import java.util.logging.Logger;
/**
@@ -49,7 +51,7 @@ public class LocalDrive {
public static final String HELP_STRING =
"Usage:\n\n" +
- "statsd_localdrive upload CONFIG_FILE [CONFIG_ID] [--binary]\n" +
+ "statsd_localdrive [-s DEVICE_SERIAL] upload CONFIG_FILE [CONFIG_ID] [--binary]\n" +
" Uploads the given statsd config file (in binary or human-readable-text format).\n" +
" If a config with this id already exists, removes it first.\n" +
" CONFIG_FILE Location of config file on host.\n" +
@@ -59,12 +61,12 @@ public class LocalDrive {
// Similar to: adb shell cmd stats config update SHELL_UID CONFIG_ID
"\n" +
- "statsd_localdrive update CONFIG_FILE [CONFIG_ID] [--binary]\n" +
+ "statsd_localdrive [-s DEVICE_SERIAL] update CONFIG_FILE [CONFIG_ID] [--binary]\n" +
" Same as upload, but does not remove the old config first (if it already exists).\n" +
// Similar to: adb shell cmd stats config update SHELL_UID CONFIG_ID
"\n" +
- "statsd_localdrive get-data [CONFIG_ID] [--clear] [--binary] [--no-uid-map]\n" +
+ "statsd_localdrive [-s DEVICE_SERIAL] get-data [CONFIG_ID] [--clear] [--binary] [--no-uid-map]\n" +
" Prints the output statslog data (in binary or human-readable-text format).\n" +
" CONFIG_ID Long ID of the config. If absent, uses " + DEFAULT_CONFIG_ID + ".\n" +
" --binary Output should be in binary, instead of default human-readable text.\n" +
@@ -75,13 +77,13 @@ public class LocalDrive {
// --include_current_bucket --proto
"\n" +
- "statsd_localdrive remove [CONFIG_ID]\n" +
+ "statsd_localdrive [-s DEVICE_SERIAL] remove [CONFIG_ID]\n" +
" Removes the config.\n" +
" CONFIG_ID Long ID of the config. If absent, uses " + DEFAULT_CONFIG_ID + ".\n" +
// Equivalent to: adb shell cmd stats config remove SHELL_UID CONFIG_ID
"\n" +
- "statsd_localdrive clear [CONFIG_ID]\n" +
+ "statsd_localdrive [-s DEVICE_SERIAL] clear [CONFIG_ID]\n" +
" Clears the data associated with the config.\n" +
" CONFIG_ID Long ID of the config. If absent, uses " + DEFAULT_CONFIG_ID + ".\n" +
// Similar to: adb shell cmd stats dump-report SHELL_UID CONFIG_ID
@@ -94,29 +96,59 @@ public class LocalDrive {
/** Usage: make statsd_localdrive && statsd_localdrive */
public static void main(String[] args) {
Utils.setUpLogger(sLogger, DEBUG);
+ if (args.length == 0) {
+ printHelp();
+ return;
+ }
+
+ int remainingArgsLength = args.length;
+ String deviceSerial = null;
+ if (args[0].equals("-s")) {
+ if (args.length == 1) {
+ printHelp();
+ }
+ deviceSerial = args[1];
+ remainingArgsLength -= 2;
+ }
+
+ List<String> connectedDevices = Utils.getDeviceSerials(sLogger);
+ if (connectedDevices == null || connectedDevices.size() == 0) {
+ sLogger.log(Level.SEVERE, "No device connected.");
+ return;
+ }
+ if (connectedDevices.size() == 1 && deviceSerial == null) {
+ deviceSerial = connectedDevices.get(0);
+ }
+
+ if (deviceSerial == null) {
+ sLogger.log(Level.SEVERE, "More than one devices connected. Please specify"
+ + " with -s DEVICE_SERIAL");
+ return;
+ }
- if (!Utils.isAcceptableStatsd(sLogger, MIN_SDK, MIN_CODENAME)) {
+ if (!Utils.isAcceptableStatsd(sLogger, MIN_SDK, MIN_CODENAME, deviceSerial)) {
sLogger.severe("LocalDrive only works with statsd versions for Android "
+ MIN_CODENAME + " or higher.");
return;
}
- if (args.length > 0) {
- switch (args[0]) {
+ int idx = args.length - remainingArgsLength;
+ if (remainingArgsLength > 0) {
+ switch (args[idx]) {
case "clear":
- cmdClear(args);
+ cmdClear(args, idx, deviceSerial);
return;
case "get-data":
- cmdGetData(args);
+ cmdGetData(args, idx, deviceSerial);
return;
case "remove":
- cmdRemove(args);
+ cmdRemove(args, idx);
return;
case "update":
- cmdUpdate(args);
+ cmdUpdate(args, idx, deviceSerial);
return;
case "upload":
- cmdUpload(args);
+ cmdUpload(args, idx, deviceSerial);
return;
}
}
@@ -128,17 +160,18 @@ public class LocalDrive {
}
// upload CONFIG_FILE [CONFIG_ID] [--binary]
- private static boolean cmdUpload(String[] args) {
- return updateConfig(args, true);
+ private static boolean cmdUpload(String[] args, int idx, String deviceSerial) {
+ return updateConfig(args, idx, true, deviceSerial);
}
// update CONFIG_FILE [CONFIG_ID] [--binary]
- private static boolean cmdUpdate(String[] args) {
- return updateConfig(args, false);
+ private static boolean cmdUpdate(String[] args, int idx, String deviceSerial) {
+ return updateConfig(args, idx, false, deviceSerial);
}
- private static boolean updateConfig(String[] args, boolean removeOldConfig) {
- int argCount = args.length - 1; // Used up one for upload/update.
+ private static boolean updateConfig(String[] args, int idx, boolean removeOldConfig,
+ String deviceSerial) {
+ int argCount = args.length - 1 - idx; // Used up one for upload/update.
// Get CONFIG_FILE
if (argCount < 1) {
@@ -146,7 +179,7 @@ public class LocalDrive {
printHelp();
return false;
}
- final String origConfigLocation = args[1];
+ final String origConfigLocation = args[idx + 1];
if (!new File(origConfigLocation).exists()) {
sLogger.severe("Error - Cannot find the provided config file: " + origConfigLocation);
return false;
@@ -154,13 +187,13 @@ public class LocalDrive {
argCount--;
// Get --binary
- boolean binary = contains(args, 2, BINARY_FLAG);
+ boolean binary = contains(args, idx + 2, BINARY_FLAG);
if (binary) argCount --;
// Get CONFIG_ID
long configId;
try {
- configId = getConfigId(argCount < 1, args, 2);
+ configId = getConfigId(argCount < 1, args, idx + 2);
} catch (NumberFormatException e) {
sLogger.severe("Invalid config id provided.");
printHelp();
@@ -174,7 +207,8 @@ public class LocalDrive {
try {
Utils.runCommand(null, sLogger, "adb", "shell", Utils.CMD_REMOVE_CONFIG,
Utils.SHELL_UID, String.valueOf(configId));
- Utils.getReportList(configId, true /* clearData */, true /* SHELL_UID */, sLogger);
+ Utils.getReportList(configId, true /* clearData */, true /* SHELL_UID */, sLogger,
+ deviceSerial);
} catch (InterruptedException | IOException e) {
sLogger.severe("Failed to remove config: " + e.getMessage());
return false;
@@ -218,19 +252,19 @@ public class LocalDrive {
}
// get-data [CONFIG_ID] [--clear] [--binary] [--no-uid-map]
- private static boolean cmdGetData(String[] args) {
- boolean binary = contains(args, 1, BINARY_FLAG);
- boolean noUidMap = contains(args, 1, NO_UID_MAP_FLAG);
- boolean clearData = contains(args, 1, CLEAR_DATA);
+ private static boolean cmdGetData(String[] args, int idx, String deviceSerial) {
+ boolean binary = contains(args, idx + 1, BINARY_FLAG);
+ boolean noUidMap = contains(args, idx + 1, NO_UID_MAP_FLAG);
+ boolean clearData = contains(args, idx + 1, CLEAR_DATA);
// Get CONFIG_ID
- int argCount = args.length - 1; // Used up one for get-data.
+ int argCount = args.length - 1 - idx; // Used up one for get-data.
if (binary) argCount--;
if (noUidMap) argCount--;
if (clearData) argCount--;
long configId;
try {
- configId = getConfigId(argCount < 1, args, 1);
+ configId = getConfigId(argCount < 1, args, idx + 1);
} catch (NumberFormatException e) {
sLogger.severe("Invalid config id provided.");
printHelp();
@@ -243,7 +277,8 @@ public class LocalDrive {
// Even if the args request no modifications, we still parse it to make sure it's valid.
ConfigMetricsReportList reportList;
try {
- reportList = Utils.getReportList(configId, clearData, true /* SHELL_UID */, sLogger);
+ reportList = Utils.getReportList(configId, clearData, true /* SHELL_UID */, sLogger,
+ deviceSerial);
} catch (IOException | InterruptedException e) {
sLogger.severe("Failed to get report list: " + e.getMessage());
return false;
@@ -274,11 +309,11 @@ public class LocalDrive {
}
// clear [CONFIG_ID]
- private static boolean cmdClear(String[] args) {
+ private static boolean cmdClear(String[] args, int idx, String deviceSerial) {
// Get CONFIG_ID
long configId;
try {
- configId = getConfigId(false, args, 1);
+ configId = getConfigId(false, args, idx + 1);
} catch (NumberFormatException e) {
sLogger.severe("Invalid config id provided.");
printHelp();
@@ -287,7 +322,8 @@ public class LocalDrive {
sLogger.fine(String.format("cmdClear with %d", configId));
try {
- Utils.getReportList(configId, true /* clearData */, true /* SHELL_UID */, sLogger);
+ Utils.getReportList(configId, true /* clearData */, true /* SHELL_UID */, sLogger,
+ deviceSerial);
} catch (IOException | InterruptedException e) {
sLogger.severe("Failed to get report list: " + e.getMessage());
return false;
@@ -296,11 +332,11 @@ public class LocalDrive {
}
// remove [CONFIG_ID]
- private static boolean cmdRemove(String[] args) {
+ private static boolean cmdRemove(String[] args, int idx) {
// Get CONFIG_ID
long configId;
try {
- configId = getConfigId(false, args, 1);
+ configId = getConfigId(false, args, idx + 1);
} catch (NumberFormatException e) {
sLogger.severe("Invalid config id provided.");
printHelp();
diff --git a/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java b/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java
index 75518a3ea56f..2a7cfd306174 100644
--- a/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java
+++ b/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java
@@ -37,6 +37,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
+import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -71,6 +72,7 @@ public class TestDrive {
private static final Logger LOGGER = Logger.getLogger(TestDrive.class.getName());
private String mAdditionalAllowedPackage;
+ private String mDeviceSerial;
private final Set<Long> mTrackedMetrics = new HashSet<>();
public static void main(String[] args) {
@@ -81,15 +83,41 @@ public class TestDrive {
if (args.length < 1) {
LOGGER.log(Level.SEVERE, "Usage: ./test_drive [-p additional_allowed_package] "
+ + "[-s DEVICE_SERIAL_NUMBER]"
+ "<atomId1> <atomId2> ... <atomIdN>");
return;
}
- if (args.length >= 3 && args[0].equals("-p")) {
- testDrive.mAdditionalAllowedPackage = args[1];
+ List<String> connectedDevices = Utils.getDeviceSerials(LOGGER);
+ if (connectedDevices == null || connectedDevices.size() == 0) {
+ LOGGER.log(Level.SEVERE, "No device connected.");
+ return;
+ }
+
+ int arg_index = 0;
+ while (arg_index < args.length) {
+ String arg = args[arg_index];
+ if (arg.equals("-p")) {
+ testDrive.mAdditionalAllowedPackage = args[++arg_index];
+ } else if (arg.equals("-s")) {
+ testDrive.mDeviceSerial = args[++arg_index];
+ } else {
+ break;
+ }
+ arg_index++;
+ }
+
+ if (connectedDevices.size() == 1 && testDrive.mDeviceSerial == null) {
+ testDrive.mDeviceSerial = connectedDevices.get(0);
+ }
+
+ if (testDrive.mDeviceSerial == null) {
+ LOGGER.log(Level.SEVERE, "More than one devices connected. Please specify"
+ + " with -s DEVICE_SERIAL");
+ return;
}
- for (int i = testDrive.mAdditionalAllowedPackage == null ? 0 : 2; i < args.length; i++) {
+ for (int i = arg_index; i < args.length; i++) {
try {
int atomId = Integer.valueOf(args[i]);
if (Atom.getDescriptor().findFieldByNumber(atomId) == null) {
@@ -109,7 +137,7 @@ public class TestDrive {
LOGGER.log(Level.SEVERE, "Failed to create valid config.");
return;
}
- remoteConfigPath = testDrive.pushConfig(config);
+ remoteConfigPath = testDrive.pushConfig(config, testDrive.mDeviceSerial);
LOGGER.info("Pushed the following config to statsd:");
LOGGER.info(config.toString());
if (!hasPulledAtom(trackedAtoms)) {
@@ -120,17 +148,18 @@ public class TestDrive {
} else {
LOGGER.info("Now wait for 1.5 minutes ...");
Thread.sleep(15_000);
- Utils.logAppBreadcrumb(0, 0, LOGGER);
+ Utils.logAppBreadcrumb(0, 0, LOGGER, testDrive.mDeviceSerial);
Thread.sleep(75_000);
}
testDrive.dumpMetrics();
} catch (Exception e) {
LOGGER.log(Level.SEVERE, "Failed to test drive: " + e.getMessage(), e);
} finally {
- testDrive.removeConfig();
+ testDrive.removeConfig(testDrive.mDeviceSerial);
if (remoteConfigPath != null) {
try {
- Utils.runCommand(null, LOGGER, "adb", "shell", "rm", remoteConfigPath);
+ Utils.runCommand(null, LOGGER,
+ "adb", "-s", testDrive.mDeviceSerial, "shell", "rm", remoteConfigPath);
} catch (Exception e) {
LOGGER.log(Level.WARNING,
"Unable to remove remote config file: " + remoteConfigPath, e);
@@ -140,7 +169,8 @@ public class TestDrive {
}
private void dumpMetrics() throws Exception {
- ConfigMetricsReportList reportList = Utils.getReportList(CONFIG_ID, true, false, LOGGER);
+ ConfigMetricsReportList reportList = Utils.getReportList(CONFIG_ID, true, false, LOGGER,
+ mDeviceSerial);
// We may get multiple reports. Take the last one.
ConfigMetricsReport report = reportList.getReports(reportList.getReportsCount() - 1);
for (StatsLogReport statsLog : report.getMetricsList()) {
@@ -216,22 +246,24 @@ public class TestDrive {
return atomMatcherBuilder.build();
}
- private static String pushConfig(StatsdConfig config) throws IOException, InterruptedException {
+ private static String pushConfig(StatsdConfig config, String deviceSerial)
+ throws IOException, InterruptedException {
File configFile = File.createTempFile("statsdconfig", ".config");
configFile.deleteOnExit();
Files.write(config.toByteArray(), configFile);
String remotePath = "/data/local/tmp/" + configFile.getName();
- Utils.runCommand(null, LOGGER, "adb", "push", configFile.getAbsolutePath(), remotePath);
- Utils.runCommand(null, LOGGER,
- "adb", "shell", "cat", remotePath, "|", Utils.CMD_UPDATE_CONFIG,
+ Utils.runCommand(null, LOGGER, "adb", "-s", deviceSerial,
+ "push", configFile.getAbsolutePath(), remotePath);
+ Utils.runCommand(null, LOGGER, "adb", "-s", deviceSerial,
+ "shell", "cat", remotePath, "|", Utils.CMD_UPDATE_CONFIG,
String.valueOf(CONFIG_ID));
return remotePath;
}
- private static void removeConfig() {
+ private static void removeConfig(String deviceSerial) {
try {
- Utils.runCommand(null, LOGGER,
- "adb", "shell", Utils.CMD_REMOVE_CONFIG, String.valueOf(CONFIG_ID));
+ Utils.runCommand(null, LOGGER, "adb", "-s", deviceSerial,
+ "shell", Utils.CMD_REMOVE_CONFIG, String.valueOf(CONFIG_ID));
} catch (Exception e) {
LOGGER.log(Level.SEVERE, "Failed to remove config: " + e.getMessage());
}
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index 65f45d895027..ea5cc7f2e8bc 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -634,17 +634,39 @@ public final class DisplayManager {
public VirtualDisplay createVirtualDisplay(@NonNull String name,
int width, int height, int densityDpi, @Nullable Surface surface, int flags,
@Nullable VirtualDisplay.Callback callback, @Nullable Handler handler) {
- return createVirtualDisplay(null /* projection */, name, width, height, densityDpi, surface,
- flags, callback, handler, null /* uniqueId */);
+ final VirtualDisplayConfig.Builder builder = new VirtualDisplayConfig.Builder(name, width,
+ height, densityDpi);
+ builder.setFlags(flags);
+ if (surface != null) {
+ builder.setSurface(surface);
+ }
+ return createVirtualDisplay(null /* projection */, builder.build(), callback, handler);
}
+ // TODO : Remove this hidden API after remove all callers. (Refer to MultiDisplayService)
/** @hide */
public VirtualDisplay createVirtualDisplay(@Nullable MediaProjection projection,
@NonNull String name, int width, int height, int densityDpi, @Nullable Surface surface,
int flags, @Nullable VirtualDisplay.Callback callback, @Nullable Handler handler,
@Nullable String uniqueId) {
- return mGlobal.createVirtualDisplay(mContext, projection,
- name, width, height, densityDpi, surface, flags, callback, handler, uniqueId);
+ final VirtualDisplayConfig.Builder builder = new VirtualDisplayConfig.Builder(name, width,
+ height, densityDpi);
+ builder.setFlags(flags);
+ if (uniqueId != null) {
+ builder.setUniqueId(uniqueId);
+ }
+ if (surface != null) {
+ builder.setSurface(surface);
+ }
+ return createVirtualDisplay(projection, builder.build(), callback, handler);
+ }
+
+ /** @hide */
+ public VirtualDisplay createVirtualDisplay(@Nullable MediaProjection projection,
+ @NonNull VirtualDisplayConfig virtualDisplayConfig,
+ @Nullable VirtualDisplay.Callback callback, @Nullable Handler handler) {
+ return mGlobal.createVirtualDisplay(mContext, projection, virtualDisplayConfig, callback,
+ handler);
}
/**
diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
index 526db85b47d4..4d645e6052a7 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -451,35 +451,26 @@ public final class DisplayManagerGlobal {
}
}
- public VirtualDisplay createVirtualDisplay(Context context, MediaProjection projection,
- String name, int width, int height, int densityDpi, Surface surface, int flags,
- VirtualDisplay.Callback callback, Handler handler, String uniqueId) {
- if (TextUtils.isEmpty(name)) {
- throw new IllegalArgumentException("name must be non-null and non-empty");
- }
- if (width <= 0 || height <= 0 || densityDpi <= 0) {
- throw new IllegalArgumentException("width, height, and densityDpi must be "
- + "greater than 0");
- }
-
+ public VirtualDisplay createVirtualDisplay(@NonNull Context context, MediaProjection projection,
+ @NonNull VirtualDisplayConfig virtualDisplayConfig, VirtualDisplay.Callback callback,
+ Handler handler) {
VirtualDisplayCallback callbackWrapper = new VirtualDisplayCallback(callback, handler);
IMediaProjection projectionToken = projection != null ? projection.getProjection() : null;
int displayId;
try {
- displayId = mDm.createVirtualDisplay(callbackWrapper, projectionToken,
- context.getPackageName(), name, width, height, densityDpi, surface, flags,
- uniqueId);
+ displayId = mDm.createVirtualDisplay(virtualDisplayConfig, callbackWrapper,
+ projectionToken, context.getPackageName());
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
if (displayId < 0) {
- Log.e(TAG, "Could not create virtual display: " + name);
+ Log.e(TAG, "Could not create virtual display: " + virtualDisplayConfig.getName());
return null;
}
Display display = getRealDisplay(displayId);
if (display == null) {
Log.wtf(TAG, "Could not obtain display info for newly created "
- + "virtual display: " + name);
+ + "virtual display: " + virtualDisplayConfig.getName());
try {
mDm.releaseVirtualDisplay(callbackWrapper);
} catch (RemoteException ex) {
@@ -487,7 +478,8 @@ public final class DisplayManagerGlobal {
}
return null;
}
- return new VirtualDisplay(this, display, callbackWrapper, surface);
+ return new VirtualDisplay(this, display, callbackWrapper,
+ virtualDisplayConfig.getSurface());
}
public void setVirtualDisplaySurface(IVirtualDisplayCallback token, Surface surface) {
diff --git a/core/java/android/hardware/display/IDisplayManager.aidl b/core/java/android/hardware/display/IDisplayManager.aidl
index d22188ec5d7f..c697106d0c17 100644
--- a/core/java/android/hardware/display/IDisplayManager.aidl
+++ b/core/java/android/hardware/display/IDisplayManager.aidl
@@ -22,6 +22,7 @@ import android.hardware.display.BrightnessConfiguration;
import android.hardware.display.Curve;
import android.hardware.display.IDisplayManagerCallback;
import android.hardware.display.IVirtualDisplayCallback;
+import android.hardware.display.VirtualDisplayConfig;
import android.hardware.display.WifiDisplay;
import android.hardware.display.WifiDisplayStatus;
import android.media.projection.IMediaProjection;
@@ -71,9 +72,9 @@ interface IDisplayManager {
// Requires CAPTURE_VIDEO_OUTPUT, CAPTURE_SECURE_VIDEO_OUTPUT, or an appropriate
// MediaProjection token for certain combinations of flags.
- int createVirtualDisplay(in IVirtualDisplayCallback callback,
- in IMediaProjection projectionToken, String packageName, String name,
- int width, int height, int densityDpi, in Surface surface, int flags, String uniqueId);
+ int createVirtualDisplay(in VirtualDisplayConfig virtualDisplayConfig,
+ in IVirtualDisplayCallback callback, in IMediaProjection projectionToken,
+ String packageName);
// No permissions required, but must be same Uid as the creator.
void resizeVirtualDisplay(in IVirtualDisplayCallback token,
diff --git a/core/java/android/hardware/display/VirtualDisplayConfig.aidl b/core/java/android/hardware/display/VirtualDisplayConfig.aidl
new file mode 100644
index 000000000000..c28f1dfb9806
--- /dev/null
+++ b/core/java/android/hardware/display/VirtualDisplayConfig.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.display;
+
+parcelable VirtualDisplayConfig;
diff --git a/core/java/android/hardware/display/VirtualDisplayConfig.java b/core/java/android/hardware/display/VirtualDisplayConfig.java
new file mode 100644
index 000000000000..10e1c7c2e0df
--- /dev/null
+++ b/core/java/android/hardware/display/VirtualDisplayConfig.java
@@ -0,0 +1,491 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.display;
+
+import static android.view.Display.DEFAULT_DISPLAY;
+
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.media.projection.MediaProjection;
+import android.os.Handler;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.view.Surface;
+
+import com.android.internal.util.DataClass;
+
+/**
+ * Holds configuration used to create {@link VirtualDisplay} instances. See
+ * {@link MediaProjection#createVirtualDisplay(VirtualDisplayConfig, VirtualDisplay.Callback, Handler)}.
+ *
+ * @hide
+ */
+@DataClass(genParcelable = true, genAidl = true, genBuilder = true)
+public final class VirtualDisplayConfig implements Parcelable {
+ /**
+ * The name of the virtual display, must be non-empty.
+ */
+ @NonNull
+ private String mName;
+
+ /**
+ * The width of the virtual display in pixels. Must be greater than 0.
+ */
+ @IntRange(from = 1)
+ private int mWidth;
+
+ /**
+ * The height of the virtual display in pixels. Must be greater than 0.
+ */
+ @IntRange(from = 1)
+ private int mHeight;
+
+ /**
+ * The density of the virtual display in dpi. Must be greater than 0.
+ */
+ @IntRange(from = 1)
+ private int mDensityDpi;
+
+ /**
+ * A combination of virtual display flags.
+ * {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_PUBLIC},
+ * {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_PRESENTATION},
+ * {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_SECURE},
+ * {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY},
+ * or {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR}.
+ */
+ private int mFlags = 0;
+
+ /**
+ * The surface to which the content of the virtual display should be rendered, or null if
+ * there is none initially.
+ */
+ @Nullable
+ private Surface mSurface = null;
+
+ /**
+ * The unique identifier for the display. Shouldn't be displayed to the user.
+ * @hide
+ */
+ @Nullable
+ private String mUniqueId = null;
+
+ /**
+ * The id of the display that the virtual display should mirror, or
+ * {@link android.view.Display#DEFAULT_DISPLAY} if there is none initially.
+ */
+ private int mDisplayIdToMirror = DEFAULT_DISPLAY;
+
+
+
+ // Code below generated by codegen v1.0.15.
+ //
+ // DO NOT MODIFY!
+ // CHECKSTYLE:OFF Generated code
+ //
+ // To regenerate run:
+ // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/hardware/display/VirtualDisplayConfig.java
+ //
+ // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+ // Settings > Editor > Code Style > Formatter Control
+ //@formatter:off
+
+
+ @DataClass.Generated.Member
+ /* package-private */ VirtualDisplayConfig(
+ @NonNull String name,
+ @IntRange(from = 1) int width,
+ @IntRange(from = 1) int height,
+ @IntRange(from = 1) int densityDpi,
+ int flags,
+ @Nullable Surface surface,
+ @Nullable String uniqueId,
+ int displayIdToMirror) {
+ this.mName = name;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mName);
+ this.mWidth = width;
+ com.android.internal.util.AnnotationValidations.validate(
+ IntRange.class, null, mWidth,
+ "from", 1);
+ this.mHeight = height;
+ com.android.internal.util.AnnotationValidations.validate(
+ IntRange.class, null, mHeight,
+ "from", 1);
+ this.mDensityDpi = densityDpi;
+ com.android.internal.util.AnnotationValidations.validate(
+ IntRange.class, null, mDensityDpi,
+ "from", 1);
+ this.mFlags = flags;
+ this.mSurface = surface;
+ this.mUniqueId = uniqueId;
+ this.mDisplayIdToMirror = displayIdToMirror;
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ /**
+ * The name of the virtual display, must be non-empty.
+ */
+ @DataClass.Generated.Member
+ public @NonNull String getName() {
+ return mName;
+ }
+
+ /**
+ * The width of the virtual display in pixels. Must be greater than 0.
+ */
+ @DataClass.Generated.Member
+ public @IntRange(from = 1) int getWidth() {
+ return mWidth;
+ }
+
+ /**
+ * The height of the virtual display in pixels. Must be greater than 0.
+ */
+ @DataClass.Generated.Member
+ public @IntRange(from = 1) int getHeight() {
+ return mHeight;
+ }
+
+ /**
+ * The density of the virtual display in dpi. Must be greater than 0.
+ */
+ @DataClass.Generated.Member
+ public @IntRange(from = 1) int getDensityDpi() {
+ return mDensityDpi;
+ }
+
+ /**
+ * A combination of virtual display flags.
+ * {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_PUBLIC},
+ * {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_PRESENTATION},
+ * {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_SECURE},
+ * {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY},
+ * or {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR}.
+ */
+ @DataClass.Generated.Member
+ public int getFlags() {
+ return mFlags;
+ }
+
+ /**
+ * The surface to which the content of the virtual display should be rendered, or null if
+ * there is none initially.
+ */
+ @DataClass.Generated.Member
+ public @Nullable Surface getSurface() {
+ return mSurface;
+ }
+
+ /**
+ * The unique identifier for the display. Shouldn't be displayed to the user.
+ *
+ * @hide
+ */
+ @DataClass.Generated.Member
+ public @Nullable String getUniqueId() {
+ return mUniqueId;
+ }
+
+ /**
+ * The id of the display that the virtual display should mirror, or
+ * {@link android.view.Display#DEFAULT_DISPLAY} if there is none initially.
+ */
+ @DataClass.Generated.Member
+ public int getDisplayIdToMirror() {
+ return mDisplayIdToMirror;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ // You can override field parcelling by defining methods like:
+ // void parcelFieldName(Parcel dest, int flags) { ... }
+
+ int flg = 0;
+ if (mSurface != null) flg |= 0x20;
+ if (mUniqueId != null) flg |= 0x40;
+ dest.writeInt(flg);
+ dest.writeString(mName);
+ dest.writeInt(mWidth);
+ dest.writeInt(mHeight);
+ dest.writeInt(mDensityDpi);
+ dest.writeInt(mFlags);
+ if (mSurface != null) dest.writeTypedObject(mSurface, flags);
+ if (mUniqueId != null) dest.writeString(mUniqueId);
+ dest.writeInt(mDisplayIdToMirror);
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int describeContents() { return 0; }
+
+ /** @hide */
+ @SuppressWarnings({"unchecked", "RedundantCast"})
+ @DataClass.Generated.Member
+ /* package-private */ VirtualDisplayConfig(@NonNull Parcel in) {
+ // You can override field unparcelling by defining methods like:
+ // static FieldType unparcelFieldName(Parcel in) { ... }
+
+ int flg = in.readInt();
+ String name = in.readString();
+ int width = in.readInt();
+ int height = in.readInt();
+ int densityDpi = in.readInt();
+ int flags = in.readInt();
+ Surface surface = (flg & 0x20) == 0 ? null : (Surface) in.readTypedObject(Surface.CREATOR);
+ String uniqueId = (flg & 0x40) == 0 ? null : in.readString();
+ int displayIdToMirror = in.readInt();
+
+ this.mName = name;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mName);
+ this.mWidth = width;
+ com.android.internal.util.AnnotationValidations.validate(
+ IntRange.class, null, mWidth,
+ "from", 1);
+ this.mHeight = height;
+ com.android.internal.util.AnnotationValidations.validate(
+ IntRange.class, null, mHeight,
+ "from", 1);
+ this.mDensityDpi = densityDpi;
+ com.android.internal.util.AnnotationValidations.validate(
+ IntRange.class, null, mDensityDpi,
+ "from", 1);
+ this.mFlags = flags;
+ this.mSurface = surface;
+ this.mUniqueId = uniqueId;
+ this.mDisplayIdToMirror = displayIdToMirror;
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ @DataClass.Generated.Member
+ public static final @NonNull Parcelable.Creator<VirtualDisplayConfig> CREATOR
+ = new Parcelable.Creator<VirtualDisplayConfig>() {
+ @Override
+ public VirtualDisplayConfig[] newArray(int size) {
+ return new VirtualDisplayConfig[size];
+ }
+
+ @Override
+ public VirtualDisplayConfig createFromParcel(@NonNull Parcel in) {
+ return new VirtualDisplayConfig(in);
+ }
+ };
+
+ /**
+ * A builder for {@link VirtualDisplayConfig}
+ */
+ @SuppressWarnings("WeakerAccess")
+ @DataClass.Generated.Member
+ public static final class Builder {
+
+ private @NonNull String mName;
+ private @IntRange(from = 1) int mWidth;
+ private @IntRange(from = 1) int mHeight;
+ private @IntRange(from = 1) int mDensityDpi;
+ private int mFlags;
+ private @Nullable Surface mSurface;
+ private @Nullable String mUniqueId;
+ private int mDisplayIdToMirror;
+
+ private long mBuilderFieldsSet = 0L;
+
+ /**
+ * Creates a new Builder.
+ *
+ * @param name
+ * The name of the virtual display, must be non-empty.
+ * @param width
+ * The width of the virtual display in pixels. Must be greater than 0.
+ * @param height
+ * The height of the virtual display in pixels. Must be greater than 0.
+ * @param densityDpi
+ * The density of the virtual display in dpi. Must be greater than 0.
+ */
+ public Builder(
+ @NonNull String name,
+ @IntRange(from = 1) int width,
+ @IntRange(from = 1) int height,
+ @IntRange(from = 1) int densityDpi) {
+ mName = name;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mName);
+ mWidth = width;
+ com.android.internal.util.AnnotationValidations.validate(
+ IntRange.class, null, mWidth,
+ "from", 1);
+ mHeight = height;
+ com.android.internal.util.AnnotationValidations.validate(
+ IntRange.class, null, mHeight,
+ "from", 1);
+ mDensityDpi = densityDpi;
+ com.android.internal.util.AnnotationValidations.validate(
+ IntRange.class, null, mDensityDpi,
+ "from", 1);
+ }
+
+ /**
+ * The name of the virtual display, must be non-empty.
+ */
+ @DataClass.Generated.Member
+ public @NonNull Builder setName(@NonNull String value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x1;
+ mName = value;
+ return this;
+ }
+
+ /**
+ * The width of the virtual display in pixels. Must be greater than 0.
+ */
+ @DataClass.Generated.Member
+ public @NonNull Builder setWidth(@IntRange(from = 1) int value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x2;
+ mWidth = value;
+ return this;
+ }
+
+ /**
+ * The height of the virtual display in pixels. Must be greater than 0.
+ */
+ @DataClass.Generated.Member
+ public @NonNull Builder setHeight(@IntRange(from = 1) int value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x4;
+ mHeight = value;
+ return this;
+ }
+
+ /**
+ * The density of the virtual display in dpi. Must be greater than 0.
+ */
+ @DataClass.Generated.Member
+ public @NonNull Builder setDensityDpi(@IntRange(from = 1) int value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x8;
+ mDensityDpi = value;
+ return this;
+ }
+
+ /**
+ * A combination of virtual display flags.
+ * {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_PUBLIC},
+ * {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_PRESENTATION},
+ * {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_SECURE},
+ * {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY},
+ * or {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR}.
+ */
+ @DataClass.Generated.Member
+ public @NonNull Builder setFlags(int value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x10;
+ mFlags = value;
+ return this;
+ }
+
+ /**
+ * The surface to which the content of the virtual display should be rendered, or null if
+ * there is none initially.
+ */
+ @DataClass.Generated.Member
+ public @NonNull Builder setSurface(@NonNull Surface value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x20;
+ mSurface = value;
+ return this;
+ }
+
+ /**
+ * The unique identifier for the display. Shouldn't be displayed to the user.
+ *
+ * @hide
+ */
+ @DataClass.Generated.Member
+ public @NonNull Builder setUniqueId(@NonNull String value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x40;
+ mUniqueId = value;
+ return this;
+ }
+
+ /**
+ * The id of the display that the virtual display should mirror, or
+ * {@link android.view.Display#DEFAULT_DISPLAY} if there is none initially.
+ */
+ @DataClass.Generated.Member
+ public @NonNull Builder setDisplayIdToMirror(int value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x80;
+ mDisplayIdToMirror = value;
+ return this;
+ }
+
+ /** Builds the instance. This builder should not be touched after calling this! */
+ public @NonNull VirtualDisplayConfig build() {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x100; // Mark builder used
+
+ if ((mBuilderFieldsSet & 0x10) == 0) {
+ mFlags = 0;
+ }
+ if ((mBuilderFieldsSet & 0x20) == 0) {
+ mSurface = null;
+ }
+ if ((mBuilderFieldsSet & 0x40) == 0) {
+ mUniqueId = null;
+ }
+ if ((mBuilderFieldsSet & 0x80) == 0) {
+ mDisplayIdToMirror = DEFAULT_DISPLAY;
+ }
+ VirtualDisplayConfig o = new VirtualDisplayConfig(
+ mName,
+ mWidth,
+ mHeight,
+ mDensityDpi,
+ mFlags,
+ mSurface,
+ mUniqueId,
+ mDisplayIdToMirror);
+ return o;
+ }
+
+ private void checkNotUsed() {
+ if ((mBuilderFieldsSet & 0x100) != 0) {
+ throw new IllegalStateException(
+ "This Builder should not be reused. Use a new Builder instance instead");
+ }
+ }
+ }
+
+ @DataClass.Generated(
+ time = 1585179350902L,
+ codegenVersion = "1.0.15",
+ sourceFile = "frameworks/base/core/java/android/hardware/display/VirtualDisplayConfig.java",
+ inputSignatures = "private @android.annotation.NonNull java.lang.String mName\nprivate @android.annotation.IntRange(from=1L) int mWidth\nprivate @android.annotation.IntRange(from=1L) int mHeight\nprivate @android.annotation.IntRange(from=1L) int mDensityDpi\nprivate int mFlags\nprivate @android.annotation.Nullable android.view.Surface mSurface\nprivate @android.annotation.Nullable java.lang.String mUniqueId\nprivate int mDisplayIdToMirror\nclass VirtualDisplayConfig extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genParcelable=true, genAidl=true, genBuilder=true)")
+ @Deprecated
+ private void __metadata() {}
+
+
+ //@formatter:on
+ // End of generated code
+
+}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 35f955f7e78b..b5e8dd8212e3 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -1747,16 +1747,13 @@ public final class ViewRootImpl implements ViewParent,
|| !mBlastSurfaceControl.isValid()) {
return null;
}
+
if (mBlastBufferQueue == null) {
mBlastBufferQueue = new BLASTBufferQueue(
mBlastSurfaceControl, width, height);
}
mBlastBufferQueue.update(mBlastSurfaceControl, width, height);
- mTransaction.show(mBlastSurfaceControl)
- .reparent(mBlastSurfaceControl, mSurfaceControl)
- .apply();
-
return mBlastBufferQueue.getSurface();
}
diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java
index 561ee604aa7f..316a5f2c88d2 100644
--- a/core/java/android/view/WindowManagerImpl.java
+++ b/core/java/android/view/WindowManagerImpl.java
@@ -36,7 +36,6 @@ import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.IResultReceiver;
import java.util.List;
@@ -70,8 +69,7 @@ import java.util.List;
public final class WindowManagerImpl implements WindowManager {
@UnsupportedAppUsage
private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
- @VisibleForTesting
- public final Context mContext;
+ private final Context mContext;
private final Window mParentWindow;
private IBinder mDefaultToken;
diff --git a/core/java/com/android/internal/policy/DecorContext.java b/core/java/com/android/internal/policy/DecorContext.java
index f00776897f2c..99b4b5fb7707 100644
--- a/core/java/com/android/internal/policy/DecorContext.java
+++ b/core/java/com/android/internal/policy/DecorContext.java
@@ -41,17 +41,17 @@ import java.lang.ref.WeakReference;
public class DecorContext extends ContextThemeWrapper {
private PhoneWindow mPhoneWindow;
private WindowManager mWindowManager;
- private Resources mResources;
+ private Resources mActivityResources;
private ContentCaptureManager mContentCaptureManager;
- private WeakReference<Context> mContext;
+ private WeakReference<Context> mActivityContext;
// TODO(b/149928768): Non-activity context can be passed.
@VisibleForTesting
- public DecorContext(Context baseContext, Context context) {
- super(baseContext.createDisplayContext(context.getDisplayNoVerify()), null);
- mContext = new WeakReference<>(context);
- mResources = context.getResources();
+ public DecorContext(Context context, Context activityContext) {
+ super(context.createDisplayContext(activityContext.getDisplayNoVerify()), null);
+ mActivityContext = new WeakReference<>(activityContext);
+ mActivityResources = activityContext.getResources();
}
void setPhoneWindow(PhoneWindow phoneWindow) {
@@ -61,56 +61,58 @@ public class DecorContext extends ContextThemeWrapper {
@Override
public Object getSystemService(String name) {
- final Context context = mContext.get();
if (Context.WINDOW_SERVICE.equals(name)) {
- if (context != null && mWindowManager == null) {
- WindowManagerImpl wm = (WindowManagerImpl) context.getSystemService(name);
+ if (mWindowManager == null) {
+ WindowManagerImpl wm =
+ (WindowManagerImpl) super.getSystemService(Context.WINDOW_SERVICE);
mWindowManager = wm.createLocalWindowManager(mPhoneWindow);
}
return mWindowManager;
}
if (Context.CONTENT_CAPTURE_MANAGER_SERVICE.equals(name)) {
- if (context != null && mContentCaptureManager == null) {
- mContentCaptureManager = (ContentCaptureManager) context.getSystemService(name);
+ if (mContentCaptureManager == null) {
+ Context activityContext = mActivityContext.get();
+ if (activityContext != null) {
+ mContentCaptureManager = (ContentCaptureManager) activityContext
+ .getSystemService(name);
+ }
}
return mContentCaptureManager;
}
- // LayoutInflater and WallpaperManagerService should also be obtained from context
- // instead of application context.
- return (context != null) ? context.getSystemService(name) : super.getSystemService(name);
+ return super.getSystemService(name);
}
@Override
public Resources getResources() {
- Context context = mContext.get();
+ Context activityContext = mActivityContext.get();
// Attempt to update the local cached Resources from the activity context. If the activity
// is no longer around, return the old cached values.
- if (context != null) {
- mResources = context.getResources();
+ if (activityContext != null) {
+ mActivityResources = activityContext.getResources();
}
- return mResources;
+ return mActivityResources;
}
@Override
public AssetManager getAssets() {
- return mResources.getAssets();
+ return mActivityResources.getAssets();
}
@Override
public AutofillOptions getAutofillOptions() {
- Context context = mContext.get();
- if (context != null) {
- return context.getAutofillOptions();
+ Context activityContext = mActivityContext.get();
+ if (activityContext != null) {
+ return activityContext.getAutofillOptions();
}
return null;
}
@Override
public ContentCaptureOptions getContentCaptureOptions() {
- Context context = mContext.get();
- if (context != null) {
- return context.getContentCaptureOptions();
+ Context activityContext = mActivityContext.get();
+ if (activityContext != null) {
+ return activityContext.getContentCaptureOptions();
}
return null;
}
diff --git a/core/java/com/android/internal/policy/DividerSnapAlgorithm.java b/core/java/com/android/internal/policy/DividerSnapAlgorithm.java
index 2779be6f9753..575a5320bbd3 100644
--- a/core/java/com/android/internal/policy/DividerSnapAlgorithm.java
+++ b/core/java/com/android/internal/policy/DividerSnapAlgorithm.java
@@ -69,6 +69,7 @@ public class DividerSnapAlgorithm {
private final ArrayList<SnapTarget> mTargets = new ArrayList<>();
private final Rect mInsets = new Rect();
private final int mSnapMode;
+ private final boolean mFreeSnapMode;
private final int mMinimalSizeResizableTask;
private final int mTaskHeightInMinimizedMode;
private final float mFixedRatio;
@@ -125,6 +126,8 @@ public class DividerSnapAlgorithm {
mInsets.set(insets);
mSnapMode = isMinimizedMode ? SNAP_MODE_MINIMIZED :
res.getInteger(com.android.internal.R.integer.config_dockedStackDividerSnapMode);
+ mFreeSnapMode = res.getBoolean(
+ com.android.internal.R.bool.config_dockedStackDividerFreeSnapMode);
mFixedRatio = res.getFraction(
com.android.internal.R.fraction.docked_stack_divider_fixed_ratio, 1, 1);
mMinimalSizeResizableTask = res.getDimensionPixelSize(
@@ -247,7 +250,20 @@ public class DividerSnapAlgorithm {
}
}
+ private boolean shouldApplyFreeSnapMode(int position) {
+ if (!mFreeSnapMode) {
+ return false;
+ }
+ if (!isFirstSplitTargetAvailable() || !isLastSplitTargetAvailable()) {
+ return false;
+ }
+ return mFirstSplitTarget.position < position && position < mLastSplitTarget.position;
+ }
+
private SnapTarget snap(int position, boolean hardDismiss) {
+ if (shouldApplyFreeSnapMode(position)) {
+ return new SnapTarget(position, position, SnapTarget.FLAG_NONE);
+ }
int minIndex = -1;
float minDistance = Float.MAX_VALUE;
int size = mTargets.size();
diff --git a/core/java/com/android/internal/widget/ConversationLayout.java b/core/java/com/android/internal/widget/ConversationLayout.java
index ab68c440483e..523c7493420b 100644
--- a/core/java/com/android/internal/widget/ConversationLayout.java
+++ b/core/java/com/android/internal/widget/ConversationLayout.java
@@ -331,7 +331,8 @@ public class ConversationLayout extends FrameLayout
@RemotableViewMethod
public void setIsImportantConversation(boolean isImportantConversation) {
mImportantConversation = isImportantConversation;
- mImportanceRingView.setVisibility(isImportantConversation ? VISIBLE : GONE);
+ mImportanceRingView.setVisibility(isImportantConversation
+ && mIcon.getVisibility() != GONE ? VISIBLE : GONE);
}
public boolean isImportantConversation() {
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index b1bba53bd7ab..340dd4d7d89a 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3346,6 +3346,10 @@
This should only be set when the device has gestural navigation enabled by default. -->
<bool name="config_showGesturalNavigationHints">false</bool>
+ <!-- Controls the free snap mode for the docked stack divider. In this mode, the divider can be
+ snapped to any position between the first target and the last target. -->
+ <bool name="config_dockedStackDividerFreeSnapMode">false</bool>
+
<!-- Default insets [LEFT/RIGHTxTOP/BOTTOM] from the screen edge for picture-in-picture windows.
These values are in DPs and will be converted to pixel sizes internally. -->
<string translatable="false" name="config_defaultPictureInPictureScreenEdgeInsets">16x16</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index ec8058235912..11dda41d0b57 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1667,6 +1667,7 @@
<java-symbol type="bool" name="config_perDisplayFocusEnabled" />
<java-symbol type="bool" name="config_showNavigationBar" />
<java-symbol type="bool" name="config_supportAutoRotation" />
+ <java-symbol type="bool" name="config_dockedStackDividerFreeSnapMode" />
<java-symbol type="dimen" name="docked_stack_divider_thickness" />
<java-symbol type="dimen" name="docked_stack_divider_insets" />
<java-symbol type="dimen" name="docked_stack_minimize_thickness" />
diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
index 74ca2036d764..bc0cdc1e029b 100644
--- a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
@@ -34,6 +34,8 @@ import static com.android.internal.app.ChooserListAdapter.SHORTCUT_TARGET_SCORE_
import static com.android.internal.app.ChooserWrapperActivity.sOverrides;
import static com.android.internal.app.MatcherUtils.first;
+import static junit.framework.Assert.assertTrue;
+
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.CoreMatchers.notNullValue;
@@ -1682,6 +1684,54 @@ public class ChooserActivityTest {
.SharesheetStandardEvent.SHARESHEET_PROFILE_CHANGED.getId()));
}
+ @Test
+ public void testAutolaunch_singleTarget_wifthWorkProfileAndTabbedViewOff_noAutolaunch() {
+ ResolverActivity.ENABLE_TABBED_VIEW = false;
+ List<ResolvedComponentInfo> personalResolvedComponentInfos =
+ createResolvedComponentsForTestWithOtherProfile(2, /* userId */ 10);
+ when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class)))
+ .thenReturn(new ArrayList<>(personalResolvedComponentInfos));
+ Intent sendIntent = createSendTextIntent();
+ sendIntent.setType("TestType");
+ ResolveInfo[] chosen = new ResolveInfo[1];
+ sOverrides.onSafelyStartCallback = targetInfo -> {
+ chosen[0] = targetInfo.getResolveInfo();
+ return true;
+ };
+ waitForIdle();
+
+ mActivityRule.launchActivity(Intent.createChooser(sendIntent, "work tab test"));
+ waitForIdle();
+
+ assertTrue(chosen[0] == null);
+ }
+
+ @Test
+ public void testAutolaunch_singleTarget_noWorkProfile_autolaunch() {
+ ResolverActivity.ENABLE_TABBED_VIEW = false;
+ List<ResolvedComponentInfo> personalResolvedComponentInfos =
+ createResolvedComponentsForTest(1);
+ when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class)))
+ .thenReturn(new ArrayList<>(personalResolvedComponentInfos));
+ Intent sendIntent = createSendTextIntent();
+ sendIntent.setType("TestType");
+ ResolveInfo[] chosen = new ResolveInfo[1];
+ sOverrides.onSafelyStartCallback = targetInfo -> {
+ chosen[0] = targetInfo.getResolveInfo();
+ return true;
+ };
+ waitForIdle();
+
+ mActivityRule.launchActivity(Intent.createChooser(sendIntent, "work tab test"));
+ waitForIdle();
+
+ assertThat(chosen[0], is(personalResolvedComponentInfos.get(0).getResolveInfoAt(0)));
+ }
+
private Intent createSendTextIntent() {
Intent sendIntent = new Intent();
sendIntent.setAction(Intent.ACTION_SEND);
diff --git a/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
index 5a3aff937b79..0bf8663c7a85 100644
--- a/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
@@ -30,6 +30,8 @@ import static com.android.internal.app.MatcherUtils.first;
import static com.android.internal.app.ResolverDataProvider.createPackageManagerMockedInfo;
import static com.android.internal.app.ResolverWrapperActivity.sOverrides;
+import static junit.framework.Assert.assertTrue;
+
import static org.hamcrest.CoreMatchers.allOf;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.not;
@@ -710,6 +712,54 @@ public class ResolverActivityTest {
.check(matches(isDisplayed()));
}
+ @Test
+ public void testAutolaunch_singleTarget_withWorkProfileAndTabbedViewOff_noAutolaunch() {
+ ResolverActivity.ENABLE_TABBED_VIEW = false;
+ List<ResolvedComponentInfo> personalResolvedComponentInfos =
+ createResolvedComponentsForTestWithOtherProfile(2, /* userId */ 10);
+ when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class)))
+ .thenReturn(new ArrayList<>(personalResolvedComponentInfos));
+ Intent sendIntent = createSendImageIntent();
+ sendIntent.setType("TestType");
+ ResolveInfo[] chosen = new ResolveInfo[1];
+ sOverrides.onSafelyStartCallback = targetInfo -> {
+ chosen[0] = targetInfo.getResolveInfo();
+ return true;
+ };
+ waitForIdle();
+
+ mActivityRule.launchActivity(sendIntent);
+ waitForIdle();
+
+ assertTrue(chosen[0] == null);
+ }
+
+ @Test
+ public void testAutolaunch_singleTarget_noWorkProfile_autolaunch() {
+ ResolverActivity.ENABLE_TABBED_VIEW = false;
+ List<ResolvedComponentInfo> personalResolvedComponentInfos =
+ createResolvedComponentsForTest(1);
+ when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class)))
+ .thenReturn(new ArrayList<>(personalResolvedComponentInfos));
+ Intent sendIntent = createSendImageIntent();
+ sendIntent.setType("TestType");
+ ResolveInfo[] chosen = new ResolveInfo[1];
+ sOverrides.onSafelyStartCallback = targetInfo -> {
+ chosen[0] = targetInfo.getResolveInfo();
+ return true;
+ };
+ waitForIdle();
+
+ mActivityRule.launchActivity(sendIntent);
+ waitForIdle();
+
+ assertThat(chosen[0], is(personalResolvedComponentInfos.get(0).getResolveInfoAt(0)));
+ }
+
private Intent createSendImageIntent() {
Intent sendIntent = new Intent();
sendIntent.setAction(Intent.ACTION_SEND);
diff --git a/core/tests/coretests/src/com/android/internal/policy/DecorContextTest.java b/core/tests/coretests/src/com/android/internal/policy/DecorContextTest.java
index d019704fb684..3e40466e4b64 100644
--- a/core/tests/coretests/src/com/android/internal/policy/DecorContextTest.java
+++ b/core/tests/coretests/src/com/android/internal/policy/DecorContextTest.java
@@ -20,24 +20,19 @@ import static android.view.Display.DEFAULT_DISPLAY;
import static org.junit.Assert.assertEquals;
-import android.app.Activity;
-import android.app.EmptyActivity;
import android.content.Context;
import android.hardware.display.DisplayManagerGlobal;
import android.platform.test.annotations.Presubmit;
import android.view.Display;
import android.view.DisplayAdjustments;
import android.view.DisplayInfo;
-import android.view.WindowManager;
-import android.view.WindowManagerImpl;
-import androidx.test.core.app.ApplicationProvider;
+import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
-import androidx.test.rule.ActivityTestRule;
import androidx.test.runner.AndroidJUnit4;
+
import org.junit.Before;
-import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -51,13 +46,9 @@ public final class DecorContextTest {
private Context mContext;
private static final int EXTERNAL_DISPLAY = DEFAULT_DISPLAY + 1;
- @Rule
- public ActivityTestRule<EmptyActivity> mActivityRule =
- new ActivityTestRule<>(EmptyActivity.class);
-
@Before
- public void setUp() {
- mContext = ApplicationProvider.getApplicationContext();
+ public void setUp() throws Exception {
+ mContext = InstrumentationRegistry.getContext();
}
@Test
@@ -85,19 +76,4 @@ public final class DecorContextTest {
Display associatedDisplay = decorContext.getDisplay();
assertEquals(expectedDisplayId, associatedDisplay.getDisplayId());
}
-
- @Test
- public void testGetWindowManagerFromVisualDecorContext() throws Throwable {
- mActivityRule.runOnUiThread(() -> {
- Activity activity = mActivityRule.getActivity();
- final DecorContext decorContext = new DecorContext(mContext.getApplicationContext(),
- activity);
- WindowManagerImpl actualWm = (WindowManagerImpl)
- decorContext.getSystemService(WindowManager.class);
- WindowManagerImpl expectedWm = (WindowManagerImpl)
- activity.getSystemService(WindowManager.class);
- // Verify that window manager is from activity not application context.
- assertEquals(expectedWm.mContext, actualWm.mContext);
- });
- }
}
diff --git a/media/java/android/media/projection/MediaProjection.java b/media/java/android/media/projection/MediaProjection.java
index 632cfb0f1e30..37e141537c79 100644
--- a/media/java/android/media/projection/MediaProjection.java
+++ b/media/java/android/media/projection/MediaProjection.java
@@ -21,6 +21,7 @@ import android.annotation.Nullable;
import android.content.Context;
import android.hardware.display.DisplayManager;
import android.hardware.display.VirtualDisplay;
+import android.hardware.display.VirtualDisplayConfig;
import android.media.projection.IMediaProjection;
import android.media.projection.IMediaProjectionCallback;
import android.os.Handler;
@@ -100,11 +101,18 @@ public final class MediaProjection {
int width, int height, int dpi, boolean isSecure, @Nullable Surface surface,
@Nullable VirtualDisplay.Callback callback, @Nullable Handler handler) {
DisplayManager dm = (DisplayManager) mContext.getSystemService(Context.DISPLAY_SERVICE);
- int flags = isSecure ? DisplayManager.VIRTUAL_DISPLAY_FLAG_SECURE : 0;
- return dm.createVirtualDisplay(this, name, width, height, dpi, surface,
- flags | DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR |
- DisplayManager.VIRTUAL_DISPLAY_FLAG_PRESENTATION, callback, handler,
- null /* uniqueId */);
+ int flags = DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR
+ | DisplayManager.VIRTUAL_DISPLAY_FLAG_PRESENTATION;
+ if (isSecure) {
+ flags |= DisplayManager.VIRTUAL_DISPLAY_FLAG_SECURE;
+ }
+ final VirtualDisplayConfig.Builder builder = new VirtualDisplayConfig.Builder(name, width,
+ height, dpi);
+ builder.setFlags(flags);
+ if (surface != null) {
+ builder.setSurface(surface);
+ }
+ return dm.createVirtualDisplay(this, builder.build(), callback, handler);
}
/**
@@ -133,9 +141,35 @@ public final class MediaProjection {
public VirtualDisplay createVirtualDisplay(@NonNull String name,
int width, int height, int dpi, int flags, @Nullable Surface surface,
@Nullable VirtualDisplay.Callback callback, @Nullable Handler handler) {
- DisplayManager dm = (DisplayManager) mContext.getSystemService(Context.DISPLAY_SERVICE);
- return dm.createVirtualDisplay(this, name, width, height, dpi, surface, flags, callback,
- handler, null /* uniqueId */);
+ final VirtualDisplayConfig.Builder builder = new VirtualDisplayConfig.Builder(name, width,
+ height, dpi);
+ builder.setFlags(flags);
+ if (surface != null) {
+ builder.setSurface(surface);
+ }
+ return createVirtualDisplay(builder.build(), callback, handler);
+ }
+
+ /**
+ * Creates a {@link android.hardware.display.VirtualDisplay} to capture the
+ * contents of the screen.
+ *
+ * @param virtualDisplayConfig The arguments for the virtual display configuration. See
+ * {@link VirtualDisplayConfig} for using it.
+ * @param callback Callback to call when the virtual display's state
+ * changes, or null if none.
+ * @param handler The {@link android.os.Handler} on which the callback should be
+ * invoked, or null if the callback should be invoked on the calling
+ * thread's main {@link android.os.Looper}.
+ *
+ * @see android.hardware.display.VirtualDisplay
+ * @hide
+ */
+ @Nullable
+ public VirtualDisplay createVirtualDisplay(@NonNull VirtualDisplayConfig virtualDisplayConfig,
+ @Nullable VirtualDisplay.Callback callback, @Nullable Handler handler) {
+ DisplayManager dm = mContext.getSystemService(DisplayManager.class);
+ return dm.createVirtualDisplay(this, virtualDisplayConfig, callback, handler);
}
/**
diff --git a/packages/SystemUI/res/layout/bubble_overflow_activity.xml b/packages/SystemUI/res/layout/bubble_overflow_activity.xml
index 65b04fd8fd99..b3c7cf74941a 100644
--- a/packages/SystemUI/res/layout/bubble_overflow_activity.xml
+++ b/packages/SystemUI/res/layout/bubble_overflow_activity.xml
@@ -20,6 +20,8 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingTop="@dimen/bubble_overflow_padding"
+ android:paddingLeft="@dimen/bubble_overflow_padding"
+ android:paddingRight="@dimen/bubble_overflow_padding"
android:orientation="vertical"
android:layout_gravity="center_horizontal">
diff --git a/packages/SystemUI/res/layout/bubble_overflow_view.xml b/packages/SystemUI/res/layout/bubble_overflow_view.xml
index d67c81d67ada..88a05ec5824a 100644
--- a/packages/SystemUI/res/layout/bubble_overflow_view.xml
+++ b/packages/SystemUI/res/layout/bubble_overflow_view.xml
@@ -37,5 +37,6 @@
android:layout_height="wrap_content"
android:maxLines="1"
android:layout_gravity="center"
+ android:paddingTop="@dimen/bubble_overflow_text_padding"
android:gravity="center"/>
</LinearLayout>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index a9e5fa9cf4ae..9074dad608ce 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1148,11 +1148,13 @@
<!-- Default (and minimum) height of the expanded view shown when the bubble is expanded -->
<dimen name="bubble_expanded_default_height">180dp</dimen>
<!-- Default height of bubble overflow -->
- <dimen name="bubble_overflow_height">380dp</dimen>
+ <dimen name="bubble_overflow_height">460dp</dimen>
<!-- Bubble overflow padding when there are no bubbles -->
<dimen name="bubble_overflow_empty_state_padding">16dp</dimen>
<!-- Padding of container for overflow bubbles -->
- <dimen name="bubble_overflow_padding">5dp</dimen>
+ <dimen name="bubble_overflow_padding">15dp</dimen>
+ <!-- Padding of label for bubble overflow view -->
+ <dimen name="bubble_overflow_text_padding">7dp</dimen>
<!-- Height of the triangle that points to the expanded bubble -->
<dimen name="bubble_pointer_height">4dp</dimen>
<!-- Width of the triangle that points to the expanded bubble -->
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java
index 2231d11b7bc2..3f985ef37746 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java
@@ -81,11 +81,15 @@ public class BubbleOverflowActivity extends Activity {
DisplayMetrics displayMetrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
- final int viewWidth = displayMetrics.widthPixels / columns;
+ final int recyclerViewWidth = (displayMetrics.widthPixels
+ - res.getDimensionPixelSize(R.dimen.bubble_overflow_padding));
+ final int viewWidth = recyclerViewWidth / columns;
final int maxOverflowBubbles = res.getInteger(R.integer.bubbles_max_overflow);
final int rows = (int) Math.ceil((double) maxOverflowBubbles / columns);
- final int viewHeight = res.getDimensionPixelSize(R.dimen.bubble_overflow_height) / rows;
+ final int recyclerViewHeight = res.getDimensionPixelSize(R.dimen.bubble_overflow_height)
+ - res.getDimensionPixelSize(R.dimen.bubble_overflow_padding);
+ final int viewHeight = recyclerViewHeight / rows;
mAdapter = new BubbleOverflowAdapter(mOverflowBubbles,
mBubbleController::promoteBubbleFromOverflow, viewWidth, viewHeight);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/DynamicChildBindController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/DynamicChildBindController.java
index 059d6ffa9180..148cdea92052 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/DynamicChildBindController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/DynamicChildBindController.java
@@ -95,8 +95,8 @@ public class DynamicChildBindController {
private void freeChildContent(NotificationEntry entry) {
RowContentBindParams params = mStage.getStageParams(entry);
- params.freeContentViews(FLAG_CONTENT_VIEW_CONTRACTED);
- params.freeContentViews(FLAG_CONTENT_VIEW_EXPANDED);
+ params.markContentViewsFreeable(FLAG_CONTENT_VIEW_CONTRACTED);
+ params.markContentViewsFreeable(FLAG_CONTENT_VIEW_EXPANDED);
mStage.requestRebind(entry, null);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
index d2f781d2e19c..77376e595819 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -539,7 +539,8 @@ public class NotificationEntryManager implements
}
}
- private void addNotificationInternal(StatusBarNotification notification,
+ private void addNotificationInternal(
+ StatusBarNotification notification,
RankingMap rankingMap) throws InflationException {
String key = notification.getKey();
if (DEBUG) {
@@ -579,6 +580,9 @@ public class NotificationEntryManager implements
for (NotifCollectionListener listener : mNotifCollectionListeners) {
listener.onEntryAdded(entry);
}
+ for (NotifCollectionListener listener : mNotifCollectionListeners) {
+ listener.onRankingApplied();
+ }
}
public void addNotification(StatusBarNotification notification, RankingMap ranking) {
@@ -635,6 +639,9 @@ public class NotificationEntryManager implements
for (NotificationEntryListener listener : mNotificationEntryListeners) {
listener.onPostEntryUpdated(entry);
}
+ for (NotifCollectionListener listener : mNotifCollectionListeners) {
+ listener.onRankingApplied();
+ }
}
public void updateNotification(StatusBarNotification notification, RankingMap ranking) {
@@ -693,6 +700,9 @@ public class NotificationEntryManager implements
for (NotifCollectionListener listener : mNotifCollectionListeners) {
listener.onRankingUpdate(rankingMap);
}
+ for (NotifCollectionListener listener : mNotifCollectionListeners) {
+ listener.onRankingApplied();
+ }
}
private void updateRankingOfPendingNotifications(@Nullable RankingMap rankingMap) {
@@ -799,6 +809,9 @@ public class NotificationEntryManager implements
*/
public void updateRanking(RankingMap rankingMap, String reason) {
updateRankingAndSort(rankingMap, reason);
+ for (NotifCollectionListener listener : mNotifCollectionListeners) {
+ listener.onRankingApplied();
+ }
}
/** Resorts / filters the current notification set with the current RankingMap */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
index 0377c0900d63..d22564b2a811 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
@@ -67,7 +67,6 @@ import com.android.systemui.statusbar.notification.icon.IconPack;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRowController;
import com.android.systemui.statusbar.notification.row.NotificationGuts;
-import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag;
import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager;
import java.util.ArrayList;
@@ -579,10 +578,6 @@ public final class NotificationEntry extends ListEntry {
if (row != null) row.resetUserExpansion();
}
- public void freeContentViewWhenSafe(@InflationFlag int inflationFlag) {
- if (row != null) row.freeContentViewWhenSafe(inflationFlag);
- }
-
public boolean rowExists() {
return row != null;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.java
index 573c129f199a..2a3b2b7d815d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.java
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.notification.collection.coordinator;
import static com.android.systemui.statusbar.NotificationRemoteInputManager.FORCE_REMOTE_INPUT_HISTORY;
+import static com.android.systemui.statusbar.notification.interruption.NotificationAlertingManager.alertAgain;
import android.annotation.Nullable;
@@ -28,6 +29,8 @@ import com.android.systemui.statusbar.notification.collection.listbuilder.plugga
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSection;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender;
+import com.android.systemui.statusbar.notification.headsup.HeadsUpViewBinder;
+import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
@@ -55,6 +58,8 @@ public class HeadsUpCoordinator implements Coordinator {
private static final String TAG = "HeadsUpCoordinator";
private final HeadsUpManager mHeadsUpManager;
+ private final HeadsUpViewBinder mHeadsUpViewBinder;
+ private final NotificationInterruptStateProvider mNotificationInterruptStateProvider;
private final NotificationRemoteInputManager mRemoteInputManager;
// tracks the current HeadUpNotification reported by HeadsUpManager
@@ -66,8 +71,12 @@ public class HeadsUpCoordinator implements Coordinator {
@Inject
public HeadsUpCoordinator(
HeadsUpManager headsUpManager,
+ HeadsUpViewBinder headsUpViewBinder,
+ NotificationInterruptStateProvider notificationInterruptStateProvider,
NotificationRemoteInputManager remoteInputManager) {
mHeadsUpManager = headsUpManager;
+ mHeadsUpViewBinder = headsUpViewBinder;
+ mNotificationInterruptStateProvider = notificationInterruptStateProvider;
mRemoteInputManager = remoteInputManager;
}
@@ -84,8 +93,51 @@ public class HeadsUpCoordinator implements Coordinator {
return mNotifSection;
}
+ private void onHeadsUpViewBound(NotificationEntry entry) {
+ mHeadsUpManager.showNotification(entry);
+ }
+
private final NotifCollectionListener mNotifCollectionListener = new NotifCollectionListener() {
/**
+ * Notification was just added and if it should heads up, bind the view and then show it.
+ */
+ @Override
+ public void onEntryAdded(NotificationEntry entry) {
+ if (mNotificationInterruptStateProvider.shouldHeadsUp(entry)) {
+ mHeadsUpViewBinder.bindHeadsUpView(
+ entry,
+ HeadsUpCoordinator.this::onHeadsUpViewBound);
+ }
+ }
+
+ /**
+ * Notification could've updated to be heads up or not heads up. Even if it did update to
+ * heads up, if the notification specified that it only wants to alert once, don't heads
+ * up again.
+ */
+ @Override
+ public void onEntryUpdated(NotificationEntry entry) {
+ boolean hunAgain = alertAgain(entry, entry.getSbn().getNotification());
+ // includes check for whether this notification should be filtered:
+ boolean shouldHeadsUp = mNotificationInterruptStateProvider.shouldHeadsUp(entry);
+ final boolean wasHeadsUp = mHeadsUpManager.isAlerting(entry.getKey());
+ if (wasHeadsUp) {
+ if (shouldHeadsUp) {
+ mHeadsUpManager.updateNotification(entry.getKey(), hunAgain);
+ } else if (!mHeadsUpManager.isEntryAutoHeadsUpped(entry.getKey())) {
+ // We don't want this to be interrupting anymore, let's remove it
+ mHeadsUpManager.removeNotification(
+ entry.getKey(), false /* removeImmediately */);
+ }
+ } else if (shouldHeadsUp && hunAgain) {
+ // This notification was updated to be heads up, show it!
+ mHeadsUpViewBinder.bindHeadsUpView(
+ entry,
+ HeadsUpCoordinator.this::onHeadsUpViewBound);
+ }
+ }
+
+ /**
* Stop alerting HUNs that are removed from the notification collection
*/
@Override
@@ -98,6 +150,11 @@ public class HeadsUpCoordinator implements Coordinator {
mHeadsUpManager.removeNotification(entry.getKey(), removeImmediatelyForRemoteInput);
}
}
+
+ @Override
+ public void onEntryCleanUp(NotificationEntry entry) {
+ mHeadsUpViewBinder.abortBindCallback(entry);
+ }
};
private final NotifLifetimeExtender mLifetimeExtender = new NotifLifetimeExtender() {
@@ -153,6 +210,9 @@ public class HeadsUpCoordinator implements Coordinator {
mNotifPromoter.invalidateList();
mNotifSection.invalidateList();
}
+ if (!isHeadsUp) {
+ mHeadsUpViewBinder.unbindHeadsUpView(entry);
+ }
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java
index 742615c7fd0f..9973ef9ae14e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java
@@ -33,9 +33,7 @@ import com.android.systemui.statusbar.notification.collection.inflation.NotifInf
import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeFinalizeFilterListener;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
-import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
import com.android.systemui.statusbar.notification.row.NotifInflationErrorManager;
-import com.android.systemui.statusbar.policy.HeadsUpManager;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -63,8 +61,6 @@ public class PreparationCoordinator implements Coordinator {
private final NotifViewBarn mViewBarn;
private final Map<NotificationEntry, Integer> mInflationStates = new ArrayMap<>();
private final IStatusBarService mStatusBarService;
- private final NotificationInterruptStateProvider mNotificationInterruptStateProvider;
- private final HeadsUpManager mHeadsUpManager;
@Inject
public PreparationCoordinator(
@@ -72,9 +68,7 @@ public class PreparationCoordinator implements Coordinator {
NotifInflaterImpl notifInflater,
NotifInflationErrorManager errorManager,
NotifViewBarn viewBarn,
- IStatusBarService service,
- NotificationInterruptStateProvider notificationInterruptStateProvider,
- HeadsUpManager headsUpManager
+ IStatusBarService service
) {
mLogger = logger;
mNotifInflater = notifInflater;
@@ -83,8 +77,6 @@ public class PreparationCoordinator implements Coordinator {
mNotifErrorManager.addInflationErrorListener(mInflationErrorListener);
mViewBarn = viewBarn;
mStatusBarService = service;
- mNotificationInterruptStateProvider = notificationInterruptStateProvider;
- mHeadsUpManager = headsUpManager;
}
@Override
@@ -158,11 +150,6 @@ public class PreparationCoordinator implements Coordinator {
mLogger.logNotifInflated(entry.getKey());
mViewBarn.registerViewForEntry(entry, entry.getRow());
mInflationStates.put(entry, STATE_INFLATED);
-
- // TODO: should eventually be moved to HeadsUpCoordinator
- if (mNotificationInterruptStateProvider.shouldHeadsUp(entry)) {
- mHeadsUpManager.showNotification(entry);
- }
mNotifInflatingFilter.invalidateList();
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
index 723728488826..32f1822804f8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
@@ -16,8 +16,6 @@
package com.android.systemui.statusbar.notification.collection.inflation;
-import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_HEADS_UP;
-
import android.annotation.Nullable;
import android.content.Context;
import android.content.pm.ApplicationInfo;
@@ -227,24 +225,17 @@ public class NotificationRowBinderImpl implements NotificationRowBinder {
final boolean useIncreasedCollapsedHeight =
mMessagingUtil.isImportantMessaging(sbn, entry.getImportance());
- final boolean useIncreasedHeadsUp = useIncreasedCollapsedHeight
- && !mPresenter.isPresenterFullyCollapsed();
final boolean isLowPriority = entry.isAmbient();
RowContentBindParams params = mRowContentBindStage.getStageParams(entry);
params.setUseIncreasedCollapsedHeight(useIncreasedCollapsedHeight);
- params.setUseIncreasedHeadsUpHeight(useIncreasedHeadsUp);
params.setUseLowPriority(entry.isAmbient());
- if (mNotificationInterruptStateProvider.shouldHeadsUp(entry)) {
- params.requireContentViews(FLAG_CONTENT_VIEW_HEADS_UP);
- }
//TODO: Replace this API with RowContentBindParams directly
row.setNeedsRedaction(mNotificationLockscreenUserManager.needsRedaction(entry));
params.rebindAllContentViews();
mRowContentBindStage.requestRebind(entry, en -> {
row.setUsesIncreasedCollapsedHeight(useIncreasedCollapsedHeight);
- row.setUsesIncreasedHeadsUpHeight(useIncreasedHeadsUp);
row.setIsLowPriority(isLowPriority);
mInflationCallback.onAsyncInflationFinished(en);
});
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionListener.java
index 0c0cded32db2..41ca52d5a626 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionListener.java
@@ -74,7 +74,9 @@ public interface NotifCollectionListener {
* non-lifetime-extended notification entries will have their ranking object updated.
*
* Ranking updates occur whenever a notification is added, updated, or removed, or when a
- * standalone ranking is sent from the server.
+ * standalone ranking is sent from the server. If a non-standalone ranking is applied, the event
+ * that accompanied the ranking is emitted first (e.g. {@link #onEntryAdded}), followed by the
+ * ranking event.
*/
default void onRankingApplied() {
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpBindController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpBindController.java
new file mode 100644
index 000000000000..a7b1f37edf0e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpBindController.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.headsup;
+
+import androidx.annotation.NonNull;
+
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.coordinator.HeadsUpCoordinator;
+import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
+import com.android.systemui.statusbar.notification.interruption.NotificationAlertingManager;
+import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
+import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ * Controller class for old pipeline heads up view binding. It listens to
+ * {@link NotificationEntryManager} entry events and appropriately binds or unbinds the heads up
+ * view.
+ *
+ * This has a subtle contract with {@link NotificationAlertingManager} where this controller handles
+ * the heads up binding, but {@link NotificationAlertingManager} listens for general inflation
+ * events to actually mark it heads up/update. In the new pipeline, we combine the classes.
+ * See {@link HeadsUpCoordinator}.
+ */
+@Singleton
+public class HeadsUpBindController {
+ private final HeadsUpViewBinder mHeadsUpViewBinder;
+ private final NotificationInterruptStateProvider mInterruptStateProvider;
+
+ @Inject
+ HeadsUpBindController(
+ HeadsUpViewBinder headsUpViewBinder,
+ NotificationInterruptStateProvider notificationInterruptStateProvider) {
+ mInterruptStateProvider = notificationInterruptStateProvider;
+ mHeadsUpViewBinder = headsUpViewBinder;
+ }
+
+ /**
+ * Attach this controller and add its listeners.
+ */
+ public void attach(
+ NotificationEntryManager entryManager,
+ HeadsUpManager headsUpManager) {
+ entryManager.addCollectionListener(mCollectionListener);
+ headsUpManager.addListener(mOnHeadsUpChangedListener);
+ }
+
+ private NotifCollectionListener mCollectionListener = new NotifCollectionListener() {
+ @Override
+ public void onEntryAdded(NotificationEntry entry) {
+ if (mInterruptStateProvider.shouldHeadsUp(entry)) {
+ mHeadsUpViewBinder.bindHeadsUpView(entry, null);
+ }
+ }
+
+ @Override
+ public void onEntryUpdated(NotificationEntry entry) {
+ if (mInterruptStateProvider.shouldHeadsUp(entry)) {
+ mHeadsUpViewBinder.bindHeadsUpView(entry, null);
+ }
+ }
+
+ @Override
+ public void onEntryCleanUp(NotificationEntry entry) {
+ mHeadsUpViewBinder.abortBindCallback(entry);
+ }
+ };
+
+ private OnHeadsUpChangedListener mOnHeadsUpChangedListener = new OnHeadsUpChangedListener() {
+ @Override
+ public void onHeadsUpStateChanged(@NonNull NotificationEntry entry, boolean isHeadsUp) {
+ if (!isHeadsUp) {
+ mHeadsUpViewBinder.unbindHeadsUpView(entry);
+ }
+ }
+ };
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpViewBinder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpViewBinder.java
new file mode 100644
index 000000000000..37acfa8dc0a4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpViewBinder.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.headsup;
+
+import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_HEADS_UP;
+
+import android.util.ArrayMap;
+
+import androidx.annotation.Nullable;
+import androidx.core.os.CancellationSignal;
+
+import com.android.internal.util.NotificationMessagingUtil;
+import com.android.systemui.statusbar.NotificationPresenter;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.coordinator.HeadsUpCoordinator;
+import com.android.systemui.statusbar.notification.row.NotifBindPipeline.BindCallback;
+import com.android.systemui.statusbar.notification.row.RowContentBindParams;
+import com.android.systemui.statusbar.notification.row.RowContentBindStage;
+
+import java.util.Map;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ * Wrapper around heads up view binding logic. {@link HeadsUpViewBinder} is responsible for
+ * figuring out the right heads up inflation parameters and inflating/freeing the heads up
+ * content view.
+ *
+ * TODO: This should be moved into {@link HeadsUpCoordinator} when the old pipeline is deprecated
+ * (i.e. when {@link HeadsUpBindController} is removed).
+ */
+@Singleton
+public class HeadsUpViewBinder {
+ private final RowContentBindStage mStage;
+ private final NotificationMessagingUtil mNotificationMessagingUtil;
+ private final Map<NotificationEntry, CancellationSignal> mOngoingBindCallbacks =
+ new ArrayMap<>();
+
+ private NotificationPresenter mNotificationPresenter;
+
+ @Inject
+ HeadsUpViewBinder(
+ NotificationMessagingUtil notificationMessagingUtil,
+ RowContentBindStage bindStage) {
+ mNotificationMessagingUtil = notificationMessagingUtil;
+ mStage = bindStage;
+ }
+
+ /**
+ * Set notification presenter to determine parameters for heads up view inflation.
+ */
+ public void setPresenter(NotificationPresenter presenter) {
+ mNotificationPresenter = presenter;
+ }
+
+ /**
+ * Bind heads up view to the notification row.
+ * @param callback callback after heads up view is bound
+ */
+ public void bindHeadsUpView(NotificationEntry entry, @Nullable BindCallback callback) {
+ RowContentBindParams params = mStage.getStageParams(entry);
+ final boolean isImportantMessage = mNotificationMessagingUtil.isImportantMessaging(
+ entry.getSbn(), entry.getImportance());
+ final boolean useIncreasedHeadsUp = isImportantMessage
+ && !mNotificationPresenter.isPresenterFullyCollapsed();
+ params.setUseIncreasedHeadsUpHeight(useIncreasedHeadsUp);
+ params.requireContentViews(FLAG_CONTENT_VIEW_HEADS_UP);
+ CancellationSignal signal = mStage.requestRebind(entry, en -> {
+ en.getRow().setUsesIncreasedHeadsUpHeight(params.useIncreasedHeadsUpHeight());
+ if (callback != null) {
+ callback.onBindFinished(en);
+ }
+ });
+ abortBindCallback(entry);
+ mOngoingBindCallbacks.put(entry, signal);
+ }
+
+ /**
+ * Abort any callbacks waiting for heads up view binding to finish for a given notification.
+ * @param entry notification with bind in progress
+ */
+ public void abortBindCallback(NotificationEntry entry) {
+ CancellationSignal ongoingBindCallback = mOngoingBindCallbacks.remove(entry);
+ if (ongoingBindCallback != null) {
+ ongoingBindCallback.cancel();
+ }
+ }
+
+ /**
+ * Unbind the heads up view from the notification row.
+ */
+ public void unbindHeadsUpView(NotificationEntry entry) {
+ abortBindCallback(entry);
+ mStage.getStageParams(entry).markContentViewsFreeable(FLAG_CONTENT_VIEW_HEADS_UP);
+ mStage.requestRebind(entry, null);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
index 7a7178c3464a..d1cceaeb6dd5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
@@ -28,6 +28,7 @@ import com.android.systemui.statusbar.notification.NotificationEntryManager
import com.android.systemui.statusbar.notification.NotificationListController
import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl
import com.android.systemui.statusbar.notification.collection.init.NotifPipelineInitializer
+import com.android.systemui.statusbar.notification.headsup.HeadsUpBindController
import com.android.systemui.statusbar.notification.row.NotifBindPipelineInitializer
import com.android.systemui.statusbar.notification.stack.NotificationListContainer
import com.android.systemui.statusbar.phone.NotificationGroupAlertTransferHelper
@@ -35,6 +36,7 @@ import com.android.systemui.statusbar.phone.NotificationGroupManager
import com.android.systemui.statusbar.phone.StatusBar
import com.android.systemui.statusbar.policy.DeviceProvisionedController
import com.android.systemui.statusbar.policy.HeadsUpManager
+import com.android.systemui.statusbar.notification.headsup.HeadsUpViewBinder
import com.android.systemui.statusbar.policy.RemoteInputUriController
import dagger.Lazy
import java.io.FileDescriptor
@@ -63,7 +65,9 @@ class NotificationsControllerImpl @Inject constructor(
private val bubbleController: BubbleController,
private val groupManager: NotificationGroupManager,
private val groupAlertTransferHelper: NotificationGroupAlertTransferHelper,
- private val headsUpManager: HeadsUpManager
+ private val headsUpManager: HeadsUpManager,
+ private val headsUpBindController: HeadsUpBindController,
+ private val headsUpViewBinder: HeadsUpViewBinder
) : NotificationsController {
override fun initialize(
@@ -91,6 +95,7 @@ class NotificationsControllerImpl @Inject constructor(
presenter,
listContainer,
bindRowCallback)
+ headsUpViewBinder.setPresenter(presenter)
notifBindPipelineInitializer.initialize()
if (featureFlags.isNewNotifPipelineEnabled) {
@@ -109,6 +114,7 @@ class NotificationsControllerImpl @Inject constructor(
groupAlertTransferHelper.bind(entryManager, groupManager)
headsUpManager.addListener(groupManager)
headsUpManager.addListener(groupAlertTransferHelper)
+ headsUpBindController.attach(entryManager, headsUpManager)
groupManager.setHeadsUpManager(headsUpManager)
groupAlertTransferHelper.setHeadsUpManager(headsUpManager)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationAlertingManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationAlertingManager.java
index b5725029450d..5d070981f81b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationAlertingManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationAlertingManager.java
@@ -17,7 +17,6 @@
package com.android.systemui.statusbar.notification.interruption;
import static com.android.systemui.statusbar.NotificationRemoteInputManager.FORCE_REMOTE_INPUT_HISTORY;
-import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_HEADS_UP;
import android.app.Notification;
import android.service.notification.StatusBarNotification;
@@ -95,16 +94,10 @@ public class NotificationAlertingManager {
// TODO: Instead of this back and forth, we should listen to changes in heads up and
// cancel on-going heads up view inflation using the bind pipeline.
if (entry.getRow().getPrivateLayout().getHeadsUpChild() != null) {
- // Possible for shouldHeadsUp to change between the inflation starting and ending.
- // If it does and we no longer need to heads up, we should free the view.
- if (mNotificationInterruptStateProvider.shouldHeadsUp(entry)) {
- mHeadsUpManager.showNotification(entry);
- if (!mStatusBarStateController.isDozing()) {
- // Mark as seen immediately
- setNotificationShown(entry.getSbn());
- }
- } else {
- entry.freeContentViewWhenSafe(FLAG_CONTENT_VIEW_HEADS_UP);
+ mHeadsUpManager.showNotification(entry);
+ if (!mStatusBarStateController.isDozing()) {
+ // Mark as seen immediately
+ setNotificationShown(entry.getSbn());
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 255c2ea808e0..2917346153d2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -17,12 +17,7 @@
package com.android.systemui.statusbar.notification.row;
import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator.ExpandAnimationParameters;
-import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_CONTRACTED;
-import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_EXPANDED;
import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_HEADSUP;
-import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_CONTRACTED;
-import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_EXPANDED;
-import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_HEADS_UP;
import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_PUBLIC;
import android.animation.Animator;
@@ -463,41 +458,16 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
* Marks a content view as freeable, setting it so that future inflations do not reinflate
* and ensuring that the view is freed when it is safe to remove.
*
- * TODO: This should be moved to the respective coordinator and call
- * {@link RowContentBindParams#freeContentViews} directly after disappear animation
- * finishes instead of depending on binding API to know when it's "safe".
- *
* @param inflationFlag flag corresponding to the content view to be freed
+ * @deprecated By default, {@link NotificationContentInflater#unbindContent} will tell the
+ * view hierarchy to only free when the view is safe to remove so this method is no longer
+ * needed. Will remove when all uses are gone.
*/
+ @Deprecated
public void freeContentViewWhenSafe(@InflationFlag int inflationFlag) {
- // View should not be reinflated in the future
- Runnable freeViewRunnable = () -> {
- // Possible for notification to be removed after free request.
- if (!isRemoved()) {
- RowContentBindParams params = mRowContentBindStage.getStageParams(mEntry);
- params.freeContentViews(inflationFlag);
- mRowContentBindStage.requestRebind(mEntry, null /* callback */);
- }
- };
- switch (inflationFlag) {
- case FLAG_CONTENT_VIEW_CONTRACTED:
- getPrivateLayout().performWhenContentInactive(VISIBLE_TYPE_CONTRACTED,
- freeViewRunnable);
- break;
- case FLAG_CONTENT_VIEW_EXPANDED:
- getPrivateLayout().performWhenContentInactive(VISIBLE_TYPE_EXPANDED,
- freeViewRunnable);
- break;
- case FLAG_CONTENT_VIEW_HEADS_UP:
- getPrivateLayout().performWhenContentInactive(VISIBLE_TYPE_HEADSUP,
- freeViewRunnable);
- break;
- case FLAG_CONTENT_VIEW_PUBLIC:
- getPublicLayout().performWhenContentInactive(VISIBLE_TYPE_CONTRACTED,
- freeViewRunnable);
- default:
- break;
- }
+ RowContentBindParams params = mRowContentBindStage.getStageParams(mEntry);
+ params.markContentViewsFreeable(inflationFlag);
+ mRowContentBindStage.requestRebind(mEntry, null /* callback */);
}
/**
@@ -1571,7 +1541,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
if (needsRedaction) {
params.requireContentViews(FLAG_CONTENT_VIEW_PUBLIC);
} else {
- params.freeContentViews(FLAG_CONTENT_VIEW_PUBLIC);
+ params.markContentViewsFreeable(FLAG_CONTENT_VIEW_PUBLIC);
}
mRowContentBindStage.requestRebind(mEntry, null /* callback */);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifBindPipeline.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifBindPipeline.java
index d744fc398d7a..893e8490eb90 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifBindPipeline.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifBindPipeline.java
@@ -16,6 +16,9 @@
package com.android.systemui.statusbar.notification.row;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.widget.FrameLayout;
@@ -25,12 +28,15 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.os.CancellationSignal;
+import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinder;
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag;
+import java.util.ArrayList;
+import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -75,14 +81,18 @@ import javax.inject.Singleton;
public final class NotifBindPipeline {
private final Map<NotificationEntry, BindEntry> mBindEntries = new ArrayMap<>();
private final NotifBindPipelineLogger mLogger;
+ private final List<BindCallback> mScratchCallbacksList = new ArrayList<>();
+ private final Handler mMainHandler;
private BindStage mStage;
@Inject
NotifBindPipeline(
CommonNotifCollection collection,
- NotifBindPipelineLogger logger) {
+ NotifBindPipelineLogger logger,
+ @Main Looper mainLooper) {
collection.addCollectionListener(mCollectionListener);
mLogger = logger;
+ mMainHandler = new NotifBindPipelineHandler(mainLooper);
}
/**
@@ -107,7 +117,7 @@ public final class NotifBindPipeline {
final BindEntry bindEntry = getBindEntry(entry);
bindEntry.row = row;
if (bindEntry.invalidated) {
- startPipeline(entry);
+ requestPipelineRun(entry);
}
}
@@ -130,7 +140,28 @@ public final class NotifBindPipeline {
signal.setOnCancelListener(() -> callbacks.remove(callback));
}
- startPipeline(entry);
+ requestPipelineRun(entry);
+ }
+
+ /**
+ * Request pipeline to start.
+ *
+ * We avoid starting the pipeline immediately as multiple clients may request rebinds
+ * back-to-back due to a single change (e.g. notification update), and it's better to start
+ * the real work once rather than repeatedly start and cancel it.
+ */
+ private void requestPipelineRun(NotificationEntry entry) {
+ mLogger.logRequestPipelineRun(entry.getKey());
+
+ final BindEntry bindEntry = getBindEntry(entry);
+
+ // Abort any existing pipeline run
+ mStage.abortStage(entry, bindEntry.row);
+
+ if (!mMainHandler.hasMessages(START_PIPELINE_MSG, entry)) {
+ Message msg = Message.obtain(mMainHandler, START_PIPELINE_MSG, entry);
+ mMainHandler.sendMessage(msg);
+ }
}
/**
@@ -151,7 +182,6 @@ public final class NotifBindPipeline {
return;
}
- mStage.abortStage(entry, row);
mStage.executeStage(entry, row, (en) -> onPipelineComplete(en));
}
@@ -162,10 +192,15 @@ public final class NotifBindPipeline {
mLogger.logFinishedPipeline(entry.getKey(), callbacks.size());
bindEntry.invalidated = false;
- for (BindCallback cb : callbacks) {
- cb.onBindFinished(entry);
- }
+ // Move all callbacks to separate list as callbacks may themselves add/remove callbacks.
+ // TODO: Throw an exception for this re-entrant behavior once we deprecate
+ // NotificationGroupAlertTransferHelper
+ mScratchCallbacksList.addAll(callbacks);
callbacks.clear();
+ for (int i = 0; i < mScratchCallbacksList.size(); i++) {
+ mScratchCallbacksList.get(i).onBindFinished(entry);
+ }
+ mScratchCallbacksList.clear();
}
private final NotifCollectionListener mCollectionListener = new NotifCollectionListener() {
@@ -183,6 +218,7 @@ public final class NotifBindPipeline {
mStage.abortStage(entry, row);
}
mStage.deleteStageParams(entry);
+ mMainHandler.removeMessages(START_PIPELINE_MSG, entry);
}
};
@@ -211,4 +247,25 @@ public final class NotifBindPipeline {
public final Set<BindCallback> callbacks = new ArraySet<>();
public boolean invalidated;
}
+
+ private static final int START_PIPELINE_MSG = 1;
+
+ private class NotifBindPipelineHandler extends Handler {
+
+ NotifBindPipelineHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case START_PIPELINE_MSG:
+ NotificationEntry entry = (NotificationEntry) msg.obj;
+ startPipeline(entry);
+ break;
+ default:
+ throw new IllegalArgumentException("Unknown message type: " + msg.what);
+ }
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifBindPipelineLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifBindPipelineLogger.kt
index 2717d7ad143b..199730427aec 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifBindPipelineLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifBindPipelineLogger.kt
@@ -40,6 +40,14 @@ class NotifBindPipelineLogger @Inject constructor(
})
}
+ fun logRequestPipelineRun(notifKey: String) {
+ buffer.log(TAG, INFO, {
+ str1 = notifKey
+ }, {
+ "Request pipeline run for notif: $str1"
+ })
+ }
+
fun logStartPipeline(notifKey: String) {
buffer.log(TAG, INFO, {
str1 = notifKey
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
index 719f74fdcde2..9d5443729d45 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
@@ -118,6 +118,9 @@ public class NotificationContentInflater implements NotificationRowContentBinder
mRemoteViewCache.clearCache(entry);
}
+ // Cancel any pending frees on any view we're trying to bind since we should be bound after.
+ cancelContentViewFrees(row, contentToBind);
+
AsyncInflationTask task = new AsyncInflationTask(
mBgExecutor,
mInflateSynchronously,
@@ -198,44 +201,69 @@ public class NotificationContentInflater implements NotificationRowContentBinder
}
/**
- * Frees the content view associated with the inflation flag. Will only succeed if the
- * view is safe to remove.
+ * Frees the content view associated with the inflation flag as soon as the view is not showing.
*
* @param inflateFlag the flag corresponding to the content view which should be freed
*/
- private void freeNotificationView(NotificationEntry entry, ExpandableNotificationRow row,
+ private void freeNotificationView(
+ NotificationEntry entry,
+ ExpandableNotificationRow row,
@InflationFlag int inflateFlag) {
switch (inflateFlag) {
case FLAG_CONTENT_VIEW_CONTRACTED:
- if (row.getPrivateLayout().isContentViewInactive(VISIBLE_TYPE_CONTRACTED)) {
+ row.getPrivateLayout().performWhenContentInactive(VISIBLE_TYPE_CONTRACTED, () -> {
row.getPrivateLayout().setContractedChild(null);
mRemoteViewCache.removeCachedView(entry, FLAG_CONTENT_VIEW_CONTRACTED);
- }
+ });
break;
case FLAG_CONTENT_VIEW_EXPANDED:
- if (row.getPrivateLayout().isContentViewInactive(VISIBLE_TYPE_EXPANDED)) {
+ row.getPrivateLayout().performWhenContentInactive(VISIBLE_TYPE_EXPANDED, () -> {
row.getPrivateLayout().setExpandedChild(null);
mRemoteViewCache.removeCachedView(entry, FLAG_CONTENT_VIEW_EXPANDED);
- }
+ });
break;
case FLAG_CONTENT_VIEW_HEADS_UP:
- if (row.getPrivateLayout().isContentViewInactive(VISIBLE_TYPE_HEADSUP)) {
+ row.getPrivateLayout().performWhenContentInactive(VISIBLE_TYPE_HEADSUP, () -> {
row.getPrivateLayout().setHeadsUpChild(null);
mRemoteViewCache.removeCachedView(entry, FLAG_CONTENT_VIEW_HEADS_UP);
row.getPrivateLayout().setHeadsUpInflatedSmartReplies(null);
- }
+ });
break;
case FLAG_CONTENT_VIEW_PUBLIC:
- if (row.getPublicLayout().isContentViewInactive(VISIBLE_TYPE_CONTRACTED)) {
+ row.getPublicLayout().performWhenContentInactive(VISIBLE_TYPE_CONTRACTED, () -> {
row.getPublicLayout().setContractedChild(null);
mRemoteViewCache.removeCachedView(entry, FLAG_CONTENT_VIEW_PUBLIC);
- }
+ });
break;
default:
break;
}
}
+ /**
+ * Cancel any pending content view frees from {@link #freeNotificationView} for the provided
+ * content views.
+ *
+ * @param row top level notification row containing the content views
+ * @param contentViews content views to cancel pending frees on
+ */
+ private void cancelContentViewFrees(
+ ExpandableNotificationRow row,
+ @InflationFlag int contentViews) {
+ if ((contentViews & FLAG_CONTENT_VIEW_CONTRACTED) != 0) {
+ row.getPrivateLayout().removeContentInactiveRunnable(VISIBLE_TYPE_CONTRACTED);
+ }
+ if ((contentViews & FLAG_CONTENT_VIEW_EXPANDED) != 0) {
+ row.getPrivateLayout().removeContentInactiveRunnable(VISIBLE_TYPE_EXPANDED);
+ }
+ if ((contentViews & FLAG_CONTENT_VIEW_HEADS_UP) != 0) {
+ row.getPrivateLayout().removeContentInactiveRunnable(VISIBLE_TYPE_HEADSUP);
+ }
+ if ((contentViews & FLAG_CONTENT_VIEW_PUBLIC) != 0) {
+ row.getPublicLayout().removeContentInactiveRunnable(VISIBLE_TYPE_CONTRACTED);
+ }
+ }
+
private static InflationProgress inflateSmartReplyViews(InflationProgress result,
@InflationFlag int reInflateFlags, NotificationEntry entry, Context context,
Context packageContext, HeadsUpManager headsUpManager,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
index 8efdc1b56e8e..b18bf01ea91f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
@@ -385,6 +385,7 @@ public class NotificationContentView extends FrameLayout {
*/
public void setContractedChild(@Nullable View child) {
if (mContractedChild != null) {
+ mOnContentViewInactiveListeners.remove(mContractedChild);
mContractedChild.animate().cancel();
removeView(mContractedChild);
}
@@ -432,6 +433,7 @@ public class NotificationContentView extends FrameLayout {
((ViewGroup)mExpandedRemoteInput.getParent()).removeView(mExpandedRemoteInput);
}
}
+ mOnContentViewInactiveListeners.remove(mExpandedChild);
mExpandedChild.animate().cancel();
removeView(mExpandedChild);
mExpandedRemoteInput = null;
@@ -470,6 +472,7 @@ public class NotificationContentView extends FrameLayout {
((ViewGroup)mHeadsUpRemoteInput.getParent()).removeView(mHeadsUpRemoteInput);
}
}
+ mOnContentViewInactiveListeners.remove(mHeadsUpChild);
mHeadsUpChild.animate().cancel();
removeView(mHeadsUpChild);
mHeadsUpRemoteInput = null;
@@ -1108,7 +1111,6 @@ public class NotificationContentView extends FrameLayout {
public void onNotificationUpdated(NotificationEntry entry) {
mStatusBarNotification = entry.getSbn();
- mOnContentViewInactiveListeners.clear();
mBeforeN = entry.targetSdk < Build.VERSION_CODES.N;
updateAllSingleLineViews();
ExpandableNotificationRow row = entry.getRow();
@@ -1623,7 +1625,7 @@ public class NotificationContentView extends FrameLayout {
* @param visibleType visible type corresponding to the content view to listen
* @param listener runnable to run once when the content view becomes inactive
*/
- public void performWhenContentInactive(int visibleType, Runnable listener) {
+ void performWhenContentInactive(int visibleType, Runnable listener) {
View view = getViewForVisibleType(visibleType);
// View is already inactive
if (view == null || isContentViewInactive(visibleType)) {
@@ -1634,6 +1636,22 @@ public class NotificationContentView extends FrameLayout {
}
/**
+ * Remove content inactive listeners for a given content view . See
+ * {@link #performWhenContentInactive}.
+ *
+ * @param visibleType visible type corresponding to the content type
+ */
+ void removeContentInactiveRunnable(int visibleType) {
+ View view = getViewForVisibleType(visibleType);
+ // View is already inactive
+ if (view == null) {
+ return;
+ }
+
+ mOnContentViewInactiveListeners.remove(view);
+ }
+
+ /**
* Whether or not the content view is inactive. This means it should not be visible
* or the showing content as removing it would cause visual jank.
*
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindParams.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindParams.java
index 88ed0bb37d0d..d3fec695f012 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindParams.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindParams.java
@@ -109,11 +109,14 @@ public final class RowContentBindParams {
}
/**
- * Free the content view so that it will no longer be bound after the rebind request.
+ * Mark the content view to be freed. The view may not be immediately freeable since it may
+ * be visible and animating out but this lets the binder know to free the view when safe.
+ * Note that the callback passed into {@link RowContentBindStage#requestRebind}
+ * may return before the view is actually freed since the view is considered up-to-date.
*
* @see InflationFlag
*/
- public void freeContentViews(@InflationFlag int contentViews) {
+ public void markContentViewsFreeable(@InflationFlag int contentViews) {
mContentViews &= ~contentViews;
mDirtyContentViews &= ~contentViews;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
index 0c311b403c48..5205bab8fea3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
@@ -130,10 +130,18 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper {
if (mNotificationHeader != null) {
mNotificationHeader.setAppOpsOnClickListener(listener);
}
- mAppOps.setOnClickListener(listener);
- mCameraIcon.setOnClickListener(listener);
- mMicIcon.setOnClickListener(listener);
- mOverlayIcon.setOnClickListener(listener);
+ if (mAppOps != null) {
+ mAppOps.setOnClickListener(listener);
+ }
+ if (mCameraIcon != null) {
+ mCameraIcon.setOnClickListener(listener);
+ }
+ if (mMicIcon != null) {
+ mMicIcon.setOnClickListener(listener);
+ }
+ if (mOverlayIcon != null) {
+ mOverlayIcon.setOnClickListener(listener);
+ }
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
index 8c31a372a756..dd9c8207af06 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
@@ -391,7 +391,9 @@ public class NotificationGroupAlertTransferHelper implements OnHeadsUpChangedLis
alertNotificationWhenPossible(entry, mHeadsUpManager);
} else {
// The transfer is no longer valid. Free the content.
- entry.getRow().freeContentViewWhenSafe(mHeadsUpManager.getContentFlag());
+ mRowContentBindStage.getStageParams(entry).markContentViewsFreeable(
+ contentFlag);
+ mRowContentBindStage.requestRebind(entry, null);
}
}
});
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
index 66a1d3ff756a..0d5a14960850 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
@@ -161,7 +161,6 @@ public abstract class HeadsUpManager extends AlertingNotificationManager {
for (OnHeadsUpChangedListener listener : mListeners) {
listener.onHeadsUpStateChanged(entry, false);
}
- entry.freeContentViewWhenSafe(FLAG_CONTENT_VIEW_HEADS_UP);
}
protected void updatePinnedMode() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/OnHeadsUpChangedListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/OnHeadsUpChangedListener.java
index 94aa39142926..86998ab2fdd9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/OnHeadsUpChangedListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/OnHeadsUpChangedListener.java
@@ -11,7 +11,7 @@
* 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
+ * limitations under the License.
*/
package com.android.systemui.statusbar.policy;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ExpandHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/ExpandHelperTest.java
index 471149c936e4..6decb88ee148 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ExpandHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ExpandHelperTest.java
@@ -23,6 +23,7 @@ import static org.mockito.Mockito.when;
import android.animation.ObjectAnimator;
import android.content.Context;
import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
import androidx.test.annotation.UiThreadTest;
@@ -52,7 +53,11 @@ public class ExpandHelperTest extends SysuiTestCase {
mDependency.injectMockDependency(NotificationMediaManager.class);
allowTestableLooperAsMainThread();
Context context = getContext();
- mRow = new NotificationTestHelper(context, mDependency).createRow();
+ NotificationTestHelper helper = new NotificationTestHelper(
+ mContext,
+ mDependency,
+ TestableLooper.get(this));
+ mRow = helper.createRow();
mCallback = mock(ExpandHelper.Callback.class);
mExpandHelper = new ExpandHelper(context, mCallback, 10, 100);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
index 6f3fbb9cbd2c..037f04ec1d7c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
@@ -223,7 +223,10 @@ public class BubbleControllerTest extends SysuiTestCase {
mNotificationShadeWindowController.attach();
// Need notifications for bubbles
- mNotificationTestHelper = new NotificationTestHelper(mContext, mDependency);
+ mNotificationTestHelper = new NotificationTestHelper(
+ mContext,
+ mDependency,
+ TestableLooper.get(this));
mRow = mNotificationTestHelper.createBubble(mDeleteIntent);
mRow2 = mNotificationTestHelper.createBubble(mDeleteIntent);
mNonBubbleNotifRow = mNotificationTestHelper.createRow();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java
index 7df39838b167..d2f912770577 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java
@@ -109,7 +109,10 @@ public class BubbleDataTest extends SysuiTestCase {
@Before
public void setUp() throws Exception {
- mNotificationTestHelper = new NotificationTestHelper(mContext, mDependency);
+ mNotificationTestHelper = new NotificationTestHelper(
+ mContext,
+ mDependency,
+ TestableLooper.get(this));
MockitoAnnotations.initMocks(this);
mEntryA1 = createBubbleEntry(1, "a1", "package.a");
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java
index a31e3f8d7cc9..545de210d5b5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java
@@ -210,7 +210,10 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase {
mNotificationShadeWindowController.attach();
// Need notifications for bubbles
- mNotificationTestHelper = new NotificationTestHelper(mContext, mDependency);
+ mNotificationTestHelper = new NotificationTestHelper(
+ mContext,
+ mDependency,
+ TestableLooper.get(this));
mRow = mNotificationTestHelper.createBubble(mDeleteIntent);
mRow2 = mNotificationTestHelper.createBubble(mDeleteIntent);
mNonBubbleNotifRow = mNotificationTestHelper.createRow();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
index 83877f2e72f1..ea68516e639c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
@@ -99,7 +99,7 @@ public class NotificationViewHierarchyManagerTest extends SysuiTestCase {
mDependency.injectTestDependency(NotificationGroupManager.class, mGroupManager);
mDependency.injectTestDependency(VisualStabilityManager.class, mVisualStabilityManager);
- mHelper = new NotificationTestHelper(mContext, mDependency);
+ mHelper = new NotificationTestHelper(mContext, mDependency, TestableLooper.get(this));
mViewHierarchyManager = new NotificationViewHierarchyManager(mContext,
mHandler, mLockscreenUserManager, mGroupManager, mVisualStabilityManager,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/AboveShelfObserverTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/AboveShelfObserverTest.java
index 0a38f163cfba..2b9456160122 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/AboveShelfObserverTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/AboveShelfObserverTest.java
@@ -21,6 +21,7 @@ import static org.mockito.Mockito.verify;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
import android.widget.FrameLayout;
@@ -46,7 +47,10 @@ public class AboveShelfObserverTest extends SysuiTestCase {
@Before
public void setUp() throws Exception {
allowTestableLooperAsMainThread();
- mNotificationTestHelper = new NotificationTestHelper(getContext(), mDependency);
+ mNotificationTestHelper = new NotificationTestHelper(
+ mContext,
+ mDependency,
+ TestableLooper.get(this));
mHostLayout = new FrameLayout(getContext());
mObserver = new AboveShelfObserver(mHostLayout);
ExpandableNotificationRow row = mNotificationTestHelper.createRow();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/DynamicChildBindControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/DynamicChildBindControllerTest.java
index bf2d59880ffb..29040147d1ca 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/DynamicChildBindControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/DynamicChildBindControllerTest.java
@@ -83,8 +83,8 @@ public class DynamicChildBindControllerTest extends SysuiTestCase {
mDynamicChildBindController.updateChildContentViews(mGroupNotifs);
// THEN we free content views
- verify(bindParams).freeContentViews(FLAG_CONTENT_VIEW_CONTRACTED);
- verify(bindParams).freeContentViews(FLAG_CONTENT_VIEW_EXPANDED);
+ verify(bindParams).markContentViewsFreeable(FLAG_CONTENT_VIEW_CONTRACTED);
+ verify(bindParams).markContentViewsFreeable(FLAG_CONTENT_VIEW_EXPANDED);
verify(mBindStage).requestRebind(eq(lastChild), any());
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java
index 97e0a3199e17..277ac244cec5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java
@@ -32,6 +32,7 @@ import android.content.pm.PackageManager;
import android.os.Bundle;
import android.service.notification.StatusBarNotification;
import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
import androidx.test.annotation.UiThreadTest;
@@ -98,7 +99,11 @@ public class NotificationFilterTest extends SysuiTestCase {
mDependency.injectTestDependency(KeyguardEnvironment.class, mEnvironment);
when(mEnvironment.isDeviceProvisioned()).thenReturn(true);
when(mEnvironment.isNotificationForCurrentProfiles(any())).thenReturn(true);
- mRow = new NotificationTestHelper(getContext(), mDependency).createRow();
+ NotificationTestHelper testHelper = new NotificationTestHelper(
+ mContext,
+ mDependency,
+ TestableLooper.get(this));
+ mRow = testHelper.createRow();
mNotificationFilter = new NotificationFilter(mock(StatusBarStateController.class));
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.java
index 0c109c498dd7..f3038ce051cd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.java
@@ -19,6 +19,8 @@ package com.android.systemui.statusbar.notification.collection.coordinator;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -37,6 +39,9 @@ import com.android.systemui.statusbar.notification.collection.listbuilder.plugga
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSection;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender;
+import com.android.systemui.statusbar.notification.headsup.HeadsUpViewBinder;
+import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
+import com.android.systemui.statusbar.notification.row.NotifBindPipeline.BindCallback;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
@@ -63,6 +68,8 @@ public class HeadsUpCoordinatorTest extends SysuiTestCase {
@Mock private NotifPipeline mNotifPipeline;
@Mock private HeadsUpManager mHeadsUpManager;
+ @Mock private HeadsUpViewBinder mHeadsUpViewBinder;
+ @Mock private NotificationInterruptStateProvider mNotificationInterruptStateProvider;
@Mock private NotificationRemoteInputManager mRemoteInputManager;
@Mock private RemoteInputController mRemoteInputController;
@Mock private NotifLifetimeExtender.OnEndLifetimeExtensionCallback mEndLifetimeExtension;
@@ -76,6 +83,8 @@ public class HeadsUpCoordinatorTest extends SysuiTestCase {
mCoordinator = new HeadsUpCoordinator(
mHeadsUpManager,
+ mHeadsUpViewBinder,
+ mNotificationInterruptStateProvider,
mRemoteInputManager
);
@@ -168,6 +177,36 @@ public class HeadsUpCoordinatorTest extends SysuiTestCase {
}
@Test
+ public void testShowHUNOnInflationFinished() {
+ // WHEN a notification should HUN and its inflation is finished
+ when(mNotificationInterruptStateProvider.shouldHeadsUp(mEntry)).thenReturn(true);
+
+ ArgumentCaptor<BindCallback> bindCallbackCaptor =
+ ArgumentCaptor.forClass(BindCallback.class);
+ mCollectionListener.onEntryAdded(mEntry);
+ verify(mHeadsUpViewBinder).bindHeadsUpView(eq(mEntry), bindCallbackCaptor.capture());
+
+ bindCallbackCaptor.getValue().onBindFinished(mEntry);
+
+ // THEN we tell the HeadsUpManager to show the notification
+ verify(mHeadsUpManager).showNotification(mEntry);
+ }
+
+ @Test
+ public void testNoHUNOnInflationFinished() {
+ // WHEN a notification shouldn't HUN and its inflation is finished
+ when(mNotificationInterruptStateProvider.shouldHeadsUp(mEntry)).thenReturn(false);
+ ArgumentCaptor<BindCallback> bindCallbackCaptor =
+ ArgumentCaptor.forClass(BindCallback.class);
+ mCollectionListener.onEntryAdded(mEntry);
+
+ // THEN we never bind the heads up view or tell HeadsUpManager to show the notification
+ verify(mHeadsUpViewBinder, never()).bindHeadsUpView(
+ eq(mEntry), bindCallbackCaptor.capture());
+ verify(mHeadsUpManager, never()).showNotification(mEntry);
+ }
+
+ @Test
public void testOnEntryRemovedRemovesHeadsUpNotification() {
// GIVEN the current HUN is mEntry
setCurrentHUN(mEntry);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java
index 8143cf5a4673..6b9e43bcb290 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java
@@ -20,10 +20,8 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
import android.os.RemoteException;
import android.testing.AndroidTestingRunner;
@@ -41,9 +39,7 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntryB
import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeFinalizeFilterListener;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
-import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
import com.android.systemui.statusbar.notification.row.NotifInflationErrorManager;
-import com.android.systemui.statusbar.policy.HeadsUpManager;
import org.junit.Before;
import org.junit.Test;
@@ -78,8 +74,6 @@ public class PreparationCoordinatorTest extends SysuiTestCase {
@Mock private NotifPipeline mNotifPipeline;
@Mock private IStatusBarService mService;
@Mock private NotifInflaterImpl mNotifInflater;
- @Mock private NotificationInterruptStateProvider mNotificationInterruptStateProvider;
- @Mock private HeadsUpManager mHeadsUpManager;
@Before
public void setUp() {
@@ -94,9 +88,7 @@ public class PreparationCoordinatorTest extends SysuiTestCase {
mNotifInflater,
mErrorManager,
mock(NotifViewBarn.class),
- mService,
- mNotificationInterruptStateProvider,
- mHeadsUpManager);
+ mService);
ArgumentCaptor<NotifFilter> filterCaptor = ArgumentCaptor.forClass(NotifFilter.class);
mCoordinator.attach(mNotifPipeline);
@@ -180,24 +172,4 @@ public class PreparationCoordinatorTest extends SysuiTestCase {
// THEN it isn't filtered from shade list
assertFalse(mUninflatedFilter.shouldFilterOut(mEntry, 0));
}
-
- @Test
- public void testShowHUNOnInflationFinished() {
- // WHEN a notification should HUN and its inflation is finished
- when(mNotificationInterruptStateProvider.shouldHeadsUp(mEntry)).thenReturn(true);
- mCallback.onInflationFinished(mEntry);
-
- // THEN we tell the HeadsUpManager to show the notification
- verify(mHeadsUpManager).showNotification(mEntry);
- }
-
- @Test
- public void testNoHUNOnInflationFinished() {
- // WHEN a notification shouldn't HUN and its inflation is finished
- when(mNotificationInterruptStateProvider.shouldHeadsUp(mEntry)).thenReturn(false);
- mCallback.onInflationFinished(mEntry);
-
- // THEN we never tell the HeadsUpManager to show the notification
- verify(mHeadsUpManager, never()).showNotification(mEntry);
- }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
index cb379208eb94..43dcbe30f3c3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
@@ -20,7 +20,6 @@ import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
import static com.android.systemui.statusbar.NotificationEntryHelper.modifyRanking;
import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_ALL;
-import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_HEADS_UP;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -39,6 +38,7 @@ import static org.mockito.Mockito.when;
import android.app.AppOpsManager;
import android.app.NotificationChannel;
import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
import android.util.ArraySet;
import android.view.NotificationHeaderView;
@@ -79,7 +79,10 @@ public class ExpandableNotificationRowTest extends SysuiTestCase {
@Before
public void setUp() throws Exception {
allowTestableLooperAsMainThread();
- mNotificationTestHelper = new NotificationTestHelper(mContext, mDependency);
+ mNotificationTestHelper = new NotificationTestHelper(
+ mContext,
+ mDependency,
+ TestableLooper.get(this));
mGroupRow = mNotificationTestHelper.createGroup();
mGroupRow.setHeadsUpAnimatingAwayListener(
animatingAway -> mHeadsUpAnimatingAway = animatingAway);
@@ -135,22 +138,13 @@ public class ExpandableNotificationRowTest extends SysuiTestCase {
}
@Test
- public void testFreeContentViewWhenSafe() throws Exception {
- ExpandableNotificationRow row = mNotificationTestHelper.createRow(FLAG_CONTENT_VIEW_ALL);
-
- row.freeContentViewWhenSafe(FLAG_CONTENT_VIEW_HEADS_UP);
-
- assertNull(row.getPrivateLayout().getHeadsUpChild());
- }
-
- @Test
public void setNeedsRedactionFreesViewWhenFalse() throws Exception {
ExpandableNotificationRow row = mNotificationTestHelper.createRow(FLAG_CONTENT_VIEW_ALL);
row.setNeedsRedaction(true);
row.getPublicLayout().setVisibility(View.GONE);
row.setNeedsRedaction(false);
-
+ TestableLooper.get(this).processAllMessages();
assertNull(row.getPublicLayout().getContractedChild());
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotifBindPipelineTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotifBindPipelineTest.java
index 6408f7a38133..bdd82fd98e0a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotifBindPipelineTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotifBindPipelineTest.java
@@ -59,7 +59,10 @@ public class NotifBindPipelineTest extends SysuiTestCase {
MockitoAnnotations.initMocks(this);
CommonNotifCollection collection = mock(CommonNotifCollection.class);
- mBindPipeline = new NotifBindPipeline(collection, mock(NotifBindPipelineLogger.class));
+ mBindPipeline = new NotifBindPipeline(
+ collection,
+ mock(NotifBindPipelineLogger.class),
+ TestableLooper.get(this).getLooper());
mBindPipeline.setStage(mStage);
ArgumentCaptor<NotifCollectionListener> collectionListenerCaptor =
@@ -78,6 +81,7 @@ public class NotifBindPipelineTest extends SysuiTestCase {
// WHEN content is invalidated
BindCallback callback = mock(BindCallback.class);
mStage.requestRebind(mEntry, callback);
+ TestableLooper.get(this).processAllMessages();
// WHEN stage finishes its work
mStage.doWorkSynchronously();
@@ -94,6 +98,7 @@ public class NotifBindPipelineTest extends SysuiTestCase {
// GIVEN an in-progress pipeline run
BindCallback callback = mock(BindCallback.class);
CancellationSignal signal = mStage.requestRebind(mEntry, callback);
+ TestableLooper.get(this).processAllMessages();
// WHEN the callback is cancelled.
signal.cancel();
@@ -113,10 +118,12 @@ public class NotifBindPipelineTest extends SysuiTestCase {
// WHEN the pipeline is invalidated.
BindCallback callback = mock(BindCallback.class);
mStage.requestRebind(mEntry, callback);
+ TestableLooper.get(this).processAllMessages();
// WHEN the pipeline is invalidated again before the work completes.
BindCallback callback2 = mock(BindCallback.class);
mStage.requestRebind(mEntry, callback2);
+ TestableLooper.get(this).processAllMessages();
// WHEN the stage finishes all work.
mStage.doWorkSynchronously();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManagerTest.java
index 481bac2c19c6..7c8328d59a51 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManagerTest.java
@@ -86,7 +86,7 @@ public class NotificationBlockingHelperManagerTest extends SysuiTestCase {
when(mMenuRow.getLongpressMenuItem(any(Context.class))).thenReturn(mMenuItem);
mDependency.injectMockDependency(BubbleController.class);
- mHelper = new NotificationTestHelper(mContext, mDependency);
+ mHelper = new NotificationTestHelper(mContext, mDependency, TestableLooper.get(this));
mBlockingHelperManager = new NotificationBlockingHelperManager(
mContext, mGutsManager, mEntryManager, mock(MetricsLogger.class));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
index 6a65269c34b0..25da74137a90 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
@@ -40,6 +40,7 @@ import android.os.CancellationSignal;
import android.os.Handler;
import android.os.Looper;
import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
import android.view.View;
import android.view.ViewGroup;
@@ -94,8 +95,11 @@ public class NotificationContentInflaterTest extends SysuiTestCase {
.setContentTitle("Title")
.setContentText("Text")
.setStyle(new Notification.BigTextStyle().bigText("big text"));
- ExpandableNotificationRow row = new NotificationTestHelper(mContext, mDependency).createRow(
- mBuilder.build());
+ NotificationTestHelper helper = new NotificationTestHelper(
+ mContext,
+ mDependency,
+ TestableLooper.get(this));
+ ExpandableNotificationRow row = helper.createRow(mBuilder.build());
mRow = spy(row);
final SmartReplyConstants smartReplyConstants = mock(SmartReplyConstants.class);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java
index 0f268984a996..b018b59e4389 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java
@@ -76,13 +76,13 @@ public class NotificationContentViewTest extends SysuiTestCase {
@Test
@UiThreadTest
public void testShowAppOpsIcons() {
- View mockContracted = mock(View.class);
+ View mockContracted = mock(NotificationHeaderView.class);
when(mockContracted.findViewById(com.android.internal.R.id.mic))
.thenReturn(mockContracted);
- View mockExpanded = mock(View.class);
+ View mockExpanded = mock(NotificationHeaderView.class);
when(mockExpanded.findViewById(com.android.internal.R.id.mic))
.thenReturn(mockExpanded);
- View mockHeadsUp = mock(View.class);
+ View mockHeadsUp = mock(NotificationHeaderView.class);
when(mockHeadsUp.findViewById(com.android.internal.R.id.mic))
.thenReturn(mockHeadsUp);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java
index a5d8a84e3e08..be026f7884c3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java
@@ -182,7 +182,8 @@ public class NotificationEntryManagerInflationTest extends SysuiTestCase {
NotifRemoteViewCache cache = new NotifRemoteViewCacheImpl(mEntryManager);
NotifBindPipeline pipeline = new NotifBindPipeline(
mEntryManager,
- mock(NotifBindPipelineLogger.class));
+ mock(NotifBindPipelineLogger.class),
+ TestableLooper.get(this).getLooper());
mBgExecutor = new FakeExecutor(new FakeSystemClock());
NotificationContentInflater binder = new NotificationContentInflater(
cache,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
index 462da935e0c3..ed4642344dba 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
@@ -131,7 +131,7 @@ public class NotificationGutsManagerTest extends SysuiTestCase {
mDependency.injectTestDependency(VisualStabilityManager.class, mVisualStabilityManager);
mDependency.injectMockDependency(NotificationLockscreenUserManager.class);
mHandler = Handler.createAsync(mTestableLooper.getLooper());
- mHelper = new NotificationTestHelper(mContext, mDependency);
+ mHelper = new NotificationTestHelper(mContext, mDependency, TestableLooper.get(this));
when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(false);
mGutsManager = new NotificationGutsManager(mContext, mVisualStabilityManager,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
index 0e67feb1d4ee..07f2085a1b76 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
@@ -38,6 +38,7 @@ import android.content.pm.LauncherApps;
import android.graphics.drawable.Icon;
import android.os.UserHandle;
import android.service.notification.StatusBarNotification;
+import android.testing.TestableLooper;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.widget.RemoteViews;
@@ -92,6 +93,7 @@ public class NotificationTestHelper {
private static final String APP_NAME = "appName";
private final Context mContext;
+ private final TestableLooper mTestLooper;
private int mId;
private final NotificationGroupManager mGroupManager;
private ExpandableNotificationRow mRow;
@@ -103,8 +105,12 @@ public class NotificationTestHelper {
private StatusBarStateController mStatusBarStateController;
private final PeopleNotificationIdentifier mPeopleNotificationIdentifier;
- public NotificationTestHelper(Context context, TestableDependency dependency) {
+ public NotificationTestHelper(
+ Context context,
+ TestableDependency dependency,
+ TestableLooper testLooper) {
mContext = context;
+ mTestLooper = testLooper;
dependency.injectMockDependency(NotificationMediaManager.class);
dependency.injectMockDependency(BubbleController.class);
dependency.injectMockDependency(NotificationShadeWindowController.class);
@@ -133,7 +139,10 @@ public class NotificationTestHelper {
CommonNotifCollection collection = mock(CommonNotifCollection.class);
- mBindPipeline = new NotifBindPipeline(collection, mock(NotifBindPipelineLogger.class));
+ mBindPipeline = new NotifBindPipeline(
+ collection,
+ mock(NotifBindPipelineLogger.class),
+ mTestLooper.getLooper());
mBindPipeline.setStage(mBindStage);
ArgumentCaptor<NotifCollectionListener> collectionListenerCaptor =
@@ -414,7 +423,7 @@ public class NotificationTestHelper {
mPeopleNotificationIdentifier);
row.setAboveShelfChangedListener(aboveShelf -> { });
mBindStage.getStageParams(entry).requireContentViews(extraInflationFlags);
- inflateAndWait(entry, mBindStage);
+ inflateAndWait(entry);
// This would be done as part of onAsyncInflationFinished, but we skip large amounts of
// the callback chain, so we need to make up for not adding it to the group manager
@@ -423,10 +432,10 @@ public class NotificationTestHelper {
return row;
}
- private static void inflateAndWait(NotificationEntry entry, RowContentBindStage stage)
- throws Exception {
+ private void inflateAndWait(NotificationEntry entry) throws Exception {
CountDownLatch countDownLatch = new CountDownLatch(1);
- stage.requestRebind(entry, en -> countDownLatch.countDown());
+ mBindStage.requestRebind(entry, en -> countDownLatch.countDown());
+ mTestLooper.processAllMessages();
assertTrue(countDownLatch.await(500, TimeUnit.MILLISECONDS));
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/RowContentBindStageTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/RowContentBindStageTest.java
index 0f2482ce9c4e..96a58e27ed0d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/RowContentBindStageTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/RowContentBindStageTest.java
@@ -93,7 +93,7 @@ public class RowContentBindStageTest extends SysuiTestCase {
// WHEN inflation flags are cleared and stage executed.
final int flags = FLAG_CONTENT_VIEW_CONTRACTED | FLAG_CONTENT_VIEW_EXPANDED;
- params.freeContentViews(flags);
+ params.markContentViewsFreeable(flags);
mRowContentBindStage.executeStage(mEntry, mRow, (en) -> { });
// THEN binder unbinds flags.
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapperTest.java
index b661b28c42fc..45f7c5a6fdc0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapperTest.java
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.notification.row.wrapper;
import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
import android.view.View;
import android.widget.RemoteViews;
@@ -43,7 +44,11 @@ public class NotificationCustomViewWrapperTest extends SysuiTestCase {
@Before
public void setUp() throws Exception {
allowTestableLooperAsMainThread();
- mRow = new NotificationTestHelper(mContext, mDependency).createRow();
+ NotificationTestHelper helper = new NotificationTestHelper(
+ mContext,
+ mDependency,
+ TestableLooper.get(this));
+ mRow = helper.createRow();
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapperTest.java
index 18ea774e56f0..fbe4d7315baa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapperTest.java
@@ -27,6 +27,7 @@ import android.media.session.MediaSession;
import android.media.session.PlaybackState;
import android.provider.Settings;
import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
import android.view.View;
import android.widget.RemoteViews;
@@ -97,7 +98,11 @@ public class NotificationMediaTemplateViewWrapperTest extends SysuiTestCase {
mNotif = builder.build();
assertTrue(mNotif.hasMediaSession());
- mRow = new NotificationTestHelper(mContext, mDependency).createRow(mNotif);
+ NotificationTestHelper helper = new NotificationTestHelper(
+ mContext,
+ mDependency,
+ TestableLooper.get(this));
+ mRow = helper.createRow(mNotif);
RemoteViews views = new RemoteViews(mContext.getPackageName(),
com.android.internal.R.layout.notification_template_material_big_media);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapperTest.java
index 830e8d93196c..085bd900debc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapperTest.java
@@ -20,6 +20,7 @@ import static org.mockito.Mockito.mock;
import android.content.Context;
import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
import android.view.View;
import android.widget.LinearLayout;
@@ -48,7 +49,11 @@ public class NotificationViewWrapperTest extends SysuiTestCase {
public void setup() throws Exception {
allowTestableLooperAsMainThread();
mView = mock(View.class);
- mRow = new NotificationTestHelper(getContext(), mDependency).createRow();
+ NotificationTestHelper helper = new NotificationTestHelper(
+ mContext,
+ mDependency,
+ TestableLooper.get(this));
+ mRow = helper.createRow();
mNotificationViewWrapper = new TestableNotificationViewWrapper(mContext, mView, mRow);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java
index a2029c76bb55..703789151895 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.notification.stack;
import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
import android.view.NotificationHeaderView;
import android.view.View;
@@ -44,7 +45,10 @@ public class NotificationChildrenContainerTest extends SysuiTestCase {
@Before
public void setUp() throws Exception {
allowTestableLooperAsMainThread();
- mNotificationTestHelper = new NotificationTestHelper(mContext, mDependency);
+ mNotificationTestHelper = new NotificationTestHelper(
+ mContext,
+ mDependency,
+ TestableLooper.get(this));
mGroup = mNotificationTestHelper.createGroup();
mChildrenContainer = mGroup.getChildrenContainer();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java
index ba2b94677814..d795cbac700f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java
@@ -24,6 +24,7 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
import androidx.test.filters.SmallTest;
@@ -66,7 +67,10 @@ public class NotificationRoundnessManagerTest extends SysuiTestCase {
mBypassController,
new NotificationSectionsFeatureManager(new DeviceConfigProxy(), mContext));
allowTestableLooperAsMainThread();
- NotificationTestHelper testHelper = new NotificationTestHelper(getContext(), mDependency);
+ NotificationTestHelper testHelper = new NotificationTestHelper(
+ mContext,
+ mDependency,
+ TestableLooper.get(this));
mFirst = testHelper.createRow();
mFirst.setHeadsUpAnimatingAwayListener(animatingAway
-> mRoundnessManager.onHeadsupAnimatingAwayChanged(mFirst, animatingAway));
@@ -146,7 +150,10 @@ public class NotificationRoundnessManagerTest extends SysuiTestCase {
createSection(mFirst, mSecond),
createSection(null, null)
});
- NotificationTestHelper testHelper = new NotificationTestHelper(getContext(), mDependency);
+ NotificationTestHelper testHelper = new NotificationTestHelper(
+ mContext,
+ mDependency,
+ TestableLooper.get(this));
ExpandableNotificationRow row = testHelper.createRow();
NotificationEntry entry = mock(NotificationEntry.class);
when(entry.getRow()).thenReturn(row);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
index a74657e561aa..e546dff8abf6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
@@ -23,6 +23,7 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
import android.view.View;
import android.widget.TextView;
@@ -69,7 +70,10 @@ public class HeadsUpAppearanceControllerTest extends SysuiTestCase {
@Before
public void setUp() throws Exception {
allowTestableLooperAsMainThread();
- NotificationTestHelper testHelper = new NotificationTestHelper(getContext(), mDependency);
+ NotificationTestHelper testHelper = new NotificationTestHelper(
+ mContext,
+ mDependency,
+ TestableLooper.get(this));
mFirst = testHelper.createRow();
mDependency.injectTestDependency(DarkIconDispatcher.class, mDarkIconDispatcher);
mHeadsUpStatusBarView = new HeadsUpStatusBarView(mContext, mock(View.class),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java
index f6a099da3282..67f941301e5f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java
@@ -26,7 +26,6 @@ import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -236,8 +235,7 @@ public class NotificationGroupAlertTransferHelperTest extends SysuiTestCase {
verify(mBindStage).requestRebind(eq(childEntry), callbackCaptor.capture());
callbackCaptor.getValue().onBindFinished(childEntry);
- verify(childEntry.getRow(), times(1)).freeContentViewWhenSafe(mHeadsUpManager
- .getContentFlag());
+ assertTrue((params.getContentViews() & FLAG_CONTENT_VIEW_HEADS_UP) == 0);
assertFalse(mHeadsUpManager.isAlerting(childEntry.getKey()));
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
index b9c5b7c02b1c..dd28687e749c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
@@ -152,7 +152,10 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase {
when(mContentIntent.getCreatorUserHandle()).thenReturn(UserHandle.of(1));
when(mContentIntent.getIntent()).thenReturn(mContentIntentInner);
- mNotificationTestHelper = new NotificationTestHelper(mContext, mDependency);
+ mNotificationTestHelper = new NotificationTestHelper(
+ mContext,
+ mDependency,
+ TestableLooper.get(this));
// Create standard notification with contentIntent
mNotificationRow = mNotificationTestHelper.createRow();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java
index 86add98ab929..e88b514ef238 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java
@@ -105,8 +105,11 @@ public class RemoteInputViewTest extends SysuiTestCase {
@Test
public void testSendRemoteInput_intentContainsResultsAndSource() throws Exception {
- ExpandableNotificationRow row = new NotificationTestHelper(mContext, mDependency)
- .createRow();
+ NotificationTestHelper helper = new NotificationTestHelper(
+ mContext,
+ mDependency,
+ TestableLooper.get(this));
+ ExpandableNotificationRow row = helper.createRow();
RemoteInputView view = RemoteInputView.inflate(mContext, null, row.getEntry(), mController);
setTestPendingIntent(view);
@@ -127,7 +130,11 @@ public class RemoteInputViewTest extends SysuiTestCase {
private UserHandle getTargetInputMethodUser(UserHandle fromUser, UserHandle toUser)
throws Exception {
- ExpandableNotificationRow row = new NotificationTestHelper(mContext, mDependency).createRow(
+ NotificationTestHelper helper = new NotificationTestHelper(
+ mContext,
+ mDependency,
+ TestableLooper.get(this));
+ ExpandableNotificationRow row = helper.createRow(
DUMMY_MESSAGE_APP_PKG,
UserHandle.getUid(fromUser.getIdentifier(), DUMMY_MESSAGE_APP_ID),
toUser);
@@ -169,8 +176,11 @@ public class RemoteInputViewTest extends SysuiTestCase {
@Test
public void testNoCrashWithoutVisibilityListener() throws Exception {
- ExpandableNotificationRow row = new NotificationTestHelper(mContext, mDependency)
- .createRow();
+ NotificationTestHelper helper = new NotificationTestHelper(
+ mContext,
+ mDependency,
+ TestableLooper.get(this));
+ ExpandableNotificationRow row = helper.createRow();
RemoteInputView view = RemoteInputView.inflate(mContext, null, row.getEntry(), mController);
view.setOnVisibilityChangedListener(null);
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index e942b274dc53..55a92966eb29 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -645,33 +645,21 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
/**
- * Returns whether inline suggestions are enabled for Autofill.
+ * Returns whether inline suggestions are supported by Autofill provider (not augmented
+ * Autofill provider).
*/
- private boolean isInlineSuggestionsEnabledLocked() {
- return mService.isInlineSuggestionsEnabled()
- || mService.getRemoteInlineSuggestionRenderServiceLocked() != null;
+ private boolean isInlineSuggestionsEnabledByAutofillProviderLocked() {
+ return mService.isInlineSuggestionsEnabled();
}
- /**
- * Ask the IME to make an inline suggestions request if enabled.
- */
- private void maybeRequestInlineSuggestionsRequestThenFillLocked(@NonNull ViewState viewState,
- int newState, int flags) {
- if (isInlineSuggestionsEnabledLocked()) {
- Consumer<InlineSuggestionsRequest> inlineSuggestionsRequestConsumer =
- mAssistReceiver.newAutofillRequestLocked(/*isInlineRequest=*/ true);
- if (inlineSuggestionsRequestConsumer != null) {
- mInlineSuggestionSession.onCreateInlineSuggestionsRequest(mCurrentViewId,
- inlineSuggestionsRequestConsumer);
- }
- } else {
- mAssistReceiver.newAutofillRequestLocked(/*isInlineRequest=*/ false);
- }
- requestNewFillResponseLocked(viewState, newState, flags);
+ private boolean isInlineSuggestionRenderServiceAvailable() {
+ return mService.getRemoteInlineSuggestionRenderServiceLocked() != null;
}
/**
* Reads a new structure and then request a new fill response from the fill service.
+ *
+ * <p> Also asks the IME to make an inline suggestions request if it's enabled.
*/
@GuardedBy("mLock")
private void requestNewFillResponseLocked(@NonNull ViewState viewState, int newState,
@@ -717,6 +705,21 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
// structure is taken. This causes only one fill request per bust of focus changes.
cancelCurrentRequestLocked();
+ // Only ask IME to create inline suggestions request if Autofill provider supports it and
+ // the render service is available.
+ if (isInlineSuggestionsEnabledByAutofillProviderLocked()
+ && isInlineSuggestionRenderServiceAvailable()) {
+ Consumer<InlineSuggestionsRequest> inlineSuggestionsRequestConsumer =
+ mAssistReceiver.newAutofillRequestLocked(/*isInlineRequest=*/ true);
+ if (inlineSuggestionsRequestConsumer != null) {
+ mInlineSuggestionSession.onCreateInlineSuggestionsRequest(mCurrentViewId,
+ inlineSuggestionsRequestConsumer);
+ }
+ } else {
+ mAssistReceiver.newAutofillRequestLocked(/*isInlineRequest=*/ false);
+ }
+
+ // Now request the assist structure data.
try {
final Bundle receiverExtras = new Bundle();
receiverExtras.putInt(EXTRA_REQUEST_ID, requestId);
@@ -2322,8 +2325,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
if ((flags & FLAG_MANUAL_REQUEST) != 0) {
mForAugmentedAutofillOnly = false;
if (sDebug) Slog.d(TAG, "Re-starting session on view " + id + " and flags " + flags);
- maybeRequestInlineSuggestionsRequestThenFillLocked(viewState,
- ViewState.STATE_RESTARTED_SESSION, flags);
+ requestNewFillResponseLocked(viewState, ViewState.STATE_RESTARTED_SESSION, flags);
return true;
}
@@ -2333,8 +2335,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
Slog.d(TAG, "Starting partition or augmented request for view id " + id + ": "
+ viewState.getStateAsString());
}
- maybeRequestInlineSuggestionsRequestThenFillLocked(viewState,
- ViewState.STATE_STARTED_PARTITION, flags);
+ requestNewFillResponseLocked(viewState, ViewState.STATE_STARTED_PARTITION, flags);
return true;
} else {
if (sVerbose) {
@@ -2458,8 +2459,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
// View is triggering autofill.
mCurrentViewId = viewState.id;
viewState.update(value, virtualBounds, flags);
- maybeRequestInlineSuggestionsRequestThenFillLocked(viewState,
- ViewState.STATE_STARTED_SESSION, flags);
+ requestNewFillResponseLocked(viewState, ViewState.STATE_STARTED_SESSION, flags);
break;
case ACTION_VALUE_CHANGED:
if (mCompatMode && (viewState.getState() & ViewState.STATE_URL_BAR) != 0) {
@@ -3107,12 +3107,15 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}, mService.getRemoteInlineSuggestionRenderServiceLocked());
};
- // There are 3 cases when augmented autofill should ask IME for a new request:
- // 1. standard autofill provider is None
- // 2. standard autofill provider doesn't support inline (and returns null response)
- // 3. standard autofill provider supports inline, but isn't called because the field
- // doesn't want autofill
- if (mForAugmentedAutofillOnly || !isInlineSuggestionsEnabledLocked()) {
+ // When the inline suggestion render service is available, there are 2 cases when
+ // augmented autofill should ask IME for inline suggestion request, because standard
+ // autofill flow didn't:
+ // 1. the field is augmented autofill only (when standard autofill provider is None or
+ // when it returns null response)
+ // 2. standard autofill provider doesn't support inline suggestion
+ if (isInlineSuggestionRenderServiceAvailable()
+ && (mForAugmentedAutofillOnly
+ || !isInlineSuggestionsEnabledByAutofillProviderLocked())) {
if (sDebug) Slog.d(TAG, "Create inline request for augmented autofill");
mInlineSuggestionSession.onCreateInlineSuggestionsRequest(mCurrentViewId,
/*requestConsumer=*/ requestAugmentedAutofill);
diff --git a/services/core/java/com/android/server/display/DisplayDevice.java b/services/core/java/com/android/server/display/DisplayDevice.java
index 63a8d7c92441..696daca79092 100644
--- a/services/core/java/com/android/server/display/DisplayDevice.java
+++ b/services/core/java/com/android/server/display/DisplayDevice.java
@@ -19,6 +19,7 @@ package com.android.server.display;
import android.graphics.Rect;
import android.hardware.display.DisplayViewport;
import android.os.IBinder;
+import android.view.Display;
import android.view.DisplayAddress;
import android.view.Surface;
import android.view.SurfaceControl;
@@ -78,6 +79,13 @@ abstract class DisplayDevice {
}
/**
+ * Gets the id of the display to mirror.
+ */
+ public int getDisplayIdToMirrorLocked() {
+ return Display.DEFAULT_DISPLAY;
+ }
+
+ /**
* Gets the name of the display device.
*
* @return The display device name.
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index a23205124f74..3afbf661f97e 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -57,6 +57,7 @@ import android.hardware.display.DisplayedContentSamplingAttributes;
import android.hardware.display.IDisplayManager;
import android.hardware.display.IDisplayManagerCallback;
import android.hardware.display.IVirtualDisplayCallback;
+import android.hardware.display.VirtualDisplayConfig;
import android.hardware.display.WifiDisplayStatus;
import android.hardware.input.InputManagerInternal;
import android.media.projection.IMediaProjection;
@@ -794,8 +795,8 @@ public final class DisplayManagerService extends SystemService {
}
private int createVirtualDisplayInternal(IVirtualDisplayCallback callback,
- IMediaProjection projection, int callingUid, String packageName, String name, int width,
- int height, int densityDpi, Surface surface, int flags, String uniqueId) {
+ IMediaProjection projection, int callingUid, String packageName, Surface surface,
+ int flags, VirtualDisplayConfig virtualDisplayConfig) {
synchronized (mSyncRoot) {
if (mVirtualDisplayAdapter == null) {
Slog.w(TAG, "Rejecting request to create private virtual display "
@@ -804,8 +805,8 @@ public final class DisplayManagerService extends SystemService {
}
DisplayDevice device = mVirtualDisplayAdapter.createVirtualDisplayLocked(
- callback, projection, callingUid, packageName, name, width, height, densityDpi,
- surface, flags, uniqueId);
+ callback, projection, callingUid, packageName, surface, flags,
+ virtualDisplayConfig);
if (device == null) {
return -1;
}
@@ -1480,8 +1481,8 @@ public final class DisplayManagerService extends SystemService {
if (!ownContent) {
if (display != null && !display.hasContentLocked()) {
// If the display does not have any content of its own, then
- // automatically mirror the default logical display contents.
- display = null;
+ // automatically mirror the requested logical display contents if possible.
+ display = mLogicalDisplays.get(device.getDisplayIdToMirrorLocked());
}
if (display == null) {
display = mLogicalDisplays.get(Display.DEFAULT_DISPLAY);
@@ -1729,6 +1730,28 @@ public final class DisplayManagerService extends SystemService {
}
}
+ @VisibleForTesting
+ int getDisplayIdToMirrorInternal(int displayId) {
+ synchronized (mSyncRoot) {
+ LogicalDisplay display = mLogicalDisplays.get(displayId);
+ if (display != null) {
+ DisplayDevice displayDevice = display.getPrimaryDisplayDeviceLocked();
+ return displayDevice.getDisplayIdToMirrorLocked();
+ }
+ return Display.INVALID_DISPLAY;
+ }
+ }
+
+ @VisibleForTesting
+ Surface getVirtualDisplaySurfaceInternal(IBinder appToken) {
+ synchronized (mSyncRoot) {
+ if (mVirtualDisplayAdapter == null) {
+ return null;
+ }
+ return mVirtualDisplayAdapter.getVirtualDisplaySurfaceLocked(appToken);
+ }
+ }
+
private final class DisplayManagerHandler extends Handler {
public DisplayManagerHandler(Looper looper) {
super(looper, null, true /*async*/);
@@ -2050,10 +2073,8 @@ public final class DisplayManagerService extends SystemService {
}
@Override // Binder call
- public int createVirtualDisplay(IVirtualDisplayCallback callback,
- IMediaProjection projection, String packageName, String name,
- int width, int height, int densityDpi, Surface surface, int flags,
- String uniqueId) {
+ public int createVirtualDisplay(VirtualDisplayConfig virtualDisplayConfig,
+ IVirtualDisplayCallback callback, IMediaProjection projection, String packageName) {
final int callingUid = Binder.getCallingUid();
if (!validatePackageName(callingUid, packageName)) {
throw new SecurityException("packageName must match the calling uid");
@@ -2061,13 +2082,12 @@ public final class DisplayManagerService extends SystemService {
if (callback == null) {
throw new IllegalArgumentException("appToken must not be null");
}
- if (TextUtils.isEmpty(name)) {
- throw new IllegalArgumentException("name must be non-null and non-empty");
- }
- if (width <= 0 || height <= 0 || densityDpi <= 0) {
- throw new IllegalArgumentException("width, height, and densityDpi must be "
- + "greater than 0");
+ if (virtualDisplayConfig == null) {
+ throw new IllegalArgumentException("virtualDisplayConfig must not be null");
}
+ final Surface surface = virtualDisplayConfig.getSurface();
+ int flags = virtualDisplayConfig.getFlags();
+
if (surface != null && surface.isSingleBuffered()) {
throw new IllegalArgumentException("Surface can't be single-buffered");
}
@@ -2128,7 +2148,7 @@ public final class DisplayManagerService extends SystemService {
final long token = Binder.clearCallingIdentity();
try {
return createVirtualDisplayInternal(callback, projection, callingUid, packageName,
- name, width, height, densityDpi, surface, flags, uniqueId);
+ surface, flags, virtualDisplayConfig);
} finally {
Binder.restoreCallingIdentity(token);
}
diff --git a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
index f4f2eadfaa8e..ccd88483593a 100644
--- a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
@@ -28,6 +28,7 @@ import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_SUPPO
import android.content.Context;
import android.hardware.display.IVirtualDisplayCallback;
+import android.hardware.display.VirtualDisplayConfig;
import android.media.projection.IMediaProjection;
import android.media.projection.IMediaProjectionCallback;
import android.os.Handler;
@@ -84,22 +85,24 @@ public class VirtualDisplayAdapter extends DisplayAdapter {
}
public DisplayDevice createVirtualDisplayLocked(IVirtualDisplayCallback callback,
- IMediaProjection projection, int ownerUid, String ownerPackageName, String name,
- int width, int height, int densityDpi, Surface surface, int flags, String uniqueId) {
+ IMediaProjection projection, int ownerUid, String ownerPackageName, Surface surface,
+ int flags, VirtualDisplayConfig virtualDisplayConfig) {
+ String name = virtualDisplayConfig.getName();
boolean secure = (flags & VIRTUAL_DISPLAY_FLAG_SECURE) != 0;
IBinder appToken = callback.asBinder();
IBinder displayToken = mSurfaceControlDisplayFactory.createDisplay(name, secure);
final String baseUniqueId =
UNIQUE_ID_PREFIX + ownerPackageName + "," + ownerUid + "," + name + ",";
final int uniqueIndex = getNextUniqueIndex(baseUniqueId);
+ String uniqueId = virtualDisplayConfig.getUniqueId();
if (uniqueId == null) {
uniqueId = baseUniqueId + uniqueIndex;
} else {
uniqueId = UNIQUE_ID_PREFIX + ownerPackageName + ":" + uniqueId;
}
VirtualDisplayDevice device = new VirtualDisplayDevice(displayToken, appToken,
- ownerUid, ownerPackageName, name, width, height, densityDpi, surface, flags,
- new Callback(callback, mHandler), uniqueId, uniqueIndex);
+ ownerUid, ownerPackageName, surface, flags, new Callback(callback, mHandler),
+ uniqueId, uniqueIndex, virtualDisplayConfig);
mVirtualDisplayDevices.put(appToken, device);
@@ -127,6 +130,14 @@ public class VirtualDisplayAdapter extends DisplayAdapter {
}
}
+ @VisibleForTesting
+ Surface getVirtualDisplaySurfaceLocked(IBinder appToken) {
+ VirtualDisplayDevice device = mVirtualDisplayDevices.get(appToken);
+ if (device != null) {
+ return device.getSurfaceLocked();
+ }
+ return null;
+ }
public void setVirtualDisplaySurfaceLocked(IBinder appToken, Surface surface) {
VirtualDisplayDevice device = mVirtualDisplayDevices.get(appToken);
@@ -214,20 +225,21 @@ public class VirtualDisplayAdapter extends DisplayAdapter {
private int mUniqueIndex;
private Display.Mode mMode;
private boolean mIsDisplayOn;
+ private int mDisplayIdToMirror;
public VirtualDisplayDevice(IBinder displayToken, IBinder appToken,
- int ownerUid, String ownerPackageName,
- String name, int width, int height, int densityDpi, Surface surface, int flags,
- Callback callback, String uniqueId, int uniqueIndex) {
+ int ownerUid, String ownerPackageName, Surface surface, int flags,
+ Callback callback, String uniqueId, int uniqueIndex,
+ VirtualDisplayConfig virtualDisplayConfig) {
super(VirtualDisplayAdapter.this, displayToken, uniqueId);
mAppToken = appToken;
mOwnerUid = ownerUid;
mOwnerPackageName = ownerPackageName;
- mName = name;
- mWidth = width;
- mHeight = height;
- mMode = createMode(width, height, REFRESH_RATE);
- mDensityDpi = densityDpi;
+ mName = virtualDisplayConfig.getName();
+ mWidth = virtualDisplayConfig.getWidth();
+ mHeight = virtualDisplayConfig.getHeight();
+ mMode = createMode(mWidth, mHeight, REFRESH_RATE);
+ mDensityDpi = virtualDisplayConfig.getDensityDpi();
mSurface = surface;
mFlags = flags;
mCallback = callback;
@@ -235,6 +247,7 @@ public class VirtualDisplayAdapter extends DisplayAdapter {
mPendingChanges |= PENDING_SURFACE_CHANGE;
mUniqueIndex = uniqueIndex;
mIsDisplayOn = surface != null;
+ mDisplayIdToMirror = virtualDisplayConfig.getDisplayIdToMirror();
}
@Override
@@ -260,6 +273,16 @@ public class VirtualDisplayAdapter extends DisplayAdapter {
}
@Override
+ public int getDisplayIdToMirrorLocked() {
+ return mDisplayIdToMirror;
+ }
+
+ @VisibleForTesting
+ Surface getSurfaceLocked() {
+ return mSurface;
+ }
+
+ @Override
public boolean hasStableUniqueId() {
return false;
}
@@ -332,6 +355,7 @@ public class VirtualDisplayAdapter extends DisplayAdapter {
pw.println("mFlags=" + mFlags);
pw.println("mDisplayState=" + Display.stateToString(mDisplayState));
pw.println("mStopped=" + mStopped);
+ pw.println("mDisplayIdToMirror=" + mDisplayIdToMirror);
}
diff --git a/services/core/java/com/android/server/vr/Vr2dDisplay.java b/services/core/java/com/android/server/vr/Vr2dDisplay.java
index a16dbb726613..3f2b5c231dca 100644
--- a/services/core/java/com/android/server/vr/Vr2dDisplay.java
+++ b/services/core/java/com/android/server/vr/Vr2dDisplay.java
@@ -11,6 +11,7 @@ import android.content.IntentFilter;
import android.graphics.PixelFormat;
import android.hardware.display.DisplayManager;
import android.hardware.display.VirtualDisplay;
+import android.hardware.display.VirtualDisplayConfig;
import android.media.ImageReader;
import android.os.Handler;
import android.os.RemoteException;
@@ -295,10 +296,12 @@ class Vr2dDisplay {
flags |= DisplayManager.VIRTUAL_DISPLAY_FLAG_DESTROY_CONTENT_ON_REMOVAL;
flags |= DisplayManager.VIRTUAL_DISPLAY_FLAG_SECURE;
+ final VirtualDisplayConfig.Builder builder = new VirtualDisplayConfig.Builder(
+ DISPLAY_NAME, mVirtualDisplayWidth, mVirtualDisplayHeight, mVirtualDisplayDpi);
+ builder.setUniqueId(UNIQUE_DISPLAY_ID);
+ builder.setFlags(flags);
mVirtualDisplay = mDisplayManager.createVirtualDisplay(null /* projection */,
- DISPLAY_NAME, mVirtualDisplayWidth, mVirtualDisplayHeight, mVirtualDisplayDpi,
- null /* surface */, flags, null /* callback */, null /* handler */,
- UNIQUE_DISPLAY_ID);
+ builder.build(), null /* callback */, null /* handler */);
if (mVirtualDisplay != null) {
updateDisplayId(mVirtualDisplay.getDisplay().getDisplayId());
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index 9356ec200f20..77ef01134292 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -732,11 +732,11 @@ final class TaskDisplayArea extends DisplayArea<ActivityStack> {
/**
* Returns an existing stack compatible with the windowing mode and activity type or creates one
* if a compatible stack doesn't exist.
- * @see #getOrCreateStack(int, int, boolean, Intent, Task, boolean)
+ * @see #getOrCreateStack(int, int, boolean, Intent, Task)
*/
ActivityStack getOrCreateStack(int windowingMode, int activityType, boolean onTop) {
return getOrCreateStack(windowingMode, activityType, onTop, null /* intent */,
- null /* candidateTask */, false /* createdByOrganizer */);
+ null /* candidateTask */);
}
/**
@@ -748,7 +748,7 @@ final class TaskDisplayArea extends DisplayArea<ActivityStack> {
* @see #createStack(int, int, boolean)
*/
ActivityStack getOrCreateStack(int windowingMode, int activityType, boolean onTop,
- Intent intent, Task candidateTask, boolean createdByOrganizer) {
+ Intent intent, Task candidateTask) {
if (!alwaysCreateStack(windowingMode, activityType)) {
ActivityStack stack = getStack(windowingMode, activityType);
if (stack != null) {
@@ -757,13 +757,13 @@ final class TaskDisplayArea extends DisplayArea<ActivityStack> {
} else if (candidateTask != null) {
final ActivityStack stack = (ActivityStack) candidateTask;
final int position = onTop ? POSITION_TOP : POSITION_BOTTOM;
- if (isSplitScreenModeActivated()) {
- final Task splitRootSecondary = getTask(t -> t.mCreatedByOrganizer && t.isRootTask()
- && t.inSplitScreenSecondaryWindowingMode());
+ Task launchRootTask = updateLaunchRootTask(windowingMode);
+
+ if (launchRootTask != null) {
if (stack.getParent() == null) {
- splitRootSecondary.addChild(stack, position);
- } else if (stack.getParent() != splitRootSecondary) {
- stack.reparent(splitRootSecondary, position);
+ launchRootTask.addChild(stack, position);
+ } else if (stack.getParent() != launchRootTask) {
+ stack.reparent(launchRootTask, position);
}
} else if (stack.getDisplay() != mDisplayContent || !stack.isRootTask()) {
if (stack.getParent() == null) {
@@ -779,7 +779,7 @@ final class TaskDisplayArea extends DisplayArea<ActivityStack> {
return stack;
}
return createStack(windowingMode, activityType, onTop, null /*info*/, intent,
- createdByOrganizer);
+ false /* createdByOrganizer */);
}
/**
@@ -798,7 +798,7 @@ final class TaskDisplayArea extends DisplayArea<ActivityStack> {
// it's display's windowing mode.
windowingMode = validateWindowingMode(windowingMode, r, candidateTask, activityType);
return getOrCreateStack(windowingMode, activityType, onTop, null /* intent */,
- candidateTask, false /* createdByOrganizer */);
+ candidateTask);
}
@VisibleForTesting
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index 872f2543edb8..b641e4c391ce 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -277,9 +277,9 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
return null;
}
- final Task task = display.mTaskContainers.getOrCreateStack(windowingMode,
- ACTIVITY_TYPE_UNDEFINED, false /* onTop */, new Intent(),
- null /* candidateTask */, true /* createdByOrganizer */);
+ final Task task = display.mTaskContainers.createStack(windowingMode,
+ ACTIVITY_TYPE_UNDEFINED, false /* onTop */, null /* info */, new Intent(),
+ true /* createdByOrganizer */);
RunningTaskInfo out = task.getTaskInfo();
mLastSentTaskInfos.put(task, out);
return out;
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index b25383b15421..d4470f8334ea 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -1066,14 +1066,15 @@ class WindowStateAnimator {
if (!w.mSeamlesslyRotated) {
+ // Wallpaper is already updated above when calling setWallpaperPositionAndScale so
+ // we only need to consider the non-wallpaper case here.
if (!mIsWallpaper) {
applyCrop(clipRect, recoveringMemory);
- mSurfaceController.setMatrixInTransaction(mDsDx * w.mHScale * mExtraHScale,
+ mSurfaceController.setMatrixInTransaction(
+ mDsDx * w.mHScale * mExtraHScale,
mDtDx * w.mVScale * mExtraVScale,
mDtDy * w.mHScale * mExtraHScale,
mDsDy * w.mVScale * mExtraVScale, recoveringMemory);
- } else {
- setWallpaperPositionAndScale(mXOffset, mYOffset, mWallpaperScale, recoveringMemory);
}
}
@@ -1152,13 +1153,20 @@ class WindowStateAnimator {
mSurfaceController, mShownAlpha, mDsDx, w.mHScale, mDtDx, w.mVScale,
mDtDy, w.mHScale, mDsDy, w.mVScale, w);
- boolean prepared =
- mSurfaceController.prepareToShowInTransaction(mShownAlpha,
+ boolean prepared = true;
+
+ if (mIsWallpaper) {
+ setWallpaperPositionAndScale(
+ mXOffset, mYOffset, mWallpaperScale, recoveringMemory);
+ } else {
+ prepared =
+ mSurfaceController.prepareToShowInTransaction(mShownAlpha,
mDsDx * w.mHScale * mExtraHScale,
mDtDx * w.mVScale * mExtraVScale,
mDtDy * w.mHScale * mExtraHScale,
mDsDy * w.mVScale * mExtraVScale,
recoveringMemory);
+ }
if (prepared && mDrawState == HAS_DRAWN) {
if (mLastHidden) {
diff --git a/services/core/java/com/android/server/wm/WindowSurfaceController.java b/services/core/java/com/android/server/wm/WindowSurfaceController.java
index d7c97b922d2d..f7ea953e5a01 100644
--- a/services/core/java/com/android/server/wm/WindowSurfaceController.java
+++ b/services/core/java/com/android/server/wm/WindowSurfaceController.java
@@ -127,6 +127,7 @@ class WindowSurfaceController {
mBLASTSurfaceControl = win.makeSurface()
.setParent(mSurfaceControl)
.setName("BLAST Adapter Layer")
+ .setHidden(false)
.setBLASTLayer()
.build();
}
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
index 702718569871..7ad39b440e14 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -34,14 +34,17 @@ import android.hardware.display.DisplayViewport;
import android.hardware.display.DisplayedContentSample;
import android.hardware.display.DisplayedContentSamplingAttributes;
import android.hardware.display.IVirtualDisplayCallback;
+import android.hardware.display.VirtualDisplayConfig;
import android.hardware.input.InputManagerInternal;
import android.os.Handler;
import android.os.IBinder;
import android.view.Display;
import android.view.DisplayInfo;
+import android.view.Surface;
import android.view.SurfaceControl;
import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.FlakyTest;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -67,6 +70,8 @@ import java.util.stream.LongStream;
public class DisplayManagerServiceTest {
private static final int MSG_REGISTER_DEFAULT_DISPLAY_ADAPTERS = 1;
private static final long SHORT_DEFAULT_DISPLAY_TIMEOUT_MILLIS = 10;
+ private static final String VIRTUAL_DISPLAY_NAME = "Test Virtual Display";
+ private static final String PACKAGE_NAME = "com.android.frameworks.servicestests";
private Context mContext;
@@ -97,6 +102,7 @@ public class DisplayManagerServiceTest {
@Mock InputManagerInternal mMockInputManagerInternal;
@Mock IVirtualDisplayCallback.Stub mMockAppToken;
+ @Mock IVirtualDisplayCallback.Stub mMockAppToken2;
@Mock WindowManagerInternal mMockWindowManagerInternal;
@Mock LightsManager mMockLightsManager;
@Mock VirtualDisplayAdapter mMockVirtualDisplayAdapter;
@@ -135,10 +141,12 @@ public class DisplayManagerServiceTest {
int flags = DisplayManager.VIRTUAL_DISPLAY_FLAG_SUPPORTS_TOUCH;
when(mMockAppToken.asBinder()).thenReturn(mMockAppToken);
- int displayId = bs.createVirtualDisplay(mMockAppToken /* callback */,
- null /* projection */, "com.android.frameworks.servicestests",
- "Test Virtual Display", width, height, dpi, null /* surface */, flags /* flags */,
- uniqueId);
+ final VirtualDisplayConfig.Builder builder = new VirtualDisplayConfig.Builder(
+ VIRTUAL_DISPLAY_NAME, width, height, dpi);
+ builder.setUniqueId(uniqueId);
+ builder.setFlags(flags);
+ int displayId = bs.createVirtualDisplay(builder.build(), mMockAppToken /* callback */,
+ null /* projection */, PACKAGE_NAME);
displayManager.performTraversalInternal(mock(SurfaceControl.Transaction.class));
@@ -241,10 +249,12 @@ public class DisplayManagerServiceTest {
int flags = DisplayManager.VIRTUAL_DISPLAY_FLAG_ROTATES_WITH_CONTENT;
when(mMockAppToken.asBinder()).thenReturn(mMockAppToken);
- int displayId = bs.createVirtualDisplay(mMockAppToken /* callback */,
- null /* projection */, "com.android.frameworks.servicestests",
- "Test Virtual Display", width, height, dpi, null /* surface */, flags /* flags */,
- uniqueId);
+ final VirtualDisplayConfig.Builder builder = new VirtualDisplayConfig.Builder(
+ VIRTUAL_DISPLAY_NAME, width, height, dpi);
+ builder.setFlags(flags);
+ builder.setUniqueId(uniqueId);
+ int displayId = bs.createVirtualDisplay(builder.build(), mMockAppToken /* callback */,
+ null /* projection */, PACKAGE_NAME);
displayManager.performTraversalInternal(mock(SurfaceControl.Transaction.class));
@@ -409,6 +419,87 @@ public class DisplayManagerServiceTest {
assertTrue(samples.length == 0 || LongStream.of(samples).sum() == numPixels);
}
+ /**
+ * Tests that the virtual display is created with
+ * {@link VirtualDisplayConfig.Builder#setDisplayIdToMirror(int)}
+ */
+ @Test
+ @FlakyTest(bugId = 127687569)
+ public void testCreateVirtualDisplay_displayIdToMirror() throws Exception {
+ DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector);
+ registerDefaultDisplays(displayManager);
+
+ // This is effectively the DisplayManager service published to ServiceManager.
+ DisplayManagerService.BinderService binderService = displayManager.new BinderService();
+
+ final String uniqueId = "uniqueId --- displayIdToMirrorTest";
+ final int width = 600;
+ final int height = 800;
+ final int dpi = 320;
+
+ when(mMockAppToken.asBinder()).thenReturn(mMockAppToken);
+ final VirtualDisplayConfig.Builder builder = new VirtualDisplayConfig.Builder(
+ VIRTUAL_DISPLAY_NAME, width, height, dpi);
+ builder.setUniqueId(uniqueId);
+ final int firstDisplayId = binderService.createVirtualDisplay(builder.build(),
+ mMockAppToken /* callback */, null /* projection */, PACKAGE_NAME);
+
+ // The second virtual display requests to mirror the first virtual display.
+ final String uniqueId2 = "uniqueId --- displayIdToMirrorTest #2";
+ when(mMockAppToken2.asBinder()).thenReturn(mMockAppToken2);
+ final VirtualDisplayConfig.Builder builder2 = new VirtualDisplayConfig.Builder(
+ VIRTUAL_DISPLAY_NAME, width, height, dpi).setUniqueId(uniqueId2);
+ builder2.setUniqueId(uniqueId2);
+ builder2.setDisplayIdToMirror(firstDisplayId);
+ final int secondDisplayId = binderService.createVirtualDisplay(builder2.build(),
+ mMockAppToken2 /* callback */, null /* projection */, PACKAGE_NAME);
+ displayManager.performTraversalInternal(mock(SurfaceControl.Transaction.class));
+
+ // flush the handler
+ displayManager.getDisplayHandler().runWithScissors(() -> {}, 0 /* now */);
+
+ // The displayId to mirror should be a default display if there is none initially.
+ assertEquals(displayManager.getDisplayIdToMirrorInternal(firstDisplayId),
+ Display.DEFAULT_DISPLAY);
+ assertEquals(displayManager.getDisplayIdToMirrorInternal(secondDisplayId),
+ firstDisplayId);
+ }
+
+ /**
+ * Tests that the virtual display is created with
+ * {@link VirtualDisplayConfig.Builder#setSurface(Surface)}
+ */
+ @Test
+ @FlakyTest(bugId = 127687569)
+ public void testCreateVirtualDisplay_setSurface() throws Exception {
+ DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector);
+ registerDefaultDisplays(displayManager);
+
+ // This is effectively the DisplayManager service published to ServiceManager.
+ DisplayManagerService.BinderService binderService = displayManager.new BinderService();
+
+ final String uniqueId = "uniqueId --- setSurface";
+ final int width = 600;
+ final int height = 800;
+ final int dpi = 320;
+ final Surface surface = new Surface();
+
+ when(mMockAppToken.asBinder()).thenReturn(mMockAppToken);
+ final VirtualDisplayConfig.Builder builder = new VirtualDisplayConfig.Builder(
+ VIRTUAL_DISPLAY_NAME, width, height, dpi);
+ builder.setSurface(surface);
+ builder.setUniqueId(uniqueId);
+ final int displayId = binderService.createVirtualDisplay(builder.build(),
+ mMockAppToken /* callback */, null /* projection */, PACKAGE_NAME);
+
+ displayManager.performTraversalInternal(mock(SurfaceControl.Transaction.class));
+
+ // flush the handler
+ displayManager.getDisplayHandler().runWithScissors(() -> {}, 0 /* now */);
+
+ assertEquals(displayManager.getVirtualDisplaySurfaceInternal(mMockAppToken), surface);
+ }
+
private void registerDefaultDisplays(DisplayManagerService displayManager) {
Handler handler = displayManager.getDisplayHandler();
// Would prefer to call displayManager.onStart() directly here but it performs binderService
diff --git a/telephony/java/android/telephony/CellIdentityLte.java b/telephony/java/android/telephony/CellIdentityLte.java
index c37735c729a2..13a8273d64d4 100644
--- a/telephony/java/android/telephony/CellIdentityLte.java
+++ b/telephony/java/android/telephony/CellIdentityLte.java
@@ -153,8 +153,10 @@ public final class CellIdentityLte extends CellIdentity {
cid.bands.stream().mapToInt(Integer::intValue).toArray(), cid.base.bandwidth,
cid.base.base.mcc, cid.base.base.mnc, cid.base.operatorNames.alphaLong,
cid.base.operatorNames.alphaShort, cid.additionalPlmns,
- cid.optionalCsgInfo.csgInfo() != null
- ? new ClosedSubscriberGroupInfo(cid.optionalCsgInfo.csgInfo()) : null);
+ cid.optionalCsgInfo.getDiscriminator()
+ == android.hardware.radio.V1_5.OptionalCsgInfo.hidl_discriminator.csgInfo
+ ? new ClosedSubscriberGroupInfo(cid.optionalCsgInfo.csgInfo())
+ : null);
}
private CellIdentityLte(@NonNull CellIdentityLte cid) {
diff --git a/telephony/java/android/telephony/CellIdentityTdscdma.java b/telephony/java/android/telephony/CellIdentityTdscdma.java
index 3f95596a076e..6dffe922ffd1 100644
--- a/telephony/java/android/telephony/CellIdentityTdscdma.java
+++ b/telephony/java/android/telephony/CellIdentityTdscdma.java
@@ -128,8 +128,11 @@ public final class CellIdentityTdscdma extends CellIdentity {
this(cid.base.base.mcc, cid.base.base.mnc, cid.base.base.lac, cid.base.base.cid,
cid.base.base.cpid, cid.base.uarfcn, cid.base.operatorNames.alphaLong,
cid.base.operatorNames.alphaShort,
- cid.additionalPlmns, cid.optionalCsgInfo.csgInfo() != null
- ? new ClosedSubscriberGroupInfo(cid.optionalCsgInfo.csgInfo()) : null);
+ cid.additionalPlmns,
+ cid.optionalCsgInfo.getDiscriminator()
+ == android.hardware.radio.V1_5.OptionalCsgInfo.hidl_discriminator.csgInfo
+ ? new ClosedSubscriberGroupInfo(cid.optionalCsgInfo.csgInfo())
+ : null);
}
/** @hide */
diff --git a/telephony/java/android/telephony/CellIdentityWcdma.java b/telephony/java/android/telephony/CellIdentityWcdma.java
index 38c4ed482e14..eab174ade3b7 100644
--- a/telephony/java/android/telephony/CellIdentityWcdma.java
+++ b/telephony/java/android/telephony/CellIdentityWcdma.java
@@ -123,8 +123,10 @@ public final class CellIdentityWcdma extends CellIdentity {
this(cid.base.base.lac, cid.base.base.cid, cid.base.base.psc, cid.base.base.uarfcn,
cid.base.base.mcc, cid.base.base.mnc, cid.base.operatorNames.alphaLong,
cid.base.operatorNames.alphaShort, cid.additionalPlmns,
- cid.optionalCsgInfo.csgInfo() != null
- ? new ClosedSubscriberGroupInfo(cid.optionalCsgInfo.csgInfo()) : null);
+ cid.optionalCsgInfo.getDiscriminator()
+ == android.hardware.radio.V1_5.OptionalCsgInfo.hidl_discriminator.csgInfo
+ ? new ClosedSubscriberGroupInfo(cid.optionalCsgInfo.csgInfo())
+ : null);
}
private CellIdentityWcdma(@NonNull CellIdentityWcdma cid) {