diff options
288 files changed, 7104 insertions, 2938 deletions
diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp index 6aad82fda915..d22549738e74 100644 --- a/cmds/statsd/Android.bp +++ b/cmds/statsd/Android.bp @@ -40,6 +40,7 @@ cc_defaults { "src/external/StatsPullerManager.cpp", "src/external/TrainInfoPuller.cpp", "src/FieldValue.cpp", + "src/flags/flags.cpp", "src/guardrail/StatsdStats.cpp", "src/hash.cpp", "src/HashableDimensionKey.cpp", @@ -92,6 +93,7 @@ cc_defaults { "libstatslog_statsd", "libsysutils", "libutils", + "server_configurable_flags", "statsd-aidl-ndk_platform", ], shared_libs: [ @@ -265,6 +267,7 @@ cc_test { "tests/e2e/Anomaly_duration_sum_e2e_test.cpp", "tests/e2e/Attribution_e2e_test.cpp", "tests/e2e/ConfigTtl_e2e_test.cpp", + "tests/e2e/ConfigUpdate_e2e_ab_test.cpp", "tests/e2e/ConfigUpdate_e2e_test.cpp", "tests/e2e/CountMetric_e2e_test.cpp", "tests/e2e/DurationMetric_e2e_test.cpp", diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp index 6eff639aca92..c5d9f54dbf38 100644 --- a/cmds/statsd/src/StatsLogProcessor.cpp +++ b/cmds/statsd/src/StatsLogProcessor.cpp @@ -24,12 +24,13 @@ #include <frameworks/base/cmds/statsd/src/active_config_list.pb.h> #include <frameworks/base/cmds/statsd/src/experiment_ids.pb.h> +#include "StatsService.h" #include "android-base/stringprintf.h" #include "external/StatsPullerManager.h" +#include "flags/flags.h" #include "guardrail/StatsdStats.h" #include "logd/LogEvent.h" #include "metrics/CountMetricProducer.h" -#include "StatsService.h" #include "state/StateManager.h" #include "stats_log_util.h" #include "stats_util.h" @@ -520,9 +521,10 @@ void StatsLogProcessor::GetActiveConfigsLocked(const int uid, vector<int64_t>& o } void StatsLogProcessor::OnConfigUpdated(const int64_t timestampNs, const ConfigKey& key, - const StatsdConfig& config, bool modularUpdate) { + const StatsdConfig& config) { std::lock_guard<std::mutex> lock(mMetricsMutex); WriteDataToDiskLocked(key, timestampNs, CONFIG_UPDATED, NO_TIME_CONSTRAINTS); + bool modularUpdate = getFlagBool(PARTIAL_CONFIG_UPDATE_FLAG, "false"); OnConfigUpdatedLocked(timestampNs, key, config, modularUpdate); } diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h index 2af277ad1e5b..a320b2f3ba5c 100644 --- a/cmds/statsd/src/StatsLogProcessor.h +++ b/cmds/statsd/src/StatsLogProcessor.h @@ -48,7 +48,7 @@ public: void OnLogEvent(LogEvent* event); void OnConfigUpdated(const int64_t timestampNs, const ConfigKey& key, - const StatsdConfig& config, bool modularUpdate = false); + const StatsdConfig& config); void OnConfigRemoved(const ConfigKey& key); size_t GetMetricsSize(const ConfigKey& key) const; @@ -338,9 +338,9 @@ private: FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithSameDeactivation); FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations); - FRIEND_TEST(ConfigUpdateE2eTest, TestHashStrings); - FRIEND_TEST(ConfigUpdateE2eTest, TestUidMapVersionStringInstaller); - FRIEND_TEST(ConfigUpdateE2eTest, TestConfigTtl); + FRIEND_TEST(ConfigUpdateE2eAbTest, TestHashStrings); + FRIEND_TEST(ConfigUpdateE2eAbTest, TestUidMapVersionStringInstaller); + FRIEND_TEST(ConfigUpdateE2eAbTest, TestConfigTtl); FRIEND_TEST(CountMetricE2eTest, TestInitialConditionChanges); FRIEND_TEST(CountMetricE2eTest, TestSlicedState); diff --git a/cmds/statsd/src/condition/CombinationConditionTracker.h b/cmds/statsd/src/condition/CombinationConditionTracker.h index 672d61c82268..d355146c13ef 100644 --- a/cmds/statsd/src/condition/CombinationConditionTracker.h +++ b/cmds/statsd/src/condition/CombinationConditionTracker.h @@ -85,16 +85,14 @@ public: const std::vector<sp<ConditionTracker>>& allConditions, const vector<Matcher>& dimensions) const override; - void getTrueSlicedDimensions( - const std::vector<sp<ConditionTracker>>& allConditions, - std::set<HashableDimensionKey>* dimensions) const override { + const std::map<HashableDimensionKey, int>* getSlicedDimensionMap( + const std::vector<sp<ConditionTracker>>& allConditions) const override { if (mSlicedChildren.size() == 1) { - return allConditions[mSlicedChildren.front()]->getTrueSlicedDimensions( - allConditions, dimensions); + return allConditions[mSlicedChildren.front()]->getSlicedDimensionMap(allConditions); } + return nullptr; } - private: LogicalOperation mLogicalOperation; diff --git a/cmds/statsd/src/condition/ConditionTracker.h b/cmds/statsd/src/condition/ConditionTracker.h index 5a6b8cf334eb..7af022a5677a 100644 --- a/cmds/statsd/src/condition/ConditionTracker.h +++ b/cmds/statsd/src/condition/ConditionTracker.h @@ -135,9 +135,8 @@ public: return mProtoHash; } - virtual void getTrueSlicedDimensions( - const std::vector<sp<ConditionTracker>>& allConditions, - std::set<HashableDimensionKey>* dimensions) const = 0; + virtual const std::map<HashableDimensionKey, int>* getSlicedDimensionMap( + const std::vector<sp<ConditionTracker>>& allConditions) const = 0; virtual bool IsChangedDimensionTrackable() const = 0; diff --git a/cmds/statsd/src/condition/ConditionWizard.h b/cmds/statsd/src/condition/ConditionWizard.h index 892647910d9f..43db94cfa47d 100644 --- a/cmds/statsd/src/condition/ConditionWizard.h +++ b/cmds/statsd/src/condition/ConditionWizard.h @@ -53,9 +53,9 @@ public: ConditionState getUnSlicedPartConditionState(const int index) { return mAllConditions[index]->getUnSlicedPartConditionState(); } - void getTrueSlicedDimensions(const int index, - std::set<HashableDimensionKey>* trueDimensions) const { - return mAllConditions[index]->getTrueSlicedDimensions(mAllConditions, trueDimensions); + + const std::map<HashableDimensionKey, int>* getSlicedDimensionMap(const int index) const { + return mAllConditions[index]->getSlicedDimensionMap(mAllConditions); } private: diff --git a/cmds/statsd/src/condition/SimpleConditionTracker.h b/cmds/statsd/src/condition/SimpleConditionTracker.h index 7a8b40108448..2fbc8a7b4ea8 100644 --- a/cmds/statsd/src/condition/SimpleConditionTracker.h +++ b/cmds/statsd/src/condition/SimpleConditionTracker.h @@ -74,14 +74,9 @@ public: } } - void getTrueSlicedDimensions( - const std::vector<sp<ConditionTracker>>& allConditions, - std::set<HashableDimensionKey>* dimensions) const override { - for (const auto& itr : mSlicedConditionState) { - if (itr.second > 0) { - dimensions->insert(itr.first); - } - } + const std::map<HashableDimensionKey, int>* getSlicedDimensionMap( + const std::vector<sp<ConditionTracker>>& allConditions) const override { + return &mSlicedConditionState; } bool IsChangedDimensionTrackable() const override { return true; } diff --git a/cmds/statsd/src/config/ConfigListener.h b/cmds/statsd/src/config/ConfigListener.h index 3d301379f359..dcd5e52feefd 100644 --- a/cmds/statsd/src/config/ConfigListener.h +++ b/cmds/statsd/src/config/ConfigListener.h @@ -39,7 +39,7 @@ public: * A configuration was added or updated. */ virtual void OnConfigUpdated(const int64_t timestampNs, const ConfigKey& key, - const StatsdConfig& config, bool modularUpdate = false) = 0; + const StatsdConfig& config) = 0; /** * A configuration was removed. diff --git a/cmds/statsd/src/flags/flags.cpp b/cmds/statsd/src/flags/flags.cpp new file mode 100644 index 000000000000..e9fceda72b6d --- /dev/null +++ b/cmds/statsd/src/flags/flags.cpp @@ -0,0 +1,37 @@ +/* + * 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 "flags.h" + +#include <server_configurable_flags/get_flags.h> + +using server_configurable_flags::GetServerConfigurableFlag; +using std::string; + +namespace android { +namespace os { +namespace statsd { + +string getFlagString(const string& flagName, const string& defaultValue) { + return GetServerConfigurableFlag(STATSD_NATIVE_NAMESPACE, flagName, defaultValue); +} + +bool getFlagBool(const string& flagName, const string& defaultValue) { + return getFlagString(flagName, defaultValue) == "true"; +} +} // namespace statsd +} // namespace os +} // namespace android diff --git a/cmds/statsd/src/flags/flags.h b/cmds/statsd/src/flags/flags.h new file mode 100644 index 000000000000..213e1a48b074 --- /dev/null +++ b/cmds/statsd/src/flags/flags.h @@ -0,0 +1,36 @@ +/* + * 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. + */ + +#pragma once + +#include <string> + +namespace android { +namespace os { +namespace statsd { + +const std::string STATSD_NATIVE_NAMESPACE = "statsd_native"; + +const std::string PARTIAL_CONFIG_UPDATE_FLAG = "partial_config_update"; + +std::string getFlagString(const std::string& flagName, const std::string& defaultValue); + +// Returns true IFF flagName has a value of "true". +bool getFlagBool(const std::string& flagName, const std::string& defaultValue); + +} // namespace statsd +} // namespace os +} // namespace android diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp index 8869241ab8aa..001ad36134e9 100644 --- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp +++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp @@ -67,8 +67,8 @@ const int FIELD_ID_END_BUCKET_ELAPSED_MILLIS = 6; DurationMetricProducer::DurationMetricProducer( const ConfigKey& key, const DurationMetric& metric, const int conditionIndex, - const vector<ConditionState>& initialConditionCache, const int startIndex, - const int stopIndex, const int stopAllIndex, const bool nesting, + const vector<ConditionState>& initialConditionCache, const int whatIndex, + const int startIndex, const int stopIndex, const int stopAllIndex, const bool nesting, const sp<ConditionWizard>& wizard, const uint64_t protoHash, const FieldMatcher& internalDimensions, const int64_t timeBaseNs, const int64_t startTimeNs, const unordered_map<int, shared_ptr<Activation>>& eventActivationMap, @@ -107,6 +107,12 @@ DurationMetricProducer::DurationMetricProducer( ALOGE("Position ANY in dimension_in_what not supported."); } + // Dimensions in what must be subset of internal dimensions + if (!subsetDimensions(mDimensionsInWhat, mInternalDimensions)) { + ALOGE("Dimensions in what must be a subset of the internal dimensions"); + mValid = false; + } + mSliceByPositionALL = HasPositionALL(metric.dimensions_in_what()); if (metric.links().size() > 0) { @@ -115,6 +121,10 @@ DurationMetricProducer::DurationMetricProducer( mc.conditionId = link.condition(); translateFieldMatcher(link.fields_in_what(), &mc.metricFields); translateFieldMatcher(link.fields_in_condition(), &mc.conditionFields); + if (!subsetDimensions(mc.metricFields, mInternalDimensions)) { + ALOGE(("Condition links must be a subset of the internal dimensions")); + mValid = false; + } mMetric2ConditionLinks.push_back(mc); } mConditionSliced = true; @@ -126,6 +136,10 @@ DurationMetricProducer::DurationMetricProducer( ms.stateAtomId = stateLink.state_atom_id(); translateFieldMatcher(stateLink.fields_in_what(), &ms.metricFields); translateFieldMatcher(stateLink.fields_in_state(), &ms.stateFields); + if (!subsetDimensions(ms.metricFields, mInternalDimensions)) { + ALOGE(("State links must be a subset of the dimensions in what internal dimensions")); + mValid = false; + } mMetric2StateLinks.push_back(ms); } @@ -140,6 +154,8 @@ DurationMetricProducer::DurationMetricProducer( mCurrentBucketStartTimeNs = startTimeNs; VLOG("metric %lld created. bucket size %lld start_time: %lld", (long long)metric.id(), (long long)mBucketSizeNs, (long long)mTimeBaseNs); + + initTrueDimensions(whatIndex, startTimeNs); } DurationMetricProducer::~DurationMetricProducer() { @@ -224,6 +240,23 @@ bool DurationMetricProducer::onConfigUpdatedLocked( return true; } +void DurationMetricProducer::initTrueDimensions(const int whatIndex, const int64_t startTimeNs) { + std::lock_guard<std::mutex> lock(mMutex); + // Currently whatIndex will only be -1 in tests. In the future, we might want to avoid creating + // a ConditionTracker if the condition is only used in the "what" of a duration metric. In that + // scenario, -1 can also be passed. + if (whatIndex == -1) { + return; + } + const map<HashableDimensionKey, int>* slicedWhatMap = mWizard->getSlicedDimensionMap(whatIndex); + for (const auto& [internalDimKey, count] : *slicedWhatMap) { + for (int i = 0; i < count; i++) { + // Fake start events. + handleMatchedLogEventValuesLocked(mStartIndex, internalDimKey.getValues(), startTimeNs); + } + } +} + sp<AnomalyTracker> DurationMetricProducer::addAnomalyTracker( const Alert &alert, const sp<AlarmMonitor>& anomalyAlarmMonitor) { std::lock_guard<std::mutex> lock(mMutex); @@ -330,14 +363,14 @@ void DurationMetricProducer::onSlicedConditionMayChangeLocked_opt1(bool conditio // state based on the new unsliced condition state. if (dimensionsChangedToTrue == nullptr || dimensionsChangedToFalse == nullptr || (dimensionsChangedToTrue->empty() && dimensionsChangedToFalse->empty())) { - std::set<HashableDimensionKey> trueConditionDimensions; - mWizard->getTrueSlicedDimensions(mConditionTrackerIndex, &trueConditionDimensions); + const map<HashableDimensionKey, int>* slicedConditionMap = + mWizard->getSlicedDimensionMap(mConditionTrackerIndex); for (auto& whatIt : mCurrentSlicedDurationTrackerMap) { HashableDimensionKey linkedConditionDimensionKey; getDimensionForCondition(whatIt.first.getValues(), mMetric2ConditionLinks[0], &linkedConditionDimensionKey); - if (trueConditionDimensions.find(linkedConditionDimensionKey) != - trueConditionDimensions.end()) { + const auto& slicedConditionIt = slicedConditionMap->find(linkedConditionDimensionKey); + if (slicedConditionIt != slicedConditionMap->end() && slicedConditionIt->second > 0) { whatIt.second->onConditionChanged(currentUnSlicedPartCondition, eventTime); } } @@ -595,8 +628,9 @@ bool DurationMetricProducer::hitGuardRailLocked(const MetricDimensionKey& newKey } void DurationMetricProducer::handleStartEvent(const MetricDimensionKey& eventKey, - const ConditionKey& conditionKeys, - bool condition, const LogEvent& event) { + const ConditionKey& conditionKeys, bool condition, + const int64_t eventTimeNs, + const vector<FieldValue>& eventValues) { const auto& whatKey = eventKey.getDimensionKeyInWhat(); auto whatIt = mCurrentSlicedDurationTrackerMap.find(whatKey); if (whatIt == mCurrentSlicedDurationTrackerMap.end()) { @@ -608,20 +642,17 @@ void DurationMetricProducer::handleStartEvent(const MetricDimensionKey& eventKey auto it = mCurrentSlicedDurationTrackerMap.find(whatKey); if (mUseWhatDimensionAsInternalDimension) { - it->second->noteStart(whatKey, condition, event.GetElapsedTimestampNs(), conditionKeys); + it->second->noteStart(whatKey, condition, eventTimeNs, conditionKeys); return; } if (mInternalDimensions.empty()) { - it->second->noteStart(DEFAULT_DIMENSION_KEY, condition, event.GetElapsedTimestampNs(), - conditionKeys); + it->second->noteStart(DEFAULT_DIMENSION_KEY, condition, eventTimeNs, conditionKeys); } else { HashableDimensionKey dimensionKey = DEFAULT_DIMENSION_KEY; - filterValues(mInternalDimensions, event.getValues(), &dimensionKey); - it->second->noteStart(dimensionKey, condition, event.GetElapsedTimestampNs(), - conditionKeys); + filterValues(mInternalDimensions, eventValues, &dimensionKey); + it->second->noteStart(dimensionKey, condition, eventTimeNs, conditionKeys); } - } void DurationMetricProducer::onMatchedLogEventInternalLocked( @@ -633,26 +664,32 @@ void DurationMetricProducer::onMatchedLogEventInternalLocked( void DurationMetricProducer::onMatchedLogEventLocked(const size_t matcherIndex, const LogEvent& event) { - int64_t eventTimeNs = event.GetElapsedTimestampNs(); + handleMatchedLogEventValuesLocked(matcherIndex, event.getValues(), + event.GetElapsedTimestampNs()); +} + +void DurationMetricProducer::handleMatchedLogEventValuesLocked(const size_t matcherIndex, + const vector<FieldValue>& values, + const int64_t eventTimeNs) { if (eventTimeNs < mTimeBaseNs) { return; } if (mIsActive) { - flushIfNeededLocked(event.GetElapsedTimestampNs()); + flushIfNeededLocked(eventTimeNs); } // Handles Stopall events. if ((int)matcherIndex == mStopAllIndex) { for (auto& whatIt : mCurrentSlicedDurationTrackerMap) { - whatIt.second->noteStopAll(event.GetElapsedTimestampNs()); + whatIt.second->noteStopAll(eventTimeNs); } return; } HashableDimensionKey dimensionInWhat = DEFAULT_DIMENSION_KEY; if (!mDimensionsInWhat.empty()) { - filterValues(mDimensionsInWhat, event.getValues(), &dimensionInWhat); + filterValues(mDimensionsInWhat, values, &dimensionInWhat); } // Stores atom id to primary key pairs for each state atom that the metric is @@ -663,8 +700,7 @@ void DurationMetricProducer::onMatchedLogEventLocked(const size_t matcherIndex, // field values from the log event. These values will form a primary key // that will be used to query StateTracker for the correct state value. for (const auto& stateLink : mMetric2StateLinks) { - getDimensionForState(event.getValues(), stateLink, - &statePrimaryKeys[stateLink.stateAtomId]); + getDimensionForState(values, stateLink, &statePrimaryKeys[stateLink.stateAtomId]); } // For each sliced state, query StateTracker for the state value using @@ -695,19 +731,19 @@ void DurationMetricProducer::onMatchedLogEventLocked(const size_t matcherIndex, if (mUseWhatDimensionAsInternalDimension) { auto whatIt = mCurrentSlicedDurationTrackerMap.find(dimensionInWhat); if (whatIt != mCurrentSlicedDurationTrackerMap.end()) { - whatIt->second->noteStop(dimensionInWhat, event.GetElapsedTimestampNs(), false); + whatIt->second->noteStop(dimensionInWhat, eventTimeNs, false); } return; } HashableDimensionKey internalDimensionKey = DEFAULT_DIMENSION_KEY; if (!mInternalDimensions.empty()) { - filterValues(mInternalDimensions, event.getValues(), &internalDimensionKey); + filterValues(mInternalDimensions, values, &internalDimensionKey); } auto whatIt = mCurrentSlicedDurationTrackerMap.find(dimensionInWhat); if (whatIt != mCurrentSlicedDurationTrackerMap.end()) { - whatIt->second->noteStop(internalDimensionKey, event.GetElapsedTimestampNs(), false); + whatIt->second->noteStop(internalDimensionKey, eventTimeNs, false); } return; } @@ -716,7 +752,7 @@ void DurationMetricProducer::onMatchedLogEventLocked(const size_t matcherIndex, ConditionKey conditionKey; if (mConditionSliced) { for (const auto& link : mMetric2ConditionLinks) { - getDimensionForCondition(event.getValues(), link, &conditionKey[link.conditionId]); + getDimensionForCondition(values, link, &conditionKey[link.conditionId]); } auto conditionState = @@ -731,7 +767,7 @@ void DurationMetricProducer::onMatchedLogEventLocked(const size_t matcherIndex, condition = condition && mIsActive; handleStartEvent(MetricDimensionKey(dimensionInWhat, stateValuesKey), conditionKey, condition, - event); + eventTimeNs, values); } size_t DurationMetricProducer::byteSizeLocked() const { diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.h b/cmds/statsd/src/metrics/DurationMetricProducer.h index 5feb09fc1c98..9fb586f7262f 100644 --- a/cmds/statsd/src/metrics/DurationMetricProducer.h +++ b/cmds/statsd/src/metrics/DurationMetricProducer.h @@ -40,8 +40,8 @@ class DurationMetricProducer : public MetricProducer { public: DurationMetricProducer( const ConfigKey& key, const DurationMetric& durationMetric, const int conditionIndex, - const vector<ConditionState>& initialConditionCache, const int startIndex, - const int stopIndex, const int stopAllIndex, const bool nesting, + const vector<ConditionState>& initialConditionCache, const int whatIndex, + const int startIndex, const int stopIndex, const int stopAllIndex, const bool nesting, const sp<ConditionWizard>& wizard, const uint64_t protoHash, const FieldMatcher& internalDimensions, const int64_t timeBaseNs, const int64_t startTimeNs, @@ -74,8 +74,15 @@ protected: const std::map<int, HashableDimensionKey>& statePrimaryKeys) override; private: + // Initializes true dimensions of the 'what' predicate. Only to be called during initialization. + void initTrueDimensions(const int whatIndex, const int64_t startTimeNs); + + void handleMatchedLogEventValuesLocked(const size_t matcherIndex, + const std::vector<FieldValue>& values, + const int64_t eventTimeNs); void handleStartEvent(const MetricDimensionKey& eventKey, const ConditionKey& conditionKeys, - bool condition, const LogEvent& event); + bool condition, const int64_t eventTimeNs, + const vector<FieldValue>& eventValues); void onDumpReportLocked(const int64_t dumpTimeNs, const bool include_current_partial_bucket, diff --git a/cmds/statsd/src/metrics/MetricProducer.cpp b/cmds/statsd/src/metrics/MetricProducer.cpp index 5b321a0e3d60..c68e61e78f2e 100644 --- a/cmds/statsd/src/metrics/MetricProducer.cpp +++ b/cmds/statsd/src/metrics/MetricProducer.cpp @@ -56,6 +56,7 @@ MetricProducer::MetricProducer( : mMetricId(metricId), mProtoHash(protoHash), mConfigKey(key), + mValid(true), mTimeBaseNs(timeBaseNs), mCurrentBucketStartTimeNs(timeBaseNs), mCurrentBucketNum(0), diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h index 0dc8edae8056..7a1c008bd134 100644 --- a/cmds/statsd/src/metrics/MetricProducer.h +++ b/cmds/statsd/src/metrics/MetricProducer.h @@ -336,6 +336,10 @@ public: return mSlicedStateAtoms; } + inline bool isValid() const { + return mValid; + } + /* Adds an AnomalyTracker and returns it. */ virtual sp<AnomalyTracker> addAnomalyTracker(const Alert &alert, const sp<AlarmMonitor>& anomalyAlarmMonitor) { @@ -468,6 +472,8 @@ protected: const ConfigKey mConfigKey; + bool mValid; + // The time when this metric producer was first created. The end time for the current bucket // can be computed from this based on mCurrentBucketNum. int64_t mTimeBaseNs; diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h index 3c9ecdb46fd2..0e563710d56f 100644 --- a/cmds/statsd/src/metrics/MetricsManager.h +++ b/cmds/statsd/src/metrics/MetricsManager.h @@ -334,7 +334,7 @@ private: FRIEND_TEST(AlarmE2eTest, TestMultipleAlarms); FRIEND_TEST(ConfigTtlE2eTest, TestCountMetric); - FRIEND_TEST(ConfigUpdateE2eTest, TestConfigTtl); + FRIEND_TEST(ConfigUpdateE2eAbTest, TestConfigTtl); FRIEND_TEST(MetricActivationE2eTest, TestCountMetric); FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithOneDeactivation); FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithTwoDeactivations); diff --git a/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.cpp b/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.cpp index 4474df4346cf..bfa409cb8a64 100644 --- a/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.cpp +++ b/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.cpp @@ -450,7 +450,8 @@ optional<sp<MetricProducer>> createDurationMetricProducerAndUpdateMetadata( return nullopt; } - const Predicate& durationWhat = config.predicate(what_it->second); + const int whatIndex = what_it->second; + const Predicate& durationWhat = config.predicate(whatIndex); if (durationWhat.contents_case() != Predicate::ContentsCase::kSimplePredicate) { ALOGE("DurationMetric's \"what\" must be a simple condition"); return nullopt; @@ -518,6 +519,7 @@ optional<sp<MetricProducer>> createDurationMetricProducerAndUpdateMetadata( translateFieldMatcher(metric.dimensions_in_what(), &dimensionsInWhat); for (const auto& stateLink : metric.state_link()) { if (!handleMetricWithStateLink(stateLink.fields_in_what(), dimensionsInWhat)) { + ALOGW("DurationMetric's MetricStateLinks must be a subset of dimensions in what"); return nullopt; } } @@ -536,10 +538,15 @@ optional<sp<MetricProducer>> createDurationMetricProducerAndUpdateMetadata( return nullopt; } - return {new DurationMetricProducer( - key, metric, conditionIndex, initialConditionCache, startIndex, stopIndex, stopAllIndex, - nesting, wizard, metricHash, internalDimensions, timeBaseNs, currentTimeNs, - eventActivationMap, eventDeactivationMap, slicedStateAtoms, stateGroupMap)}; + sp<MetricProducer> producer = new DurationMetricProducer( + key, metric, conditionIndex, initialConditionCache, whatIndex, startIndex, stopIndex, + stopAllIndex, nesting, wizard, metricHash, internalDimensions, timeBaseNs, + currentTimeNs, eventActivationMap, eventDeactivationMap, slicedStateAtoms, + stateGroupMap); + if (!producer->isValid()) { + return nullopt; + } + return {producer}; } optional<sp<MetricProducer>> createEventMetricProducerAndUpdateMetadata( @@ -677,6 +684,7 @@ optional<sp<MetricProducer>> createValueMetricProducerAndUpdateMetadata( translateFieldMatcher(metric.dimensions_in_what(), &dimensionsInWhat); for (const auto& stateLink : metric.state_link()) { if (!handleMetricWithStateLink(stateLink.fields_in_what(), dimensionsInWhat)) { + ALOGW("ValueMetric's MetricStateLinks must be a subset of the dimensions in what"); return nullopt; } } diff --git a/cmds/statsd/tests/ConfigManager_test.cpp b/cmds/statsd/tests/ConfigManager_test.cpp index 1d8371638e90..24c8f2b1b2d8 100644 --- a/cmds/statsd/tests/ConfigManager_test.cpp +++ b/cmds/statsd/tests/ConfigManager_test.cpp @@ -44,8 +44,8 @@ static ostream& operator<<(ostream& os, const StatsdConfig& config) { */ class MockListener : public ConfigListener { public: - MOCK_METHOD4(OnConfigUpdated, void(const int64_t timestampNs, const ConfigKey& key, - const StatsdConfig& config, bool modularUpdate)); + MOCK_METHOD3(OnConfigUpdated, + void(const int64_t timestampNs, const ConfigKey& key, const StatsdConfig& config)); MOCK_METHOD1(OnConfigRemoved, void(const ConfigKey& key)); }; @@ -90,25 +90,25 @@ TEST(ConfigManagerTest, TestAddUpdateRemove) { // Add another one EXPECT_CALL(*(listener.get()), - OnConfigUpdated(_, ConfigKeyEq(1, StringToId("zzz")), StatsdConfigEq(91), _)) + OnConfigUpdated(_, ConfigKeyEq(1, StringToId("zzz")), StatsdConfigEq(91))) .RetiresOnSaturation(); manager->UpdateConfig(ConfigKey(1, StringToId("zzz")), config91); // Update It EXPECT_CALL(*(listener.get()), - OnConfigUpdated(_, ConfigKeyEq(1, StringToId("zzz")), StatsdConfigEq(92), _)) + OnConfigUpdated(_, ConfigKeyEq(1, StringToId("zzz")), StatsdConfigEq(92))) .RetiresOnSaturation(); manager->UpdateConfig(ConfigKey(1, StringToId("zzz")), config92); // Add one with the same uid but a different name EXPECT_CALL(*(listener.get()), - OnConfigUpdated(_, ConfigKeyEq(1, StringToId("yyy")), StatsdConfigEq(93), _)) + OnConfigUpdated(_, ConfigKeyEq(1, StringToId("yyy")), StatsdConfigEq(93))) .RetiresOnSaturation(); manager->UpdateConfig(ConfigKey(1, StringToId("yyy")), config93); // Add one with the same name but a different uid EXPECT_CALL(*(listener.get()), - OnConfigUpdated(_, ConfigKeyEq(2, StringToId("zzz")), StatsdConfigEq(94), _)) + OnConfigUpdated(_, ConfigKeyEq(2, StringToId("zzz")), StatsdConfigEq(94))) .RetiresOnSaturation(); manager->UpdateConfig(ConfigKey(2, StringToId("zzz")), config94); @@ -143,7 +143,7 @@ TEST(ConfigManagerTest, TestRemoveUid) { StatsdConfig config; - EXPECT_CALL(*(listener.get()), OnConfigUpdated(_, _, _, _)).Times(5); + EXPECT_CALL(*(listener.get()), OnConfigUpdated(_, _, _)).Times(5); EXPECT_CALL(*(listener.get()), OnConfigRemoved(ConfigKeyEq(2, StringToId("xxx")))); EXPECT_CALL(*(listener.get()), OnConfigRemoved(ConfigKeyEq(2, StringToId("yyy")))); EXPECT_CALL(*(listener.get()), OnConfigRemoved(ConfigKeyEq(2, StringToId("zzz")))); diff --git a/cmds/statsd/tests/e2e/ConfigUpdate_e2e_ab_test.cpp b/cmds/statsd/tests/e2e/ConfigUpdate_e2e_ab_test.cpp new file mode 100644 index 000000000000..098f284fdc64 --- /dev/null +++ b/cmds/statsd/tests/e2e/ConfigUpdate_e2e_ab_test.cpp @@ -0,0 +1,328 @@ +// 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. + +#include <android-base/properties.h> +#include <android-base/stringprintf.h> +#include <gtest/gtest.h> + +#include "flags/flags.h" +#include "src/StatsLogProcessor.h" +#include "src/storage/StorageManager.h" +#include "tests/statsd_test_util.h" + +namespace android { +namespace os { +namespace statsd { + +#ifdef __ANDROID__ +#define STATS_DATA_DIR "/data/misc/stats-data" + +using android::base::SetProperty; +using android::base::StringPrintf; +using namespace std; + +namespace { + +StatsdConfig CreateSimpleConfig() { + StatsdConfig config; + config.add_allowed_log_source("AID_STATSD"); + config.set_hash_strings_in_metric_report(false); + + *config.add_atom_matcher() = CreateBatteryStateUsbMatcher(); + // Simple count metric so the config isn't empty. + CountMetric* countMetric1 = config.add_count_metric(); + countMetric1->set_id(StringToId("Count1")); + countMetric1->set_what(config.atom_matcher(0).id()); + countMetric1->set_bucket(FIVE_MINUTES); + return config; +} +} // namespace + +// Setup for parameterized tests. +class ConfigUpdateE2eAbTest : public TestWithParam<bool> { +private: + string originalFlagValue; + +public: + void SetUp() override { + originalFlagValue = getFlagBool(PARTIAL_CONFIG_UPDATE_FLAG, ""); + string rawFlagName = + StringPrintf("persist.device_config.%s.%s", STATSD_NATIVE_NAMESPACE.c_str(), + PARTIAL_CONFIG_UPDATE_FLAG.c_str()); + SetProperty(rawFlagName, GetParam() ? "true" : "false"); + } + + void TearDown() override { + string rawFlagName = + StringPrintf("persist.device_config.%s.%s", STATSD_NATIVE_NAMESPACE.c_str(), + PARTIAL_CONFIG_UPDATE_FLAG.c_str()); + SetProperty(rawFlagName, originalFlagValue); + } +}; + +INSTANTIATE_TEST_SUITE_P(ConfigUpdateE2eAbTest, ConfigUpdateE2eAbTest, testing::Bool()); + +TEST_P(ConfigUpdateE2eAbTest, TestUidMapVersionStringInstaller) { + sp<UidMap> uidMap = new UidMap(); + vector<int32_t> uids({1000}); + vector<int64_t> versions({1}); + vector<String16> apps({String16("app1")}); + vector<String16> versionStrings({String16("v1")}); + vector<String16> installers({String16("installer1")}); + uidMap->updateMap(1, uids, versions, versionStrings, apps, installers); + + StatsdConfig config = CreateSimpleConfig(); + config.set_version_strings_in_metric_report(true); + config.set_installer_in_metric_report(false); + int64_t baseTimeNs = getElapsedRealtimeNs(); + + ConfigKey cfgKey(0, 12345); + sp<StatsLogProcessor> processor = + CreateStatsLogProcessor(baseTimeNs, baseTimeNs, config, cfgKey, nullptr, 0, uidMap); + EXPECT_EQ(processor->mMetricsManagers.size(), 1u); + sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second; + EXPECT_TRUE(metricsManager->isConfigValid()); + + // Now update. + config.set_version_strings_in_metric_report(false); + config.set_installer_in_metric_report(true); + processor->OnConfigUpdated(baseTimeNs + 1000, cfgKey, config); + EXPECT_EQ(processor->mMetricsManagers.size(), 1u); + EXPECT_EQ(metricsManager == processor->mMetricsManagers.begin()->second, GetParam()); + EXPECT_TRUE(metricsManager->isConfigValid()); + + ConfigMetricsReportList reports; + vector<uint8_t> buffer; + processor->onDumpReport(cfgKey, baseTimeNs + 1001, false, true, ADB_DUMP, FAST, &buffer); + EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); + // First report is written to disk when the update happens. + ASSERT_EQ(reports.reports_size(), 2); + UidMapping uidMapping = reports.reports(1).uid_map(); + ASSERT_EQ(uidMapping.snapshots_size(), 1); + ASSERT_EQ(uidMapping.snapshots(0).package_info_size(), 1); + EXPECT_FALSE(uidMapping.snapshots(0).package_info(0).has_version_string()); + EXPECT_EQ(uidMapping.snapshots(0).package_info(0).installer(), "installer1"); +} + +TEST_P(ConfigUpdateE2eAbTest, TestHashStrings) { + sp<UidMap> uidMap = new UidMap(); + vector<int32_t> uids({1000}); + vector<int64_t> versions({1}); + vector<String16> apps({String16("app1")}); + vector<String16> versionStrings({String16("v1")}); + vector<String16> installers({String16("installer1")}); + uidMap->updateMap(1, uids, versions, versionStrings, apps, installers); + + StatsdConfig config = CreateSimpleConfig(); + config.set_version_strings_in_metric_report(true); + config.set_hash_strings_in_metric_report(true); + int64_t baseTimeNs = getElapsedRealtimeNs(); + + ConfigKey cfgKey(0, 12345); + sp<StatsLogProcessor> processor = + CreateStatsLogProcessor(baseTimeNs, baseTimeNs, config, cfgKey, nullptr, 0, uidMap); + EXPECT_EQ(processor->mMetricsManagers.size(), 1u); + sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second; + EXPECT_TRUE(metricsManager->isConfigValid()); + + // Now update. + config.set_hash_strings_in_metric_report(false); + processor->OnConfigUpdated(baseTimeNs + 1000, cfgKey, config); + EXPECT_EQ(processor->mMetricsManagers.size(), 1u); + EXPECT_EQ(metricsManager == processor->mMetricsManagers.begin()->second, GetParam()); + EXPECT_TRUE(metricsManager->isConfigValid()); + + ConfigMetricsReportList reports; + vector<uint8_t> buffer; + processor->onDumpReport(cfgKey, baseTimeNs + 1001, false, true, ADB_DUMP, FAST, &buffer); + EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); + // First report is written to disk when the update happens. + ASSERT_EQ(reports.reports_size(), 2); + UidMapping uidMapping = reports.reports(1).uid_map(); + ASSERT_EQ(uidMapping.snapshots_size(), 1); + ASSERT_EQ(uidMapping.snapshots(0).package_info_size(), 1); + EXPECT_TRUE(uidMapping.snapshots(0).package_info(0).has_version_string()); + EXPECT_FALSE(uidMapping.snapshots(0).package_info(0).has_version_string_hash()); +} + +TEST_P(ConfigUpdateE2eAbTest, TestAnnotations) { + StatsdConfig config = CreateSimpleConfig(); + StatsdConfig_Annotation* annotation = config.add_annotation(); + annotation->set_field_int64(11); + annotation->set_field_int32(1); + int64_t baseTimeNs = getElapsedRealtimeNs(); + ConfigKey cfgKey(0, 12345); + sp<StatsLogProcessor> processor = + CreateStatsLogProcessor(baseTimeNs, baseTimeNs, config, cfgKey); + + // Now update + config.clear_annotation(); + annotation = config.add_annotation(); + annotation->set_field_int64(22); + annotation->set_field_int32(2); + processor->OnConfigUpdated(baseTimeNs + 1000, cfgKey, config); + + ConfigMetricsReportList reports; + vector<uint8_t> buffer; + processor->onDumpReport(cfgKey, baseTimeNs + 1001, false, true, ADB_DUMP, FAST, &buffer); + EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); + // First report is written to disk when the update happens. + ASSERT_EQ(reports.reports_size(), 2); + ConfigMetricsReport report = reports.reports(1); + EXPECT_EQ(report.annotation_size(), 1); + EXPECT_EQ(report.annotation(0).field_int64(), 22); + EXPECT_EQ(report.annotation(0).field_int32(), 2); +} + +TEST_P(ConfigUpdateE2eAbTest, TestPersistLocally) { + StatsdConfig config = CreateSimpleConfig(); + config.set_persist_locally(false); + int64_t baseTimeNs = getElapsedRealtimeNs(); + ConfigKey cfgKey(0, 12345); + sp<StatsLogProcessor> processor = + CreateStatsLogProcessor(baseTimeNs, baseTimeNs, config, cfgKey); + ConfigMetricsReportList reports; + vector<uint8_t> buffer; + processor->onDumpReport(cfgKey, baseTimeNs + 1001, false, true, ADB_DUMP, FAST, &buffer); + EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); + ASSERT_EQ(reports.reports_size(), 1); + // Number of reports should still be 1 since persist_locally is false. + reports.Clear(); + buffer.clear(); + processor->onDumpReport(cfgKey, baseTimeNs + 1001, false, true, ADB_DUMP, FAST, &buffer); + EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); + ASSERT_EQ(reports.reports_size(), 1); + + // Now update. + config.set_persist_locally(true); + processor->OnConfigUpdated(baseTimeNs + 1000, cfgKey, config); + + // Should get 2: 1 in memory + 1 on disk. Both should be saved on disk. + reports.Clear(); + buffer.clear(); + processor->onDumpReport(cfgKey, baseTimeNs + 1001, false, true, ADB_DUMP, FAST, &buffer); + EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); + ASSERT_EQ(reports.reports_size(), 2); + // Should get 3, 2 on disk + 1 in memory. + reports.Clear(); + buffer.clear(); + processor->onDumpReport(cfgKey, baseTimeNs + 1001, false, true, ADB_DUMP, FAST, &buffer); + EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); + ASSERT_EQ(reports.reports_size(), 3); + string suffix = StringPrintf("%d_%lld", cfgKey.GetUid(), (long long)cfgKey.GetId()); + StorageManager::deleteSuffixedFiles(STATS_DATA_DIR, suffix.c_str()); + string historySuffix = + StringPrintf("%d_%lld_history", cfgKey.GetUid(), (long long)cfgKey.GetId()); + StorageManager::deleteSuffixedFiles(STATS_DATA_DIR, historySuffix.c_str()); +} + +TEST_P(ConfigUpdateE2eAbTest, TestNoReportMetrics) { + StatsdConfig config = CreateSimpleConfig(); + // Second simple count metric. + CountMetric* countMetric = config.add_count_metric(); + countMetric->set_id(StringToId("Count2")); + countMetric->set_what(config.atom_matcher(0).id()); + countMetric->set_bucket(FIVE_MINUTES); + config.add_no_report_metric(config.count_metric(0).id()); + int64_t baseTimeNs = getElapsedRealtimeNs(); + ConfigKey cfgKey(0, 12345); + sp<StatsLogProcessor> processor = + CreateStatsLogProcessor(baseTimeNs, baseTimeNs, config, cfgKey); + + // Now update. + config.clear_no_report_metric(); + config.add_no_report_metric(config.count_metric(1).id()); + processor->OnConfigUpdated(baseTimeNs + 1000, cfgKey, config); + + ConfigMetricsReportList reports; + vector<uint8_t> buffer; + processor->onDumpReport(cfgKey, baseTimeNs + 1001, false, true, ADB_DUMP, FAST, &buffer); + EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); + // First report is written to disk when the update happens. + ASSERT_EQ(reports.reports_size(), 2); + // First report (before update) has the first count metric. + ASSERT_EQ(reports.reports(0).metrics_size(), 1); + EXPECT_EQ(reports.reports(0).metrics(0).metric_id(), config.count_metric(1).id()); + // Second report (after update) has the first count metric. + ASSERT_EQ(reports.reports(1).metrics_size(), 1); + EXPECT_EQ(reports.reports(1).metrics(0).metric_id(), config.count_metric(0).id()); +} + +TEST_P(ConfigUpdateE2eAbTest, TestAtomsAllowedFromAnyUid) { + StatsdConfig config = CreateSimpleConfig(); + int64_t baseTimeNs = getElapsedRealtimeNs(); + ConfigKey cfgKey(0, 12345); + sp<StatsLogProcessor> processor = + CreateStatsLogProcessor(baseTimeNs, baseTimeNs, config, cfgKey); + // Uses AID_ROOT, which isn't in allowed log sources. + unique_ptr<LogEvent> event = CreateBatteryStateChangedEvent( + baseTimeNs + 2, BatteryPluggedStateEnum::BATTERY_PLUGGED_USB); + processor->OnLogEvent(event.get()); + ConfigMetricsReportList reports; + vector<uint8_t> buffer; + processor->onDumpReport(cfgKey, baseTimeNs + 1001, true, true, ADB_DUMP, FAST, &buffer); + EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); + ASSERT_EQ(reports.reports_size(), 1); + // Check the metric and make sure it has 0 count. + ASSERT_EQ(reports.reports(0).metrics_size(), 1); + EXPECT_FALSE(reports.reports(0).metrics(0).has_count_metrics()); + + // Now update. Allow plugged state to be logged from any uid, so the atom will be counted. + config.add_whitelisted_atom_ids(util::PLUGGED_STATE_CHANGED); + processor->OnConfigUpdated(baseTimeNs + 1000, cfgKey, config); + unique_ptr<LogEvent> event2 = CreateBatteryStateChangedEvent( + baseTimeNs + 2000, BatteryPluggedStateEnum::BATTERY_PLUGGED_USB); + processor->OnLogEvent(event.get()); + reports.Clear(); + buffer.clear(); + processor->onDumpReport(cfgKey, baseTimeNs + 3000, true, true, ADB_DUMP, FAST, &buffer); + EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); + ASSERT_EQ(reports.reports_size(), 2); + // Check the metric and make sure it has 0 count. + ASSERT_EQ(reports.reports(1).metrics_size(), 1); + EXPECT_TRUE(reports.reports(1).metrics(0).has_count_metrics()); + ASSERT_EQ(reports.reports(1).metrics(0).count_metrics().data_size(), 1); + ASSERT_EQ(reports.reports(1).metrics(0).count_metrics().data(0).bucket_info_size(), 1); + EXPECT_EQ(reports.reports(1).metrics(0).count_metrics().data(0).bucket_info(0).count(), 1); +} + +TEST_P(ConfigUpdateE2eAbTest, TestConfigTtl) { + StatsdConfig config = CreateSimpleConfig(); + config.set_ttl_in_seconds(1); + int64_t baseTimeNs = getElapsedRealtimeNs(); + ConfigKey cfgKey(0, 12345); + sp<StatsLogProcessor> processor = + CreateStatsLogProcessor(baseTimeNs, baseTimeNs, config, cfgKey); + EXPECT_EQ(processor->mMetricsManagers.size(), 1u); + sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second; + EXPECT_EQ(metricsManager->getTtlEndNs(), baseTimeNs + NS_PER_SEC); + + config.set_ttl_in_seconds(5); + processor->OnConfigUpdated(baseTimeNs + 2 * NS_PER_SEC, cfgKey, config); + metricsManager = processor->mMetricsManagers.begin()->second; + EXPECT_EQ(metricsManager->getTtlEndNs(), baseTimeNs + 7 * NS_PER_SEC); + + // Clear the data stored on disk as a result of the update. + vector<uint8_t> buffer; + processor->onDumpReport(cfgKey, baseTimeNs + 3 * NS_PER_SEC, false, true, ADB_DUMP, FAST, + &buffer); +} + +#else +GTEST_LOG_(INFO) << "This test does nothing.\n"; +#endif + +} // namespace statsd +} // namespace os +} // namespace android diff --git a/cmds/statsd/tests/e2e/ConfigUpdate_e2e_test.cpp b/cmds/statsd/tests/e2e/ConfigUpdate_e2e_test.cpp index e01a0b63a0ca..94b778c0e0ea 100644 --- a/cmds/statsd/tests/e2e/ConfigUpdate_e2e_test.cpp +++ b/cmds/statsd/tests/e2e/ConfigUpdate_e2e_test.cpp @@ -12,11 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. +#include <android-base/properties.h> +#include <android-base/stringprintf.h> #include <gtest/gtest.h> -#include <thread> // std::this_thread::sleep_for - -#include "android-base/stringprintf.h" +#include "flags/flags.h" #include "src/StatsLogProcessor.h" #include "src/storage/StorageManager.h" #include "tests/statsd_test_util.h" @@ -26,276 +26,327 @@ namespace os { namespace statsd { #ifdef __ANDROID__ -#define STATS_DATA_DIR "/data/misc/stats-data" + +using android::base::SetProperty; using android::base::StringPrintf; +using namespace std; +// Tests that only run with the partial config update feature turned on. namespace { - -StatsdConfig CreateSimpleConfig() { +// Setup for test fixture. +class ConfigUpdateE2eTest : public ::testing::Test { +private: + string originalFlagValue; +public: + void SetUp() override { + originalFlagValue = getFlagBool(PARTIAL_CONFIG_UPDATE_FLAG, ""); + string rawFlagName = + StringPrintf("persist.device_config.%s.%s", STATSD_NATIVE_NAMESPACE.c_str(), + PARTIAL_CONFIG_UPDATE_FLAG.c_str()); + SetProperty(rawFlagName, "true"); + } + + void TearDown() override { + string rawFlagName = + StringPrintf("persist.device_config.%s.%s", STATSD_NATIVE_NAMESPACE.c_str(), + PARTIAL_CONFIG_UPDATE_FLAG.c_str()); + SetProperty(rawFlagName, originalFlagValue); + } +}; +} // Anonymous namespace. + +TEST_F(ConfigUpdateE2eTest, TestNewDurationExistingWhat) { StatsdConfig config; - config.add_allowed_log_source("AID_STATSD"); - config.set_hash_strings_in_metric_report(false); - - *config.add_atom_matcher() = CreateBatteryStateUsbMatcher(); - // Simple count metric so the config isn't empty. - CountMetric* countMetric1 = config.add_count_metric(); - countMetric1->set_id(StringToId("Count1")); - countMetric1->set_what(config.atom_matcher(0).id()); - countMetric1->set_bucket(FIVE_MINUTES); - return config; -} -} // namespace - -// Setup for parameterized tests. -class ConfigUpdateE2eTest : public TestWithParam<bool> {}; - -INSTANTIATE_TEST_SUITE_P(ConfigUpdateE2eTest, ConfigUpdateE2eTest, testing::Bool()); - -TEST_P(ConfigUpdateE2eTest, TestUidMapVersionStringInstaller) { - sp<UidMap> uidMap = new UidMap(); - vector<int32_t> uids({1000}); - vector<int64_t> versions({1}); - vector<String16> apps({String16("app1")}); - vector<String16> versionStrings({String16("v1")}); - vector<String16> installers({String16("installer1")}); - uidMap->updateMap(1, uids, versions, versionStrings, apps, installers); + config.add_allowed_log_source("AID_ROOT"); + *config.add_atom_matcher() = CreateAcquireWakelockAtomMatcher(); + *config.add_atom_matcher() = CreateReleaseWakelockAtomMatcher(); - StatsdConfig config = CreateSimpleConfig(); - config.set_version_strings_in_metric_report(true); - config.set_installer_in_metric_report(false); - int64_t baseTimeNs = getElapsedRealtimeNs(); + Predicate holdingWakelockPredicate = CreateHoldingWakelockPredicate(); + *config.add_predicate() = holdingWakelockPredicate; - ConfigKey cfgKey(0, 12345); + ConfigKey key(123, 987); + uint64_t bucketStartTimeNs = 10000000000; // 0:10 + uint64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(FIVE_MINUTES) * 1000000LL; sp<StatsLogProcessor> processor = - CreateStatsLogProcessor(baseTimeNs, baseTimeNs, config, cfgKey, nullptr, 0, uidMap); - EXPECT_EQ(processor->mMetricsManagers.size(), 1u); - sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second; - EXPECT_TRUE(metricsManager->isConfigValid()); - - // Now update. - config.set_version_strings_in_metric_report(false); - config.set_installer_in_metric_report(true); - processor->OnConfigUpdated(baseTimeNs + 1000, cfgKey, config, /*modularUpdate=*/GetParam()); - EXPECT_EQ(processor->mMetricsManagers.size(), 1u); - EXPECT_EQ(metricsManager == processor->mMetricsManagers.begin()->second, GetParam()); - EXPECT_TRUE(metricsManager->isConfigValid()); + CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, key); + + int app1Uid = 123; + vector<int> attributionUids1 = {app1Uid}; + vector<string> attributionTags1 = {"App1"}; + // Create a wakelock acquire, causing the condition to be true. + unique_ptr<LogEvent> event = CreateAcquireWakelockEvent(bucketStartTimeNs + 10 * NS_PER_SEC, + attributionUids1, attributionTags1, + "wl1"); // 0:10 + processor->OnLogEvent(event.get()); - ConfigMetricsReportList reports; - vector<uint8_t> buffer; - processor->onDumpReport(cfgKey, baseTimeNs + 1001, false, true, ADB_DUMP, FAST, &buffer); - EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); - // First report is written to disk when the update happens. - ASSERT_EQ(reports.reports_size(), 2); - UidMapping uidMapping = reports.reports(1).uid_map(); - ASSERT_EQ(uidMapping.snapshots_size(), 1); - ASSERT_EQ(uidMapping.snapshots(0).package_info_size(), 1); - EXPECT_FALSE(uidMapping.snapshots(0).package_info(0).has_version_string()); - EXPECT_EQ(uidMapping.snapshots(0).package_info(0).installer(), "installer1"); -} + // Add metric. + DurationMetric* durationMetric = config.add_duration_metric(); + durationMetric->set_id(StringToId("WakelockDuration")); + durationMetric->set_what(holdingWakelockPredicate.id()); + durationMetric->set_aggregation_type(DurationMetric::SUM); + durationMetric->set_bucket(FIVE_MINUTES); -TEST_P(ConfigUpdateE2eTest, TestHashStrings) { - sp<UidMap> uidMap = new UidMap(); - vector<int32_t> uids({1000}); - vector<int64_t> versions({1}); - vector<String16> apps({String16("app1")}); - vector<String16> versionStrings({String16("v1")}); - vector<String16> installers({String16("installer1")}); - uidMap->updateMap(1, uids, versions, versionStrings, apps, installers); - - StatsdConfig config = CreateSimpleConfig(); - config.set_version_strings_in_metric_report(true); - config.set_hash_strings_in_metric_report(true); - int64_t baseTimeNs = getElapsedRealtimeNs(); - - ConfigKey cfgKey(0, 12345); - sp<StatsLogProcessor> processor = - CreateStatsLogProcessor(baseTimeNs, baseTimeNs, config, cfgKey, nullptr, 0, uidMap); - EXPECT_EQ(processor->mMetricsManagers.size(), 1u); - sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second; - EXPECT_TRUE(metricsManager->isConfigValid()); - - // Now update. - config.set_hash_strings_in_metric_report(false); - processor->OnConfigUpdated(baseTimeNs + 1000, cfgKey, config, /*modularUpdate=*/GetParam()); - EXPECT_EQ(processor->mMetricsManagers.size(), 1u); - EXPECT_EQ(metricsManager == processor->mMetricsManagers.begin()->second, GetParam()); - EXPECT_TRUE(metricsManager->isConfigValid()); + uint64_t updateTimeNs = bucketStartTimeNs + 60 * NS_PER_SEC; // 1:00 + processor->OnConfigUpdated(updateTimeNs, key, config); + event = CreateReleaseWakelockEvent(bucketStartTimeNs + 80 * NS_PER_SEC, attributionUids1, + attributionTags1, + "wl1"); // 1:20 + processor->OnLogEvent(event.get()); + uint64_t dumpTimeNs = bucketStartTimeNs + 90 * NS_PER_SEC; // 1:30 ConfigMetricsReportList reports; vector<uint8_t> buffer; - processor->onDumpReport(cfgKey, baseTimeNs + 1001, false, true, ADB_DUMP, FAST, &buffer); + processor->onDumpReport(key, dumpTimeNs, true, true, ADB_DUMP, FAST, &buffer); EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); - // First report is written to disk when the update happens. - ASSERT_EQ(reports.reports_size(), 2); - UidMapping uidMapping = reports.reports(1).uid_map(); - ASSERT_EQ(uidMapping.snapshots_size(), 1); - ASSERT_EQ(uidMapping.snapshots(0).package_info_size(), 1); - EXPECT_TRUE(uidMapping.snapshots(0).package_info(0).has_version_string()); - EXPECT_FALSE(uidMapping.snapshots(0).package_info(0).has_version_string_hash()); + backfillDimensionPath(&reports); + backfillStringInReport(&reports); + backfillStartEndTimestamp(&reports); + ASSERT_EQ(reports.reports_size(), 1); + ASSERT_EQ(reports.reports(0).metrics_size(), 1); + EXPECT_TRUE(reports.reports(0).metrics(0).has_duration_metrics()); + + StatsLogReport::DurationMetricDataWrapper metricData; + sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).duration_metrics(), &metricData); + ASSERT_EQ(metricData.data_size(), 1); + DurationMetricData data = metricData.data(0); + ASSERT_EQ(data.bucket_info_size(), 1); + + DurationBucketInfo bucketInfo = data.bucket_info(0); + EXPECT_EQ(bucketInfo.start_bucket_elapsed_nanos(), updateTimeNs); + EXPECT_EQ(bucketInfo.end_bucket_elapsed_nanos(), dumpTimeNs); + EXPECT_EQ(bucketInfo.duration_nanos(), 20 * NS_PER_SEC); } -TEST_P(ConfigUpdateE2eTest, TestAnnotations) { - StatsdConfig config = CreateSimpleConfig(); - StatsdConfig_Annotation* annotation = config.add_annotation(); - annotation->set_field_int64(11); - annotation->set_field_int32(1); - int64_t baseTimeNs = getElapsedRealtimeNs(); - ConfigKey cfgKey(0, 12345); +TEST_F(ConfigUpdateE2eTest, TestNewDurationExistingWhatSlicedCondition) { + StatsdConfig config; + config.add_allowed_log_source("AID_ROOT"); + *config.add_atom_matcher() = CreateAcquireWakelockAtomMatcher(); + *config.add_atom_matcher() = CreateReleaseWakelockAtomMatcher(); + *config.add_atom_matcher() = CreateMoveToBackgroundAtomMatcher(); + *config.add_atom_matcher() = CreateMoveToForegroundAtomMatcher(); + + Predicate holdingWakelockPredicate = CreateHoldingWakelockPredicate(); + // The predicate is dimensioning by first attribution node by uid. + *holdingWakelockPredicate.mutable_simple_predicate()->mutable_dimensions() = + CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST}); + *config.add_predicate() = holdingWakelockPredicate; + + Predicate isInBackgroundPredicate = CreateIsInBackgroundPredicate(); + *isInBackgroundPredicate.mutable_simple_predicate()->mutable_dimensions() = + CreateDimensions(util::ACTIVITY_FOREGROUND_STATE_CHANGED, {1 /*uid*/}); + *config.add_predicate() = isInBackgroundPredicate; + + ConfigKey key(123, 987); + uint64_t bucketStartTimeNs = 10000000000; // 0:10 + uint64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(FIVE_MINUTES) * 1000000LL; sp<StatsLogProcessor> processor = - CreateStatsLogProcessor(baseTimeNs, baseTimeNs, config, cfgKey); + CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, key); + + int app1Uid = 123, app2Uid = 456; + vector<int> attributionUids1 = {app1Uid}; + vector<string> attributionTags1 = {"App1"}; + vector<int> attributionUids2 = {app2Uid}; + vector<string> attributionTags2 = {"App2"}; + unique_ptr<LogEvent> event = CreateAcquireWakelockEvent(bucketStartTimeNs + 10 * NS_PER_SEC, + attributionUids1, attributionTags1, + "wl1"); // 0:10 + processor->OnLogEvent(event.get()); + event = CreateMoveToBackgroundEvent(bucketStartTimeNs + 22 * NS_PER_SEC, app1Uid); // 0:22 + processor->OnLogEvent(event.get()); + event = CreateAcquireWakelockEvent(bucketStartTimeNs + 35 * NS_PER_SEC, attributionUids2, + attributionTags2, + "wl1"); // 0:35 + processor->OnLogEvent(event.get()); - // Now update - config.clear_annotation(); - annotation = config.add_annotation(); - annotation->set_field_int64(22); - annotation->set_field_int32(2); - processor->OnConfigUpdated(baseTimeNs + 1000, cfgKey, config, /*modularUpdate=*/GetParam()); + // Add metric. + DurationMetric* durationMetric = config.add_duration_metric(); + durationMetric->set_id(StringToId("WakelockDuration")); + durationMetric->set_what(holdingWakelockPredicate.id()); + durationMetric->set_condition(isInBackgroundPredicate.id()); + durationMetric->set_aggregation_type(DurationMetric::SUM); + // The metric is dimensioning by first attribution node and only by uid. + *durationMetric->mutable_dimensions_in_what() = + CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST}); + durationMetric->set_bucket(FIVE_MINUTES); + // Links between wakelock state atom and condition of app is in background. + auto links = durationMetric->add_links(); + links->set_condition(isInBackgroundPredicate.id()); + *links->mutable_fields_in_what() = + CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST}); + *links->mutable_fields_in_condition() = + CreateDimensions(util::ACTIVITY_FOREGROUND_STATE_CHANGED, {1 /*uid*/}); + + uint64_t updateTimeNs = bucketStartTimeNs + 60 * NS_PER_SEC; // 1:00 + processor->OnConfigUpdated(updateTimeNs, key, config); + + event = CreateMoveToBackgroundEvent(bucketStartTimeNs + 73 * NS_PER_SEC, app2Uid); // 1:13 + processor->OnLogEvent(event.get()); + event = CreateReleaseWakelockEvent(bucketStartTimeNs + 84 * NS_PER_SEC, attributionUids1, + attributionTags1, "wl1"); // 1:24 + processor->OnLogEvent(event.get()); + uint64_t dumpTimeNs = bucketStartTimeNs + 90 * NS_PER_SEC; // 1:30 ConfigMetricsReportList reports; vector<uint8_t> buffer; - processor->onDumpReport(cfgKey, baseTimeNs + 1001, false, true, ADB_DUMP, FAST, &buffer); + processor->onDumpReport(key, dumpTimeNs, true, true, ADB_DUMP, FAST, &buffer); EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); - // First report is written to disk when the update happens. - ASSERT_EQ(reports.reports_size(), 2); - ConfigMetricsReport report = reports.reports(1); - EXPECT_EQ(report.annotation_size(), 1); - EXPECT_EQ(report.annotation(0).field_int64(), 22); - EXPECT_EQ(report.annotation(0).field_int32(), 2); + backfillDimensionPath(&reports); + backfillStringInReport(&reports); + backfillStartEndTimestamp(&reports); + ASSERT_EQ(reports.reports_size(), 1); + ASSERT_EQ(reports.reports(0).metrics_size(), 1); + EXPECT_TRUE(reports.reports(0).metrics(0).has_duration_metrics()); + + StatsLogReport::DurationMetricDataWrapper metricData; + sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).duration_metrics(), &metricData); + ASSERT_EQ(metricData.data_size(), 2); + + DurationMetricData data = metricData.data(0); + ValidateAttributionUidDimension(data.dimensions_in_what(), util::WAKELOCK_STATE_CHANGED, + app1Uid); + ASSERT_EQ(data.bucket_info_size(), 1); + DurationBucketInfo bucketInfo = data.bucket_info(0); + EXPECT_EQ(bucketInfo.duration_nanos(), 24 * NS_PER_SEC); + + data = metricData.data(1); + ValidateAttributionUidDimension(data.dimensions_in_what(), util::WAKELOCK_STATE_CHANGED, + app2Uid); + ASSERT_EQ(data.bucket_info_size(), 1); + bucketInfo = data.bucket_info(0); + EXPECT_EQ(bucketInfo.duration_nanos(), 17 * NS_PER_SEC); } -TEST_P(ConfigUpdateE2eTest, TestPersistLocally) { - StatsdConfig config = CreateSimpleConfig(); - config.set_persist_locally(false); - int64_t baseTimeNs = getElapsedRealtimeNs(); - ConfigKey cfgKey(0, 12345); - sp<StatsLogProcessor> processor = - CreateStatsLogProcessor(baseTimeNs, baseTimeNs, config, cfgKey); - ConfigMetricsReportList reports; - vector<uint8_t> buffer; - processor->onDumpReport(cfgKey, baseTimeNs + 1001, false, true, ADB_DUMP, FAST, &buffer); - EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); - ASSERT_EQ(reports.reports_size(), 1); - // Number of reports should still be 1 since persist_locally is false. - reports.Clear(); - buffer.clear(); - processor->onDumpReport(cfgKey, baseTimeNs + 1001, false, true, ADB_DUMP, FAST, &buffer); - EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); - ASSERT_EQ(reports.reports_size(), 1); +TEST_F(ConfigUpdateE2eTest, TestNewDurationExistingWhatSlicedState) { + StatsdConfig config; + config.add_allowed_log_source("AID_ROOT"); + *config.add_atom_matcher() = CreateAcquireWakelockAtomMatcher(); + *config.add_atom_matcher() = CreateReleaseWakelockAtomMatcher(); - // Now update. - config.set_persist_locally(true); - processor->OnConfigUpdated(baseTimeNs + 1000, cfgKey, config, /*modularUpdate=*/GetParam()); + Predicate holdingWakelockPredicate = CreateHoldingWakelockPredicate(); + // The predicate is dimensioning by first attribution node by uid. + *holdingWakelockPredicate.mutable_simple_predicate()->mutable_dimensions() = + CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST}); + *config.add_predicate() = holdingWakelockPredicate; - // Should get 2: 1 in memory + 1 on disk. Both should be saved on disk. - reports.Clear(); - buffer.clear(); - processor->onDumpReport(cfgKey, baseTimeNs + 1001, false, true, ADB_DUMP, FAST, &buffer); - EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); - ASSERT_EQ(reports.reports_size(), 2); - // Should get 3, 2 on disk + 1 in memory. - reports.Clear(); - buffer.clear(); - processor->onDumpReport(cfgKey, baseTimeNs + 1001, false, true, ADB_DUMP, FAST, &buffer); - EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); - ASSERT_EQ(reports.reports_size(), 3); - string suffix = StringPrintf("%d_%lld", cfgKey.GetUid(), (long long)cfgKey.GetId()); - StorageManager::deleteSuffixedFiles(STATS_DATA_DIR, suffix.c_str()); - string historySuffix = - StringPrintf("%d_%lld_history", cfgKey.GetUid(), (long long)cfgKey.GetId()); - StorageManager::deleteSuffixedFiles(STATS_DATA_DIR, historySuffix.c_str()); -} + auto uidProcessState = CreateUidProcessState(); + *config.add_state() = uidProcessState; -TEST_P(ConfigUpdateE2eTest, TestNoReportMetrics) { - StatsdConfig config = CreateSimpleConfig(); - // Second simple count metric. + // Count metric. We don't care about this one. Only use it so the StateTracker gets persisted. CountMetric* countMetric = config.add_count_metric(); - countMetric->set_id(StringToId("Count2")); + countMetric->set_id(StringToId("Tmp")); countMetric->set_what(config.atom_matcher(0).id()); + countMetric->add_slice_by_state(uidProcessState.id()); + // The metric is dimensioning by first attribution node and only by uid. + *countMetric->mutable_dimensions_in_what() = + CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST}); countMetric->set_bucket(FIVE_MINUTES); - config.add_no_report_metric(config.count_metric(0).id()); - int64_t baseTimeNs = getElapsedRealtimeNs(); - ConfigKey cfgKey(0, 12345); + auto stateLink = countMetric->add_state_link(); + stateLink->set_state_atom_id(util::UID_PROCESS_STATE_CHANGED); + *stateLink->mutable_fields_in_what() = + CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST}); + *stateLink->mutable_fields_in_state() = + CreateDimensions(util::UID_PROCESS_STATE_CHANGED, {1 /*uid*/}); + config.add_no_report_metric(countMetric->id()); + + ConfigKey key(123, 987); + uint64_t bucketStartTimeNs = 10000000000; // 0:10 + uint64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(FIVE_MINUTES) * 1000000LL; sp<StatsLogProcessor> processor = - CreateStatsLogProcessor(baseTimeNs, baseTimeNs, config, cfgKey); - - // Now update. - config.clear_no_report_metric(); - config.add_no_report_metric(config.count_metric(1).id()); - processor->OnConfigUpdated(baseTimeNs + 1000, cfgKey, config, /*modularUpdate=*/GetParam()); - - ConfigMetricsReportList reports; - vector<uint8_t> buffer; - processor->onDumpReport(cfgKey, baseTimeNs + 1001, false, true, ADB_DUMP, FAST, &buffer); - EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); - // First report is written to disk when the update happens. - ASSERT_EQ(reports.reports_size(), 2); - // First report (before update) has the first count metric. - ASSERT_EQ(reports.reports(0).metrics_size(), 1); - EXPECT_EQ(reports.reports(0).metrics(0).metric_id(), config.count_metric(1).id()); - // Second report (after update) has the first count metric. - ASSERT_EQ(reports.reports(1).metrics_size(), 1); - EXPECT_EQ(reports.reports(1).metrics(0).metric_id(), config.count_metric(0).id()); -} + CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, key); + + int app1Uid = 123, app2Uid = 456; + vector<int> attributionUids1 = {app1Uid}; + vector<string> attributionTags1 = {"App1"}; + vector<int> attributionUids2 = {app2Uid}; + vector<string> attributionTags2 = {"App2"}; + unique_ptr<LogEvent> event = CreateUidProcessStateChangedEvent( + bucketStartTimeNs + 10 * NS_PER_SEC, app1Uid, + android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND); // 0:10 + processor->OnLogEvent(event.get()); + event = CreateAcquireWakelockEvent(bucketStartTimeNs + 22 * NS_PER_SEC, attributionUids1, + attributionTags1, + "wl1"); // 0:22 + processor->OnLogEvent(event.get()); + event = CreateUidProcessStateChangedEvent( + bucketStartTimeNs + 30 * NS_PER_SEC, app2Uid, + android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND); // 0:30 + processor->OnLogEvent(event.get()); -TEST_P(ConfigUpdateE2eTest, TestAtomsAllowedFromAnyUid) { - StatsdConfig config = CreateSimpleConfig(); - int64_t baseTimeNs = getElapsedRealtimeNs(); - ConfigKey cfgKey(0, 12345); - sp<StatsLogProcessor> processor = - CreateStatsLogProcessor(baseTimeNs, baseTimeNs, config, cfgKey); - // Uses AID_ROOT, which isn't in allowed log sources. - unique_ptr<LogEvent> event = CreateBatteryStateChangedEvent( - baseTimeNs + 2, BatteryPluggedStateEnum::BATTERY_PLUGGED_USB); + // Add metric. + DurationMetric* durationMetric = config.add_duration_metric(); + durationMetric->set_id(StringToId("WakelockDuration")); + durationMetric->set_what(holdingWakelockPredicate.id()); + durationMetric->add_slice_by_state(uidProcessState.id()); + durationMetric->set_aggregation_type(DurationMetric::SUM); + // The metric is dimensioning by first attribution node and only by uid. + *durationMetric->mutable_dimensions_in_what() = + CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST}); + durationMetric->set_bucket(FIVE_MINUTES); + // Links between wakelock state atom and condition of app is in background. + stateLink = durationMetric->add_state_link(); + stateLink->set_state_atom_id(util::UID_PROCESS_STATE_CHANGED); + *stateLink->mutable_fields_in_what() = + CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST}); + *stateLink->mutable_fields_in_state() = + CreateDimensions(util::UID_PROCESS_STATE_CHANGED, {1 /*uid*/}); + + uint64_t updateTimeNs = bucketStartTimeNs + 60 * NS_PER_SEC; // 1:00 + processor->OnConfigUpdated(updateTimeNs, key, config); + + event = CreateAcquireWakelockEvent(bucketStartTimeNs + 72 * NS_PER_SEC, attributionUids2, + attributionTags2, + "wl1"); // 1:13 processor->OnLogEvent(event.get()); + event = CreateUidProcessStateChangedEvent( + bucketStartTimeNs + 75 * NS_PER_SEC, app1Uid, + android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND); // 1:15 + processor->OnLogEvent(event.get()); + event = CreateReleaseWakelockEvent(bucketStartTimeNs + 84 * NS_PER_SEC, attributionUids1, + attributionTags1, "wl1"); // 1:24 + processor->OnLogEvent(event.get()); + + uint64_t dumpTimeNs = bucketStartTimeNs + 90 * NS_PER_SEC; // 1:30 ConfigMetricsReportList reports; vector<uint8_t> buffer; - processor->onDumpReport(cfgKey, baseTimeNs + 1001, true, true, ADB_DUMP, FAST, &buffer); + processor->onDumpReport(key, dumpTimeNs, true, true, ADB_DUMP, FAST, &buffer); EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); + backfillDimensionPath(&reports); + backfillStringInReport(&reports); + backfillStartEndTimestamp(&reports); ASSERT_EQ(reports.reports_size(), 1); - // Check the metric and make sure it has 0 count. ASSERT_EQ(reports.reports(0).metrics_size(), 1); - EXPECT_FALSE(reports.reports(0).metrics(0).has_count_metrics()); - - // Now update. Allow plugged state to be logged from any uid, so the atom will be counted. - config.add_whitelisted_atom_ids(util::PLUGGED_STATE_CHANGED); - processor->OnConfigUpdated(baseTimeNs + 1000, cfgKey, config, /*modularUpdate=*/GetParam()); - unique_ptr<LogEvent> event2 = CreateBatteryStateChangedEvent( - baseTimeNs + 2000, BatteryPluggedStateEnum::BATTERY_PLUGGED_USB); - processor->OnLogEvent(event.get()); - reports.Clear(); - buffer.clear(); - processor->onDumpReport(cfgKey, baseTimeNs + 3000, true, true, ADB_DUMP, FAST, &buffer); - EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); - ASSERT_EQ(reports.reports_size(), 2); - // Check the metric and make sure it has 0 count. - ASSERT_EQ(reports.reports(1).metrics_size(), 1); - EXPECT_TRUE(reports.reports(1).metrics(0).has_count_metrics()); - ASSERT_EQ(reports.reports(1).metrics(0).count_metrics().data_size(), 1); - ASSERT_EQ(reports.reports(1).metrics(0).count_metrics().data(0).bucket_info_size(), 1); - EXPECT_EQ(reports.reports(1).metrics(0).count_metrics().data(0).bucket_info(0).count(), 1); -} - -TEST_P(ConfigUpdateE2eTest, TestConfigTtl) { - StatsdConfig config = CreateSimpleConfig(); - config.set_ttl_in_seconds(1); - int64_t baseTimeNs = getElapsedRealtimeNs(); - ConfigKey cfgKey(0, 12345); - sp<StatsLogProcessor> processor = - CreateStatsLogProcessor(baseTimeNs, baseTimeNs, config, cfgKey); - EXPECT_EQ(processor->mMetricsManagers.size(), 1u); - sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second; - EXPECT_EQ(metricsManager->getTtlEndNs(), baseTimeNs + NS_PER_SEC); - - config.set_ttl_in_seconds(5); - processor->OnConfigUpdated(baseTimeNs + 2 * NS_PER_SEC, cfgKey, config, - /*modularUpdate=*/GetParam()); - metricsManager = processor->mMetricsManagers.begin()->second; - EXPECT_EQ(metricsManager->getTtlEndNs(), baseTimeNs + 7 * NS_PER_SEC); - - // Clear the data stored on disk as a result of the update. - vector<uint8_t> buffer; - processor->onDumpReport(cfgKey, baseTimeNs + 3 * NS_PER_SEC, false, true, ADB_DUMP, FAST, - &buffer); + EXPECT_TRUE(reports.reports(0).metrics(0).has_duration_metrics()); + + StatsLogReport::DurationMetricDataWrapper metricData; + sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).duration_metrics(), &metricData); + ASSERT_EQ(metricData.data_size(), 3); + + DurationMetricData data = metricData.data(0); + ValidateAttributionUidDimension(data.dimensions_in_what(), util::WAKELOCK_STATE_CHANGED, + app1Uid); + ValidateStateValue(data.slice_by_state(), util::UID_PROCESS_STATE_CHANGED, + android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND); + ASSERT_EQ(data.bucket_info_size(), 1); + DurationBucketInfo bucketInfo = data.bucket_info(0); + EXPECT_EQ(bucketInfo.duration_nanos(), 15 * NS_PER_SEC); + + data = metricData.data(1); + ValidateAttributionUidDimension(data.dimensions_in_what(), util::WAKELOCK_STATE_CHANGED, + app1Uid); + ValidateStateValue(data.slice_by_state(), util::UID_PROCESS_STATE_CHANGED, + android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND); + ASSERT_EQ(data.bucket_info_size(), 1); + bucketInfo = data.bucket_info(0); + EXPECT_EQ(bucketInfo.duration_nanos(), 9 * NS_PER_SEC); + + data = metricData.data(2); + ValidateAttributionUidDimension(data.dimensions_in_what(), util::WAKELOCK_STATE_CHANGED, + app2Uid); + ValidateStateValue(data.slice_by_state(), util::UID_PROCESS_STATE_CHANGED, + android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND); + ASSERT_EQ(data.bucket_info_size(), 1); + bucketInfo = data.bucket_info(0); + EXPECT_EQ(bucketInfo.duration_nanos(), 18 * NS_PER_SEC); } #else diff --git a/cmds/statsd/tests/e2e/DurationMetric_e2e_test.cpp b/cmds/statsd/tests/e2e/DurationMetric_e2e_test.cpp index 4efb038e538d..2473c1ca1101 100644 --- a/cmds/statsd/tests/e2e/DurationMetric_e2e_test.cpp +++ b/cmds/statsd/tests/e2e/DurationMetric_e2e_test.cpp @@ -486,11 +486,11 @@ TEST(DurationMetricE2eTest, TestWithSlicedCondition) { // Links between wakelock state atom and condition of app is in background. auto links = durationMetric->add_links(); links->set_condition(isInBackgroundPredicate.id()); - auto dimensionWhat = links->mutable_fields_in_what(); - dimensionWhat->set_field(util::WAKELOCK_STATE_CHANGED); - dimensionWhat->add_child()->set_field(1); // uid field. - *links->mutable_fields_in_condition() = CreateAttributionUidDimensions( - util::ACTIVITY_FOREGROUND_STATE_CHANGED, {Position::FIRST}); + *links->mutable_fields_in_what() = + CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST}); + auto dimensionCondition = links->mutable_fields_in_condition(); + dimensionCondition->set_field(util::ACTIVITY_FOREGROUND_STATE_CHANGED); + dimensionCondition->add_child()->set_field(1); // uid field. ConfigKey cfgKey; uint64_t bucketStartTimeNs = 10000000000; @@ -591,11 +591,11 @@ TEST(DurationMetricE2eTest, TestWithActivationAndSlicedCondition) { // Links between wakelock state atom and condition of app is in background. auto links = durationMetric->add_links(); links->set_condition(isInBackgroundPredicate.id()); - auto dimensionWhat = links->mutable_fields_in_what(); - dimensionWhat->set_field(util::WAKELOCK_STATE_CHANGED); - dimensionWhat->add_child()->set_field(1); // uid field. - *links->mutable_fields_in_condition() = CreateAttributionUidDimensions( - util::ACTIVITY_FOREGROUND_STATE_CHANGED, {Position::FIRST}); + *links->mutable_fields_in_what() = + CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST}); + auto dimensionCondition = links->mutable_fields_in_condition(); + dimensionCondition->set_field(util::ACTIVITY_FOREGROUND_STATE_CHANGED); + dimensionCondition->add_child()->set_field(1); // uid field. auto metric_activation1 = config.add_metric_activation(); metric_activation1->set_metric_id(durationMetric->id()); @@ -1228,6 +1228,9 @@ TEST(DurationMetricE2eTest, TestWithSlicedStatePrimaryFieldsSubset) { *config.add_atom_matcher() = CreateReleaseWakelockAtomMatcher(); auto holdingWakelockPredicate = CreateHoldingWakelockPredicate(); + *(holdingWakelockPredicate.mutable_simple_predicate()->mutable_dimensions()) = + CreateAttributionUidAndOtherDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST}, + {3 /* tag */}); *config.add_predicate() = holdingWakelockPredicate; auto uidProcessState = CreateUidProcessState(); diff --git a/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp b/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp index d1f89775ed6a..bb2ede4ddc2b 100644 --- a/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp +++ b/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp @@ -73,9 +73,9 @@ TEST(DurationMetricTrackerTest, TestFirstBucket) { FieldMatcher dimensions; DurationMetricProducer durationProducer( - kConfigKey, metric, -1 /*no condition*/, {}, 1 /* start index */, 2 /* stop index */, - 3 /* stop_all index */, false /*nesting*/, wizard, protoHash, dimensions, 5, - 600 * NS_PER_SEC + NS_PER_SEC / 2); + kConfigKey, metric, -1 /*no condition*/, {}, -1 /*what index not needed*/, + 1 /* start index */, 2 /* stop index */, 3 /* stop_all index */, false /*nesting*/, + wizard, protoHash, dimensions, 5, 600 * NS_PER_SEC + NS_PER_SEC / 2); EXPECT_EQ(600500000000, durationProducer.mCurrentBucketStartTimeNs); EXPECT_EQ(10, durationProducer.mCurrentBucketNum); @@ -101,9 +101,9 @@ TEST(DurationMetricTrackerTest, TestNoCondition) { FieldMatcher dimensions; DurationMetricProducer durationProducer( - kConfigKey, metric, -1 /*no condition*/, {}, 1 /* start index */, 2 /* stop index */, - 3 /* stop_all index */, false /*nesting*/, wizard, protoHash, dimensions, - bucketStartTimeNs, bucketStartTimeNs); + kConfigKey, metric, -1 /*no condition*/, {}, -1 /*what index not needed*/, + 1 /* start index */, 2 /* stop index */, 3 /* stop_all index */, false /*nesting*/, + wizard, protoHash, dimensions, bucketStartTimeNs, bucketStartTimeNs); durationProducer.onMatchedLogEvent(1 /* start index*/, event1); durationProducer.onMatchedLogEvent(2 /* stop index*/, event2); @@ -145,8 +145,9 @@ TEST(DurationMetricTrackerTest, TestNonSlicedCondition) { DurationMetricProducer durationProducer( kConfigKey, metric, 0 /* condition index */, {ConditionState::kUnknown}, - 1 /* start index */, 2 /* stop index */, 3 /* stop_all index */, false /*nesting*/, - wizard, protoHash, dimensions, bucketStartTimeNs, bucketStartTimeNs); + -1 /*what index not needed*/, 1 /* start index */, 2 /* stop index */, + 3 /* stop_all index */, false /*nesting*/, wizard, protoHash, dimensions, + bucketStartTimeNs, bucketStartTimeNs); durationProducer.mCondition = ConditionState::kFalse; EXPECT_FALSE(durationProducer.mCondition); @@ -195,8 +196,9 @@ TEST(DurationMetricTrackerTest, TestNonSlicedConditionUnknownState) { DurationMetricProducer durationProducer( kConfigKey, metric, 0 /* condition index */, {ConditionState::kUnknown}, - 1 /* start index */, 2 /* stop index */, 3 /* stop_all index */, false /*nesting*/, - wizard, protoHash, dimensions, bucketStartTimeNs, bucketStartTimeNs); + -1 /*what index not needed*/, 1 /* start index */, 2 /* stop index */, + 3 /* stop_all index */, false /*nesting*/, wizard, protoHash, dimensions, + bucketStartTimeNs, bucketStartTimeNs); EXPECT_EQ(ConditionState::kUnknown, durationProducer.mCondition); EXPECT_FALSE(durationProducer.isConditionSliced()); @@ -240,9 +242,9 @@ TEST_P(DurationMetricProducerTest_PartialBucket, TestSumDuration) { FieldMatcher dimensions; DurationMetricProducer durationProducer( - kConfigKey, metric, -1 /* no condition */, {}, 1 /* start index */, 2 /* stop index */, - 3 /* stop_all index */, false /*nesting*/, wizard, protoHash, dimensions, - bucketStartTimeNs, bucketStartTimeNs); + kConfigKey, metric, -1 /* no condition */, {}, -1 /*what index not needed*/, + 1 /* start index */, 2 /* stop index */, 3 /* stop_all index */, false /*nesting*/, + wizard, protoHash, dimensions, bucketStartTimeNs, bucketStartTimeNs); int64_t startTimeNs = bucketStartTimeNs + 1 * NS_PER_SEC; LogEvent event1(/*uid=*/0, /*pid=*/0); @@ -303,9 +305,9 @@ TEST_P(DurationMetricProducerTest_PartialBucket, TestSumDurationWithSplitInFollo FieldMatcher dimensions; DurationMetricProducer durationProducer( - kConfigKey, metric, -1 /* no condition */, {}, 1 /* start index */, 2 /* stop index */, - 3 /* stop_all index */, false /*nesting*/, wizard, protoHash, dimensions, - bucketStartTimeNs, bucketStartTimeNs); + kConfigKey, metric, -1 /* no condition */, {}, -1 /*what index not needed*/, + 1 /* start index */, 2 /* stop index */, 3 /* stop_all index */, false /*nesting*/, + wizard, protoHash, dimensions, bucketStartTimeNs, bucketStartTimeNs); int64_t startTimeNs = bucketStartTimeNs + 1 * NS_PER_SEC; LogEvent event1(/*uid=*/0, /*pid=*/0); @@ -367,9 +369,9 @@ TEST_P(DurationMetricProducerTest_PartialBucket, TestSumDurationAnomaly) { FieldMatcher dimensions; DurationMetricProducer durationProducer( - kConfigKey, metric, -1 /* no condition */, {}, 1 /* start index */, 2 /* stop index */, - 3 /* stop_all index */, false /*nesting*/, wizard, protoHash, dimensions, - bucketStartTimeNs, bucketStartTimeNs); + kConfigKey, metric, -1 /* no condition */, {}, -1 /*what index not needed*/, + 1 /* start index */, 2 /* stop index */, 3 /* stop_all index */, false /*nesting*/, + wizard, protoHash, dimensions, bucketStartTimeNs, bucketStartTimeNs); sp<AnomalyTracker> anomalyTracker = durationProducer.addAnomalyTracker(alert, alarmMonitor); EXPECT_TRUE(anomalyTracker != nullptr); @@ -413,9 +415,9 @@ TEST_P(DurationMetricProducerTest_PartialBucket, TestMaxDuration) { FieldMatcher dimensions; DurationMetricProducer durationProducer( - kConfigKey, metric, -1 /* no condition */, {}, 1 /* start index */, 2 /* stop index */, - 3 /* stop_all index */, false /*nesting*/, wizard, protoHash, dimensions, - bucketStartTimeNs, bucketStartTimeNs); + kConfigKey, metric, -1 /* no condition */, {}, -1 /*what index not needed*/, + 1 /* start index */, 2 /* stop index */, 3 /* stop_all index */, false /*nesting*/, + wizard, protoHash, dimensions, bucketStartTimeNs, bucketStartTimeNs); int64_t startTimeNs = bucketStartTimeNs + 1; LogEvent event1(/*uid=*/0, /*pid=*/0); @@ -467,9 +469,9 @@ TEST_P(DurationMetricProducerTest_PartialBucket, TestMaxDurationWithSplitInNextB FieldMatcher dimensions; DurationMetricProducer durationProducer( - kConfigKey, metric, -1 /* no condition */, {}, 1 /* start index */, 2 /* stop index */, - 3 /* stop_all index */, false /*nesting*/, wizard, protoHash, dimensions, - bucketStartTimeNs, bucketStartTimeNs); + kConfigKey, metric, -1 /* no condition */, {}, -1 /*what index not needed*/, + 1 /* start index */, 2 /* stop index */, 3 /* stop_all index */, false /*nesting*/, + wizard, protoHash, dimensions, bucketStartTimeNs, bucketStartTimeNs); int64_t startTimeNs = bucketStartTimeNs + 1; LogEvent event1(/*uid=*/0, /*pid=*/0); diff --git a/cmds/statsd/tests/metrics/parsing_utils/metrics_manager_util_test.cpp b/cmds/statsd/tests/metrics/parsing_utils/metrics_manager_util_test.cpp index 9e2350b33018..9ab7e47dce43 100644 --- a/cmds/statsd/tests/metrics/parsing_utils/metrics_manager_util_test.cpp +++ b/cmds/statsd/tests/metrics/parsing_utils/metrics_manager_util_test.cpp @@ -875,12 +875,50 @@ TEST(MetricsManagerTest, TestCreateAnomalyTrackerDurationTooLong) { FieldMatcher dimensions; sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); vector<sp<MetricProducer>> metricProducers({new DurationMetricProducer( - kConfigKey, metric, -1 /*no condition*/, {}, 1 /* start index */, 2 /* stop index */, - 3 /* stop_all index */, false /*nesting*/, wizard, 0x0123456789, dimensions, 0, 0)}); + kConfigKey, metric, -1 /*no condition*/, {}, -1 /* what index not needed*/, + 1 /* start index */, 2 /* stop index */, 3 /* stop_all index */, false /*nesting*/, + wizard, 0x0123456789, dimensions, 0, 0)}); sp<AlarmMonitor> anomalyAlarmMonitor; EXPECT_EQ(createAnomalyTracker(alert, anomalyAlarmMonitor, {{1, 0}}, metricProducers), nullopt); } +TEST(MetricsManagerTest, TestCreateDurationProducerDimensionsInWhatInvalid) { + StatsdConfig config; + config.add_allowed_log_source("AID_ROOT"); + *config.add_atom_matcher() = CreateAcquireWakelockAtomMatcher(); + *config.add_atom_matcher() = CreateReleaseWakelockAtomMatcher(); + *config.add_atom_matcher() = CreateMoveToBackgroundAtomMatcher(); + *config.add_atom_matcher() = CreateMoveToForegroundAtomMatcher(); + + Predicate holdingWakelockPredicate = CreateHoldingWakelockPredicate(); + // The predicate is dimensioning by first attribution node by uid. + FieldMatcher dimensions = + CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST}); + *holdingWakelockPredicate.mutable_simple_predicate()->mutable_dimensions() = dimensions; + *config.add_predicate() = holdingWakelockPredicate; + + DurationMetric* durationMetric = config.add_duration_metric(); + durationMetric->set_id(StringToId("WakelockDuration")); + durationMetric->set_what(holdingWakelockPredicate.id()); + durationMetric->set_aggregation_type(DurationMetric::SUM); + // The metric is dimensioning by first attribution node by uid AND tag. + // Invalid since the predicate only dimensions by uid. + *durationMetric->mutable_dimensions_in_what() = CreateAttributionUidAndOtherDimensions( + util::WAKELOCK_STATE_CHANGED, {Position::FIRST}, {3 /* tag */}); + durationMetric->set_bucket(FIVE_MINUTES); + + ConfigKey key(123, 987); + uint64_t timeNs = 456; + sp<StatsPullerManager> pullerManager = new StatsPullerManager(); + sp<AlarmMonitor> anomalyAlarmMonitor; + sp<AlarmMonitor> periodicAlarmMonitor; + sp<UidMap> uidMap; + sp<MetricsManager> metricsManager = + new MetricsManager(key, config, timeNs, timeNs, uidMap, pullerManager, + anomalyAlarmMonitor, periodicAlarmMonitor); + EXPECT_FALSE(metricsManager->isConfigValid()); +} + } // namespace statsd } // namespace os } // namespace android diff --git a/cmds/statsd/tests/statsd_test_util.cpp b/cmds/statsd/tests/statsd_test_util.cpp index 1761d5d9e1fa..153b696808dc 100644 --- a/cmds/statsd/tests/statsd_test_util.cpp +++ b/cmds/statsd/tests/statsd_test_util.cpp @@ -1091,6 +1091,21 @@ void ValidateAttributionUidAndTagDimension( .value_tuple().dimensions_value(1).value_str(), tag); } +void ValidateStateValue(const google::protobuf::RepeatedPtrField<StateValue>& stateValues, + int atomId, int64_t value) { + ASSERT_EQ(stateValues.size(), 1); + ASSERT_EQ(stateValues[0].atom_id(), atomId); + switch (stateValues[0].contents_case()) { + case StateValue::ContentsCase::kValue: + EXPECT_EQ(stateValues[0].value(), (int32_t)value); + break; + case StateValue::ContentsCase::kGroupId: + EXPECT_EQ(stateValues[0].group_id(), value); + break; + default: + FAIL() << "State value should have either a value or a group id"; + } +} bool EqualsTo(const DimensionsValue& s1, const DimensionsValue& s2) { if (s1.field() != s2.field()) { return false; diff --git a/cmds/statsd/tests/statsd_test_util.h b/cmds/statsd/tests/statsd_test_util.h index 1220019e2353..c51491244fd8 100644 --- a/cmds/statsd/tests/statsd_test_util.h +++ b/cmds/statsd/tests/statsd_test_util.h @@ -347,6 +347,8 @@ void ValidateAttributionUidAndTagDimension( const DimensionsValue& value, int atomId, int uid, const std::string& tag); void ValidateAttributionUidAndTagDimension( const DimensionsValue& value, int node_idx, int atomId, int uid, const std::string& tag); +void ValidateStateValue(const google::protobuf::RepeatedPtrField<StateValue>& stateValues, + int atomId, int64_t value); struct DimensionsPair { DimensionsPair(DimensionsValue m1, google::protobuf::RepeatedPtrField<StateValue> m2) diff --git a/core/api/current.txt b/core/api/current.txt index 9cec7a67d6ec..ed92c2977a2f 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -9098,6 +9098,15 @@ package android.bluetooth { field @NonNull public static final android.os.Parcelable.Creator<android.bluetooth.BluetoothHidDeviceAppSdpSettings> CREATOR; } + public final class BluetoothLeAudio implements java.lang.AutoCloseable android.bluetooth.BluetoothProfile { + method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public void close(); + method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) protected void finalize(); + method @NonNull public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices(); + method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public int getConnectionState(@NonNull android.bluetooth.BluetoothDevice); + method @NonNull public java.util.List<android.bluetooth.BluetoothDevice> getDevicesMatchingConnectionStates(@NonNull int[]); + field public static final String ACTION_LE_AUDIO_CONNECTION_STATE_CHANGED = "android.bluetooth.action.LE_AUDIO_CONNECTION_STATE_CHANGED"; + } + public final class BluetoothManager { method public android.bluetooth.BluetoothAdapter getAdapter(); method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices(int); @@ -10319,7 +10328,7 @@ package android.content { method public int checkPermission(String, int, int); method public int checkSelfPermission(String); method public int checkUriPermission(android.net.Uri, int, int, int); - method public int checkUriPermission(android.net.Uri, String, String, int, int, int); + method public int checkUriPermission(@Nullable android.net.Uri, @Nullable String, @Nullable String, int, int, int); method @Deprecated public void clearWallpaper() throws java.io.IOException; method public android.content.Context createConfigurationContext(android.content.res.Configuration); method public android.content.Context createContextForSplit(String) throws android.content.pm.PackageManager.NameNotFoundException; @@ -10330,13 +10339,13 @@ package android.content { method public boolean deleteDatabase(String); method public boolean deleteFile(String); method public boolean deleteSharedPreferences(String); - method public void enforceCallingOrSelfPermission(String, String); + method public void enforceCallingOrSelfPermission(String, @Nullable String); method public void enforceCallingOrSelfUriPermission(android.net.Uri, int, String); - method public void enforceCallingPermission(String, String); + method public void enforceCallingPermission(String, @Nullable String); method public void enforceCallingUriPermission(android.net.Uri, int, String); - method public void enforcePermission(String, int, int, String); + method public void enforcePermission(String, int, int, @Nullable String); method public void enforceUriPermission(android.net.Uri, int, int, int, String); - method public void enforceUriPermission(android.net.Uri, String, String, int, int, int, String); + method public void enforceUriPermission(@Nullable android.net.Uri, @Nullable String, @Nullable String, int, int, int, @Nullable String); method public String[] fileList(); method public android.content.Context getApplicationContext(); method public android.content.pm.ApplicationInfo getApplicationInfo(); @@ -10349,9 +10358,9 @@ package android.content { method public java.io.File getDataDir(); method public java.io.File getDatabasePath(String); method public java.io.File getDir(String, int); - method public java.io.File getExternalCacheDir(); + method @Nullable public java.io.File getExternalCacheDir(); method public java.io.File[] getExternalCacheDirs(); - method public java.io.File getExternalFilesDir(String); + method @Nullable public java.io.File getExternalFilesDir(@Nullable String); method public java.io.File[] getExternalFilesDirs(String); method public java.io.File[] getExternalMediaDirs(); method public java.io.File getFileStreamPath(String); @@ -10379,40 +10388,40 @@ package android.content { method public java.io.FileInputStream openFileInput(String) throws java.io.FileNotFoundException; method public java.io.FileOutputStream openFileOutput(String, int) throws java.io.FileNotFoundException; method public android.database.sqlite.SQLiteDatabase openOrCreateDatabase(String, int, android.database.sqlite.SQLiteDatabase.CursorFactory); - method public android.database.sqlite.SQLiteDatabase openOrCreateDatabase(String, int, android.database.sqlite.SQLiteDatabase.CursorFactory, android.database.DatabaseErrorHandler); + method public android.database.sqlite.SQLiteDatabase openOrCreateDatabase(String, int, android.database.sqlite.SQLiteDatabase.CursorFactory, @Nullable android.database.DatabaseErrorHandler); method @Deprecated public android.graphics.drawable.Drawable peekWallpaper(); - method public android.content.Intent registerReceiver(android.content.BroadcastReceiver, android.content.IntentFilter); - method public android.content.Intent registerReceiver(android.content.BroadcastReceiver, android.content.IntentFilter, int); - method public android.content.Intent registerReceiver(android.content.BroadcastReceiver, android.content.IntentFilter, String, android.os.Handler); - method public android.content.Intent registerReceiver(android.content.BroadcastReceiver, android.content.IntentFilter, String, android.os.Handler, int); + method public android.content.Intent registerReceiver(@Nullable android.content.BroadcastReceiver, android.content.IntentFilter); + method public android.content.Intent registerReceiver(@Nullable android.content.BroadcastReceiver, android.content.IntentFilter, int); + method public android.content.Intent registerReceiver(@Nullable android.content.BroadcastReceiver, android.content.IntentFilter, @Nullable String, @Nullable android.os.Handler); + method public android.content.Intent registerReceiver(@Nullable android.content.BroadcastReceiver, android.content.IntentFilter, @Nullable String, @Nullable android.os.Handler, int); method @Deprecated public void removeStickyBroadcast(android.content.Intent); method @Deprecated public void removeStickyBroadcastAsUser(android.content.Intent, android.os.UserHandle); method public void revokeUriPermission(android.net.Uri, int); method public void revokeUriPermission(String, android.net.Uri, int); method public void sendBroadcast(android.content.Intent); - method public void sendBroadcast(android.content.Intent, String); + method public void sendBroadcast(android.content.Intent, @Nullable String); method public void sendBroadcastAsUser(android.content.Intent, android.os.UserHandle); method public void sendBroadcastAsUser(android.content.Intent, android.os.UserHandle, String); - method public void sendOrderedBroadcast(android.content.Intent, String); - method public void sendOrderedBroadcast(android.content.Intent, String, android.content.BroadcastReceiver, android.os.Handler, int, String, android.os.Bundle); + method public void sendOrderedBroadcast(android.content.Intent, @Nullable String); + method public void sendOrderedBroadcast(android.content.Intent, @Nullable String, @Nullable android.content.BroadcastReceiver, @Nullable android.os.Handler, int, @Nullable String, @Nullable android.os.Bundle); method public void sendOrderedBroadcast(@NonNull @RequiresPermission android.content.Intent, int, @Nullable String, @Nullable String, @Nullable android.content.BroadcastReceiver, @Nullable android.os.Handler, @Nullable String, @Nullable android.os.Bundle, @Nullable android.os.Bundle); - method public void sendOrderedBroadcastAsUser(android.content.Intent, android.os.UserHandle, String, android.content.BroadcastReceiver, android.os.Handler, int, String, android.os.Bundle); + method public void sendOrderedBroadcastAsUser(android.content.Intent, android.os.UserHandle, @Nullable String, @Nullable android.content.BroadcastReceiver, @Nullable android.os.Handler, int, @Nullable String, @Nullable android.os.Bundle); method @Deprecated public void sendStickyBroadcast(android.content.Intent); method @Deprecated public void sendStickyBroadcastAsUser(android.content.Intent, android.os.UserHandle); - method @Deprecated public void sendStickyOrderedBroadcast(android.content.Intent, android.content.BroadcastReceiver, android.os.Handler, int, String, android.os.Bundle); - method @Deprecated public void sendStickyOrderedBroadcastAsUser(android.content.Intent, android.os.UserHandle, android.content.BroadcastReceiver, android.os.Handler, int, String, android.os.Bundle); + method @Deprecated public void sendStickyOrderedBroadcast(android.content.Intent, @Nullable android.content.BroadcastReceiver, @Nullable android.os.Handler, int, @Nullable String, @Nullable android.os.Bundle); + method @Deprecated public void sendStickyOrderedBroadcastAsUser(android.content.Intent, android.os.UserHandle, @Nullable android.content.BroadcastReceiver, @Nullable android.os.Handler, int, @Nullable String, @Nullable android.os.Bundle); method public void setTheme(int); method @Deprecated public void setWallpaper(android.graphics.Bitmap) throws java.io.IOException; method @Deprecated public void setWallpaper(java.io.InputStream) throws java.io.IOException; method public void startActivities(android.content.Intent[]); - method public void startActivities(android.content.Intent[], android.os.Bundle); + method public void startActivities(android.content.Intent[], @Nullable android.os.Bundle); method public void startActivity(android.content.Intent); - method public void startActivity(android.content.Intent, android.os.Bundle); - method public android.content.ComponentName startForegroundService(android.content.Intent); - method public boolean startInstrumentation(android.content.ComponentName, String, android.os.Bundle); + method public void startActivity(android.content.Intent, @Nullable android.os.Bundle); + method @Nullable public android.content.ComponentName startForegroundService(android.content.Intent); + method public boolean startInstrumentation(android.content.ComponentName, @Nullable String, @Nullable android.os.Bundle); method public void startIntentSender(android.content.IntentSender, @Nullable android.content.Intent, int, int, int) throws android.content.IntentSender.SendIntentException; method public void startIntentSender(android.content.IntentSender, @Nullable android.content.Intent, int, int, int, @Nullable android.os.Bundle) throws android.content.IntentSender.SendIntentException; - method public android.content.ComponentName startService(android.content.Intent); + method @Nullable public android.content.ComponentName startService(android.content.Intent); method public boolean stopService(android.content.Intent); method public void unbindService(android.content.ServiceConnection); method public void unregisterReceiver(android.content.BroadcastReceiver); @@ -45156,6 +45165,7 @@ package android.telephony { field public static final String KEY_CALL_BARRING_SUPPORTS_DEACTIVATE_ALL_BOOL = "call_barring_supports_deactivate_all_bool"; field public static final String KEY_CALL_BARRING_SUPPORTS_PASSWORD_CHANGE_BOOL = "call_barring_supports_password_change_bool"; field public static final String KEY_CALL_BARRING_VISIBILITY_BOOL = "call_barring_visibility_bool"; + field public static final String KEY_CALL_COMPOSER_PICTURE_SERVER_URL_STRING = "call_composer_picture_server_url_string"; field public static final String KEY_CALL_FORWARDING_BLOCKS_WHILE_ROAMING_STRING_ARRAY = "call_forwarding_blocks_while_roaming_string_array"; field public static final String KEY_CALL_REDIRECTION_SERVICE_COMPONENT_NAME_STRING = "call_redirection_service_component_name_string"; field public static final String KEY_CARRIER_ALLOW_DEFLECT_IMS_CALL_BOOL = "carrier_allow_deflect_ims_call_bool"; @@ -45345,6 +45355,7 @@ package android.telephony { field public static final String KEY_SIMPLIFIED_NETWORK_SETTINGS_BOOL = "simplified_network_settings_bool"; field public static final String KEY_SIM_NETWORK_UNLOCK_ALLOW_DISMISS_BOOL = "sim_network_unlock_allow_dismiss_bool"; field public static final String KEY_SMS_REQUIRES_DESTINATION_NUMBER_CONVERSION_BOOL = "sms_requires_destination_number_conversion_bool"; + field public static final String KEY_SUPPORTS_CALL_COMPOSER_BOOL = "supports_call_composer_bool"; field public static final String KEY_SUPPORT_3GPP_CALL_FORWARDING_WHILE_ROAMING_BOOL = "support_3gpp_call_forwarding_while_roaming_bool"; field public static final String KEY_SUPPORT_ADD_CONFERENCE_PARTICIPANTS_BOOL = "support_add_conference_participants_bool"; field public static final String KEY_SUPPORT_ADHOC_CONFERENCE_CALLS_BOOL = "support_adhoc_conference_calls_bool"; @@ -47662,6 +47673,7 @@ package android.telephony.ims.feature { public static class MmTelFeature.MmTelCapabilities { method public final boolean isCapable(int); + field public static final int CAPABILITY_TYPE_CALL_COMPOSER = 16; // 0x10 field public static final int CAPABILITY_TYPE_SMS = 8; // 0x8 field public static final int CAPABILITY_TYPE_UT = 4; // 0x4 field public static final int CAPABILITY_TYPE_VIDEO = 2; // 0x2 diff --git a/core/api/system-current.txt b/core/api/system-current.txt index d9a9c265f3b8..8eb1b5f084fe 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -1795,11 +1795,11 @@ package android.content { public class ContextWrapper extends android.content.Context { method public android.content.Context createCredentialProtectedStorageContext(); - method public java.io.File getPreloadsFileCache(); + method @Nullable public java.io.File getPreloadsFileCache(); method public boolean isCredentialProtectedStorage(); - method public void sendBroadcast(android.content.Intent, String, android.os.Bundle); - method public void sendBroadcastAsUser(android.content.Intent, android.os.UserHandle, String, android.os.Bundle); - method public void sendOrderedBroadcast(android.content.Intent, String, android.os.Bundle, android.content.BroadcastReceiver, android.os.Handler, int, String, android.os.Bundle); + method public void sendBroadcast(android.content.Intent, @Nullable String, @Nullable android.os.Bundle); + method public void sendBroadcastAsUser(android.content.Intent, android.os.UserHandle, @Nullable String, @Nullable android.os.Bundle); + method public void sendOrderedBroadcast(android.content.Intent, @Nullable String, @Nullable android.os.Bundle, @Nullable android.content.BroadcastReceiver, @Nullable android.os.Handler, int, @Nullable String, @Nullable android.os.Bundle); } public class Intent implements java.lang.Cloneable android.os.Parcelable { @@ -6506,6 +6506,7 @@ package android.net { field @Deprecated public static final int TETHER_ERROR_NO_ERROR = 0; // 0x0 field @Deprecated public static final int TETHER_ERROR_PROVISION_FAILED = 11; // 0xb field public static final int TYPE_NONE = -1; // 0xffffffff + field @Deprecated public static final int TYPE_PROXY = 16; // 0x10 field @Deprecated public static final int TYPE_WIFI_P2P = 13; // 0xd } @@ -9707,6 +9708,33 @@ package android.telecom { field @Deprecated public static final int ROUTE_WIRED_OR_EARPIECE = 5; // 0x5 } + public final class BluetoothCallQualityReport implements android.os.Parcelable { + method public int describeContents(); + method @IntRange(from=0) public int getNegativeAcknowledgementCount(); + method @IntRange(from=0) public int getPacketsNotReceivedCount(); + method @IntRange(from=0) public int getRetransmittedPacketsCount(); + method @IntRange(from=0xffffff81, to=20) public int getRssiDbm(); + method public long getSentTimestampMillis(); + method public int getSnrDb(); + method public boolean isChoppyVoice(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.telecom.BluetoothCallQualityReport> CREATOR; + field public static final String EVENT_BLUETOOTH_CALL_QUALITY_REPORT = "android.telecom.event.BLUETOOTH_CALL_QUALITY_REPORT"; + field public static final String EXTRA_BLUETOOTH_CALL_QUALITY_REPORT = "android.telecom.extra.BLUETOOTH_CALL_QUALITY_REPORT"; + } + + public static final class BluetoothCallQualityReport.Builder { + ctor public BluetoothCallQualityReport.Builder(); + method @NonNull public android.telecom.BluetoothCallQualityReport build(); + method @NonNull public android.telecom.BluetoothCallQualityReport.Builder setChoppyVoice(boolean); + method @NonNull public android.telecom.BluetoothCallQualityReport.Builder setNegativeAcknowledgementCount(int); + method @NonNull public android.telecom.BluetoothCallQualityReport.Builder setPacketsNotReceivedCount(int); + method @NonNull public android.telecom.BluetoothCallQualityReport.Builder setRetransmittedPacketsCount(int); + method @NonNull public android.telecom.BluetoothCallQualityReport.Builder setRssiDbm(int); + method @NonNull public android.telecom.BluetoothCallQualityReport.Builder setSentTimestampMillis(long); + method @NonNull public android.telecom.BluetoothCallQualityReport.Builder setSnrDb(int); + } + public final class Call { method @Deprecated public void addListener(android.telecom.Call.Listener); method public void enterBackgroundAudioProcessing(); diff --git a/core/api/test-current.txt b/core/api/test-current.txt index e392ed7b6e2d..51edd03515e1 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -18,6 +18,7 @@ package android { field public static final String MANAGE_CRATES = "android.permission.MANAGE_CRATES"; field public static final String MANAGE_NOTIFICATION_LISTENERS = "android.permission.MANAGE_NOTIFICATION_LISTENERS"; field public static final String MANAGE_ROLLBACKS = "android.permission.MANAGE_ROLLBACKS"; + field public static final String MODIFY_REFRESH_RATE_SWITCHING_TYPE = "android.permission.MODIFY_REFRESH_RATE_SWITCHING_TYPE"; field public static final String NETWORK_SETTINGS = "android.permission.NETWORK_SETTINGS"; field public static final String NETWORK_STACK = "android.permission.NETWORK_STACK"; field public static final String OVERRIDE_DISPLAY_MODE_REQUESTS = "android.permission.OVERRIDE_DISPLAY_MODE_REQUESTS"; @@ -743,9 +744,14 @@ package android.hardware.display { } public final class DisplayManager { + method @RequiresPermission(android.Manifest.permission.MODIFY_REFRESH_RATE_SWITCHING_TYPE) public int getRefreshRateSwitchingType(); method public boolean isMinimalPostProcessingRequested(int); + method @RequiresPermission(android.Manifest.permission.MODIFY_REFRESH_RATE_SWITCHING_TYPE) public void setRefreshRateSwitchingType(int); method @RequiresPermission(android.Manifest.permission.OVERRIDE_DISPLAY_MODE_REQUESTS) public void setShouldAlwaysRespectAppRequestedMode(boolean); method @RequiresPermission(android.Manifest.permission.OVERRIDE_DISPLAY_MODE_REQUESTS) public boolean shouldAlwaysRespectAppRequestedMode(); + field public static final int SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS = 2; // 0x2 + field public static final int SWITCHING_TYPE_NONE = 0; // 0x0 + field public static final int SWITCHING_TYPE_WITHIN_GROUPS = 1; // 0x1 field public static final int VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS = 512; // 0x200 field public static final int VIRTUAL_DISPLAY_FLAG_TRUSTED = 1024; // 0x400 } diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 5f2d3a2b997b..433182b38efd 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -85,6 +85,7 @@ import android.database.sqlite.SQLiteDebug.DbStats; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.HardwareRenderer; +import android.graphics.Typeface; import android.hardware.display.DisplayManagerGlobal; import android.inputmethodservice.InputMethodService; import android.media.MediaFrameworkInitializer; @@ -117,6 +118,7 @@ import android.os.Process; import android.os.RemoteCallback; import android.os.RemoteException; import android.os.ServiceManager; +import android.os.SharedMemory; import android.os.StatsFrameworkInitializer; import android.os.StatsServiceManager; import android.os.StrictMode; @@ -844,6 +846,8 @@ public final class ActivityThread extends ClientTransactionHandler { long[] disabledCompatChanges; + SharedMemory mSerializedSystemFontMap; + @Override public String toString() { return "AppBindData{appInfo=" + appInfo + "}"; @@ -1054,7 +1058,8 @@ public final class ActivityThread extends ClientTransactionHandler { boolean isRestrictedBackupMode, boolean persistent, Configuration config, CompatibilityInfo compatInfo, Map services, Bundle coreSettings, String buildSerial, AutofillOptions autofillOptions, - ContentCaptureOptions contentCaptureOptions, long[] disabledCompatChanges) { + ContentCaptureOptions contentCaptureOptions, long[] disabledCompatChanges, + SharedMemory serializedSystemFontMap) { if (services != null) { if (false) { // Test code to make sure the app could see the passed-in services. @@ -1103,6 +1108,7 @@ public final class ActivityThread extends ClientTransactionHandler { data.autofillOptions = autofillOptions; data.contentCaptureOptions = contentCaptureOptions; data.disabledCompatChanges = disabledCompatChanges; + data.mSerializedSystemFontMap = serializedSystemFontMap; sendMessage(H.BIND_APPLICATION, data); } @@ -6380,6 +6386,15 @@ public final class ActivityThread extends ClientTransactionHandler { */ LocaleList.setDefault(data.config.getLocales()); + if (Typeface.ENABLE_LAZY_TYPEFACE_INITIALIZATION) { + try { + Typeface.setSystemFontMap(data.mSerializedSystemFontMap); + } catch (IOException | ErrnoException e) { + Slog.e(TAG, "Failed to parse serialized system font map"); + Typeface.loadPreinstalledSystemFontMap(); + } + } + synchronized (mResourcesManager) { /* * Update the system configuration since its preloaded and might not diff --git a/core/java/android/app/IApplicationThread.aidl b/core/java/android/app/IApplicationThread.aidl index 22ca42eb9cf4..890e957bdff4 100644 --- a/core/java/android/app/IApplicationThread.aidl +++ b/core/java/android/app/IApplicationThread.aidl @@ -43,6 +43,7 @@ import android.os.IInterface; import android.os.ParcelFileDescriptor; import android.os.PersistableBundle; import android.os.RemoteCallback; +import android.os.SharedMemory; import com.android.internal.app.IVoiceInteractor; import com.android.internal.content.ReferrerIntent; @@ -75,7 +76,8 @@ oneway interface IApplicationThread { boolean restrictedBackupMode, boolean persistent, in Configuration config, in CompatibilityInfo compatInfo, in Map services, in Bundle coreSettings, in String buildSerial, in AutofillOptions autofillOptions, - in ContentCaptureOptions contentCaptureOptions, in long[] disabledCompatChanges); + in ContentCaptureOptions contentCaptureOptions, in long[] disabledCompatChanges, + in SharedMemory serializedSystemFontMap); void runIsolatedEntryPoint(in String entryPoint, in String[] entryPointArgs); void scheduleExit(); void scheduleServiceArgs(IBinder token, in ParceledListSlice args); diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index d5977e7711fd..a1135809fd4c 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -5932,8 +5932,7 @@ public class Notification implements Parcelable } int color; - int background = mContext.getColor( - com.android.internal.R.color.notification_material_background_color); + int background = obtainBackgroundColor(); if (rawColor == COLOR_DEFAULT) { ensureColors(p); color = ContrastColorUtil.resolveDefaultColor(mContext, background, mInNightMode); @@ -5966,8 +5965,7 @@ public class Notification implements Parcelable if (mNeutralColor != COLOR_INVALID) { return mNeutralColor; } - int background = mContext.getColor( - com.android.internal.R.color.notification_material_background_color); + int background = obtainBackgroundColor(); mNeutralColor = ContrastColorUtil.resolveDefaultColor(mContext, background, mInNightMode); if (Color.alpha(mNeutralColor) < 255) { @@ -6118,6 +6116,21 @@ public class Notification implements Parcelable return mN; } + private @ColorInt int obtainBackgroundColor() { + int defaultColor = mInNightMode ? Color.BLACK : Color.WHITE; + Resources.Theme theme = mContext.getTheme(); + if (theme == null) { + return defaultColor; + } + TypedArray ta = theme.obtainStyledAttributes(new int[]{R.attr.colorBackground}); + if (ta == null) { + return defaultColor; + } + int background = ta.getColor(0, defaultColor); + ta.recycle(); + return background; + } + /** * Apply this Builder to an existing {@link Notification} object. * @@ -6251,8 +6264,7 @@ public class Notification implements Parcelable private int resolveBackgroundColor(StandardTemplateParams p) { int backgroundColor = getBackgroundColor(p); if (backgroundColor == COLOR_DEFAULT) { - backgroundColor = mContext.getColor( - com.android.internal.R.color.notification_material_background_color); + backgroundColor = obtainBackgroundColor(); } return backgroundColor; } diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java index 7287acdd0241..392d6fbe53d6 100644 --- a/core/java/android/app/SystemServiceRegistry.java +++ b/core/java/android/app/SystemServiceRegistry.java @@ -194,6 +194,7 @@ import android.telephony.TelephonyRegistryManager; import android.util.ArrayMap; import android.util.Log; import android.util.Slog; +import android.uwb.UwbManager; import android.view.ContextThemeWrapper; import android.view.LayoutInflater; import android.view.WindowManager; @@ -732,6 +733,14 @@ public final class SystemServiceRegistry { return new SerialManager(ctx, ISerialManager.Stub.asInterface(b)); }}); + registerService(Context.UWB_SERVICE, UwbManager.class, + new CachedServiceFetcher<UwbManager>() { + @Override + public UwbManager createService(ContextImpl ctx) { + return UwbManager.getInstance(); + } + }); + registerService(Context.VIBRATOR_SERVICE, Vibrator.class, new CachedServiceFetcher<Vibrator>() { @Override diff --git a/core/java/android/bluetooth/BluetoothLeAudio.java b/core/java/android/bluetooth/BluetoothLeAudio.java new file mode 100644 index 000000000000..3f00fa6f4181 --- /dev/null +++ b/core/java/android/bluetooth/BluetoothLeAudio.java @@ -0,0 +1,451 @@ +/* + * Copyright 2020 HIMSA II K/S - www.himsa.com. + * Represented by EHIMA - www.ehima.com + * + * 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.bluetooth; + +import android.Manifest; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.RequiresPermission; +import android.annotation.SdkConstant; +import android.annotation.SdkConstant.SdkConstantType; +import android.content.Context; +import android.os.Binder; +import android.os.IBinder; +import android.os.RemoteException; +import android.util.CloseGuard; +import android.util.Log; + +import java.util.ArrayList; +import java.util.List; + +/** + * This class provides the public APIs to control the LeAudio profile. + * + * <p>BluetoothLeAudio is a proxy object for controlling the Bluetooth LE Audio + * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get + * the BluetoothLeAudio proxy object. + * + * <p> Android only supports one set of connected Bluetooth LeAudio device at a time. Each + * method is protected with its appropriate permission. + */ +public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable { + private static final String TAG = "BluetoothLeAudio"; + private static final boolean DBG = false; + private static final boolean VDBG = false; + + private CloseGuard mCloseGuard; + + /** + * Intent used to broadcast the change in connection state of the LeAudio + * profile. Please note that in the binaural case, there will be two different LE devices for + * the left and right side and each device will have their own connection state changes. + * + * <p>This intent will have 3 extras: + * <ul> + * <li> {@link #EXTRA_STATE} - The current state of the profile. </li> + * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.</li> + * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li> + * </ul> + * + * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of + * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING}, + * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}. + * + * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to + * receive. + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_LE_AUDIO_CONNECTION_STATE_CHANGED = + "android.bluetooth.action.LE_AUDIO_CONNECTION_STATE_CHANGED"; + + /** + * Intent used to broadcast the selection of a connected device as active. + * + * <p>This intent will have one extra: + * <ul> + * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. It can + * be null if no device is active. </li> + * </ul> + * + * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to + * receive. + * + * @hide + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_LE_AUDIO_ACTIVE_DEVICE_CHANGED = + "android.bluetooth.action.LE_AUDIO_ACTIVE_DEVICE_CHANGED"; + + /** + * This represents an invalid group ID. + * + * @hide + */ + public static final int GROUP_ID_INVALID = IBluetoothLeAudio.LE_AUDIO_GROUP_ID_INVALID; + + private BluetoothAdapter mAdapter; + private final BluetoothProfileConnector<IBluetoothLeAudio> mProfileConnector = + new BluetoothProfileConnector(this, BluetoothProfile.LE_AUDIO, "BluetoothLeAudio", + IBluetoothLeAudio.class.getName()) { + @Override + public IBluetoothLeAudio getServiceInterface(IBinder service) { + return IBluetoothLeAudio.Stub.asInterface(Binder.allowBlocking(service)); + } + }; + + /** + * Create a BluetoothLeAudio proxy object for interacting with the local + * Bluetooth LeAudio service. + */ + /*package*/ BluetoothLeAudio(Context context, ServiceListener listener) { + mAdapter = BluetoothAdapter.getDefaultAdapter(); + mProfileConnector.connect(context, listener); + mCloseGuard = new CloseGuard(); + mCloseGuard.open("close"); + } + + /** + * @hide + */ + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + public void close() { + mProfileConnector.disconnect(); + } + + private IBluetoothLeAudio getService() { + return mProfileConnector.getService(); + } + + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + protected void finalize() { + if (mCloseGuard != null) { + mCloseGuard.warnIfOpen(); + } + close(); + } + + /** + * Initiate connection to a profile of the remote bluetooth device. + * + * <p> This API returns false in scenarios like the profile on the + * device is already connected or Bluetooth is not turned on. + * When this API returns true, it is guaranteed that + * connection state intent for the profile will be broadcasted with + * the state. Users can get the connection state of the profile + * from this intent. + * + * + * @param device Remote Bluetooth Device + * @return false on immediate error, true otherwise + * @hide + */ + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + public boolean connect(@Nullable BluetoothDevice device) { + if (DBG) log("connect(" + device + ")"); + try { + final IBluetoothLeAudio service = getService(); + if (service != null && mAdapter.isEnabled() && isValidDevice(device)) { + return service.connect(device); + } + if (service == null) Log.w(TAG, "Proxy not attached to service"); + return false; + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return false; + } + } + + /** + * Initiate disconnection from a profile + * + * <p> This API will return false in scenarios like the profile on the + * Bluetooth device is not in connected state etc. When this API returns, + * true, it is guaranteed that the connection state change + * intent will be broadcasted with the state. Users can get the + * disconnection state of the profile from this intent. + * + * <p> If the disconnection is initiated by a remote device, the state + * will transition from {@link #STATE_CONNECTED} to + * {@link #STATE_DISCONNECTED}. If the disconnect is initiated by the + * host (local) device the state will transition from + * {@link #STATE_CONNECTED} to state {@link #STATE_DISCONNECTING} to + * state {@link #STATE_DISCONNECTED}. The transition to + * {@link #STATE_DISCONNECTING} can be used to distinguish between the + * two scenarios. + * + * + * @param device Remote Bluetooth Device + * @return false on immediate error, true otherwise + * @hide + */ + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + public boolean disconnect(@Nullable BluetoothDevice device) { + if (DBG) log("disconnect(" + device + ")"); + try { + final IBluetoothLeAudio service = getService(); + if (service != null && mAdapter.isEnabled() && isValidDevice(device)) { + return service.disconnect(device); + } + if (service == null) Log.w(TAG, "Proxy not attached to service"); + return false; + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return false; + } + } + + /** + * {@inheritDoc} + */ + @Override + public @NonNull List<BluetoothDevice> getConnectedDevices() { + if (VDBG) log("getConnectedDevices()"); + try { + final IBluetoothLeAudio service = getService(); + if (service != null && mAdapter.isEnabled()) { + return service.getConnectedDevices(); + } + if (service == null) Log.w(TAG, "Proxy not attached to service"); + return new ArrayList<BluetoothDevice>(); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return new ArrayList<BluetoothDevice>(); + } + } + + /** + * {@inheritDoc} + */ + @Override + public @NonNull List<BluetoothDevice> getDevicesMatchingConnectionStates( + @NonNull int[] states) { + if (VDBG) log("getDevicesMatchingStates()"); + try { + final IBluetoothLeAudio service = getService(); + if (service != null && mAdapter.isEnabled()) { + return service.getDevicesMatchingConnectionStates(states); + } + if (service == null) Log.w(TAG, "Proxy not attached to service"); + return new ArrayList<BluetoothDevice>(); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return new ArrayList<BluetoothDevice>(); + } + } + + /** + * {@inheritDoc} + */ + @Override + @RequiresPermission(Manifest.permission.BLUETOOTH) + public @BtProfileState int getConnectionState(@NonNull BluetoothDevice device) { + if (VDBG) log("getState(" + device + ")"); + try { + final IBluetoothLeAudio service = getService(); + if (service != null && mAdapter.isEnabled() + && isValidDevice(device)) { + return service.getConnectionState(device); + } + if (service == null) Log.w(TAG, "Proxy not attached to service"); + return BluetoothProfile.STATE_DISCONNECTED; + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return BluetoothProfile.STATE_DISCONNECTED; + } + } + + /** + * Select a connected device as active. + * + * The active device selection is per profile. An active device's + * purpose is profile-specific. For example, LeAudio audio + * streaming is to the active LeAudio device. If a remote device + * is not connected, it cannot be selected as active. + * + * <p> This API returns false in scenarios like the profile on the + * device is not connected or Bluetooth is not turned on. + * When this API returns true, it is guaranteed that the + * {@link #ACTION_LEAUDIO_ACTIVE_DEVICE_CHANGED} intent will be broadcasted + * with the active device. + * + * + * @param device the remote Bluetooth device. Could be null to clear + * the active device and stop streaming audio to a Bluetooth device. + * @return false on immediate error, true otherwise + * @hide + */ + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + public boolean setActiveDevice(@Nullable BluetoothDevice device) { + if (DBG) log("setActiveDevice(" + device + ")"); + try { + final IBluetoothLeAudio service = getService(); + if (service != null && mAdapter.isEnabled() + && ((device == null) || isValidDevice(device))) { + service.setActiveDevice(device); + return true; + } + if (service == null) Log.w(TAG, "Proxy not attached to service"); + return false; + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return false; + } + } + + /** + * Get the connected LeAudio devices that are active + * + * @return the list of active devices. Returns empty list on error. + * @hide + */ + @NonNull + @RequiresPermission(Manifest.permission.BLUETOOTH) + public List<BluetoothDevice> getActiveDevices() { + if (VDBG) log("getActiveDevices()"); + try { + final IBluetoothLeAudio service = getService(); + if (service != null && mAdapter.isEnabled()) { + return service.getActiveDevices(); + } + if (service == null) Log.w(TAG, "Proxy not attached to service"); + return new ArrayList<>(); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return new ArrayList<>(); + } + } + + /** + * Get device group id. Devices with same group id belong to same group (i.e left and right + * earbud) + * @param device LE Audio capable device + * @return group id that this device currently belongs to + * @hide + */ + @RequiresPermission(Manifest.permission.BLUETOOTH) + public int getGroupId(@NonNull BluetoothDevice device) { + if (VDBG) log("getGroupId()"); + try { + final IBluetoothLeAudio service = getService(); + if (service != null && mAdapter.isEnabled()) { + return service.getGroupId(device); + } + if (service == null) Log.w(TAG, "Proxy not attached to service"); + return GROUP_ID_INVALID; + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return GROUP_ID_INVALID; + } + } + + /** + * Set connection policy of the profile + * + * <p> The device should already be paired. + * Connection policy can be one of {@link #CONNECTION_POLICY_ALLOWED}, + * {@link #CONNECTION_POLICY_FORBIDDEN}, {@link #CONNECTION_POLICY_UNKNOWN} + * + * @param device Paired bluetooth device + * @param connectionPolicy is the connection policy to set to for this profile + * @return true if connectionPolicy is set, false on error + * @hide + */ + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + public boolean setConnectionPolicy(@NonNull BluetoothDevice device, + @ConnectionPolicy int connectionPolicy) { + if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); + try { + final IBluetoothLeAudio service = getService(); + if (service != null && mAdapter.isEnabled() + && isValidDevice(device)) { + if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_FORBIDDEN + && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) { + return false; + } + return service.setConnectionPolicy(device, connectionPolicy); + } + if (service == null) Log.w(TAG, "Proxy not attached to service"); + return false; + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return false; + } + } + + /** + * Get the connection policy of the profile. + * + * <p> The connection policy can be any of: + * {@link #CONNECTION_POLICY_ALLOWED}, {@link #CONNECTION_POLICY_FORBIDDEN}, + * {@link #CONNECTION_POLICY_UNKNOWN} + * + * @param device Bluetooth device + * @return connection policy of the device + * @hide + */ + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + public @ConnectionPolicy int getConnectionPolicy(@Nullable BluetoothDevice device) { + if (VDBG) log("getConnectionPolicy(" + device + ")"); + try { + final IBluetoothLeAudio service = getService(); + if (service != null && mAdapter.isEnabled() + && isValidDevice(device)) { + return service.getConnectionPolicy(device); + } + if (service == null) Log.w(TAG, "Proxy not attached to service"); + return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; + } + } + + + /** + * Helper for converting a state to a string. + * + * For debug use only - strings are not internationalized. + * + * @hide + */ + public static String stateToString(int state) { + switch (state) { + case STATE_DISCONNECTED: + return "disconnected"; + case STATE_CONNECTING: + return "connecting"; + case STATE_CONNECTED: + return "connected"; + case STATE_DISCONNECTING: + return "disconnecting"; + default: + return "<unknown state " + state + ">"; + } + } + + private boolean isValidDevice(@Nullable BluetoothDevice device) { + if (device == null) return false; + + if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true; + return false; + } + + private static void log(String msg) { + Log.d(TAG, msg); + } +} diff --git a/core/java/android/bluetooth/BluetoothProfile.java b/core/java/android/bluetooth/BluetoothProfile.java index db851c4f33b9..c31b04e81456 100644 --- a/core/java/android/bluetooth/BluetoothProfile.java +++ b/core/java/android/bluetooth/BluetoothProfile.java @@ -207,12 +207,19 @@ public interface BluetoothProfile { int HEARING_AID = 21; /** + * LE Audio Device + * + * @hide + */ + int LE_AUDIO = 22; + + /** * Max profile ID. This value should be updated whenever a new profile is added to match * the largest value assigned to a profile. * * @hide */ - int MAX_PROFILE_ID = 21; + int MAX_PROFILE_ID = 22; /** * Default priority for devices that we try to auto-connect to and diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 8f92bf1e3253..d920fb3e97e6 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -3544,6 +3544,7 @@ public abstract class Context { LIGHTS_SERVICE, //@hide: PEOPLE_SERVICE, //@hide: DEVICE_STATE_SERVICE, + UWB_SERVICE, }) @Retention(RetentionPolicy.SOURCE) public @interface ServiceName {} @@ -5260,6 +5261,15 @@ public abstract class Context { /** * Use with {@link #getSystemService(String)} to retrieve a + * {@link android.uwb.UwbManager}. + * + * @see #getSystemService(String) + * @hide + */ + public static final String UWB_SERVICE = "uwb"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a * {@link android.app.DreamManager} for controlling Dream states. * * @see #getSystemService(String) diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java index b0c47bdfd864..56da3cb0eb02 100644 --- a/core/java/android/content/ContextWrapper.java +++ b/core/java/android/content/ContextWrapper.java @@ -270,7 +270,7 @@ public class ContextWrapper extends Context { } @Override - public File getExternalFilesDir(String type) { + public @Nullable File getExternalFilesDir(@Nullable String type) { return mBase.getExternalFilesDir(type); } @@ -300,7 +300,7 @@ public class ContextWrapper extends Context { } @Override - public File getExternalCacheDir() { + public @Nullable File getExternalCacheDir() { return mBase.getExternalCacheDir(); } @@ -322,7 +322,7 @@ public class ContextWrapper extends Context { /** @hide **/ @Override - public File getPreloadsFileCache() { + public @Nullable File getPreloadsFileCache() { return mBase.getPreloadsFileCache(); } @@ -333,7 +333,7 @@ public class ContextWrapper extends Context { @Override public SQLiteDatabase openOrCreateDatabase(String name, int mode, CursorFactory factory, - DatabaseErrorHandler errorHandler) { + @Nullable DatabaseErrorHandler errorHandler) { return mBase.openOrCreateDatabase(name, mode, factory, errorHandler); } @@ -412,7 +412,7 @@ public class ContextWrapper extends Context { /** @hide **/ public void startActivityForResult( - String who, Intent intent, int requestCode, Bundle options) { + String who, Intent intent, int requestCode, @Nullable Bundle options) { mBase.startActivityForResult(who, intent, requestCode, options); } @@ -422,13 +422,13 @@ public class ContextWrapper extends Context { } @Override - public void startActivity(Intent intent, Bundle options) { + public void startActivity(Intent intent, @Nullable Bundle options) { mBase.startActivity(intent, options); } /** @hide */ @Override - public void startActivityAsUser(Intent intent, Bundle options, UserHandle user) { + public void startActivityAsUser(Intent intent, @Nullable Bundle options, UserHandle user) { mBase.startActivityAsUser(intent, options, user); } @@ -438,13 +438,14 @@ public class ContextWrapper extends Context { } @Override - public void startActivities(Intent[] intents, Bundle options) { + public void startActivities(Intent[] intents, @Nullable Bundle options) { mBase.startActivities(intents, options); } /** @hide */ @Override - public int startActivitiesAsUser(Intent[] intents, Bundle options, UserHandle userHandle) { + public int startActivitiesAsUser(Intent[] intents, @Nullable Bundle options, + UserHandle userHandle) { return mBase.startActivitiesAsUser(intents, options, userHandle); } @@ -472,7 +473,7 @@ public class ContextWrapper extends Context { } @Override - public void sendBroadcast(Intent intent, String receiverPermission) { + public void sendBroadcast(Intent intent, @Nullable String receiverPermission) { mBase.sendBroadcast(intent, receiverPermission); } @@ -493,27 +494,28 @@ public class ContextWrapper extends Context { /** @hide */ @SystemApi @Override - public void sendBroadcast(Intent intent, String receiverPermission, Bundle options) { + public void sendBroadcast(Intent intent, @Nullable String receiverPermission, + @Nullable Bundle options) { mBase.sendBroadcast(intent, receiverPermission, options); } /** @hide */ @Override - public void sendBroadcast(Intent intent, String receiverPermission, int appOp) { + public void sendBroadcast(Intent intent, @Nullable String receiverPermission, int appOp) { mBase.sendBroadcast(intent, receiverPermission, appOp); } @Override public void sendOrderedBroadcast(Intent intent, - String receiverPermission) { + @Nullable String receiverPermission) { mBase.sendOrderedBroadcast(intent, receiverPermission); } @Override public void sendOrderedBroadcast( - Intent intent, String receiverPermission, BroadcastReceiver resultReceiver, - Handler scheduler, int initialCode, String initialData, - Bundle initialExtras) { + Intent intent, @Nullable String receiverPermission, + @Nullable BroadcastReceiver resultReceiver, @Nullable Handler scheduler, + int initialCode, @Nullable String initialData, @Nullable Bundle initialExtras) { mBase.sendOrderedBroadcast(intent, receiverPermission, resultReceiver, scheduler, initialCode, initialData, initialExtras); @@ -523,10 +525,9 @@ public class ContextWrapper extends Context { @SystemApi @Override public void sendOrderedBroadcast( - Intent intent, String receiverPermission, Bundle options, - BroadcastReceiver resultReceiver, - Handler scheduler, int initialCode, String initialData, - Bundle initialExtras) { + Intent intent, @Nullable String receiverPermission, @Nullable Bundle options, + @Nullable BroadcastReceiver resultReceiver, @Nullable Handler scheduler, + int initialCode, @Nullable String initialData, @Nullable Bundle initialExtras) { mBase.sendOrderedBroadcast(intent, receiverPermission, options, resultReceiver, scheduler, initialCode, initialData, initialExtras); @@ -535,9 +536,9 @@ public class ContextWrapper extends Context { /** @hide */ @Override public void sendOrderedBroadcast( - Intent intent, String receiverPermission, int appOp, BroadcastReceiver resultReceiver, - Handler scheduler, int initialCode, String initialData, - Bundle initialExtras) { + Intent intent, @Nullable String receiverPermission, int appOp, + @Nullable BroadcastReceiver resultReceiver, @Nullable Handler scheduler, + int initialCode, @Nullable String initialData, @Nullable Bundle initialExtras) { mBase.sendOrderedBroadcast(intent, receiverPermission, appOp, resultReceiver, scheduler, initialCode, initialData, initialExtras); @@ -557,21 +558,22 @@ public class ContextWrapper extends Context { /** @hide */ @Override public void sendBroadcastAsUser(Intent intent, UserHandle user, - String receiverPermission, Bundle options) { + @Nullable String receiverPermission, @Nullable Bundle options) { mBase.sendBroadcastAsUser(intent, user, receiverPermission, options); } /** @hide */ @Override public void sendBroadcastAsUser(Intent intent, UserHandle user, - String receiverPermission, int appOp) { + @Nullable String receiverPermission, int appOp) { mBase.sendBroadcastAsUser(intent, user, receiverPermission, appOp); } @Override public void sendOrderedBroadcastAsUser(Intent intent, UserHandle user, - String receiverPermission, BroadcastReceiver resultReceiver, Handler scheduler, - int initialCode, String initialData, Bundle initialExtras) { + @Nullable String receiverPermission, @Nullable BroadcastReceiver resultReceiver, + @Nullable Handler scheduler, int initialCode, @Nullable String initialData, + @Nullable Bundle initialExtras) { mBase.sendOrderedBroadcastAsUser(intent, user, receiverPermission, resultReceiver, scheduler, initialCode, initialData, initialExtras); } @@ -579,8 +581,9 @@ public class ContextWrapper extends Context { /** @hide */ @Override public void sendOrderedBroadcastAsUser(Intent intent, UserHandle user, - String receiverPermission, int appOp, BroadcastReceiver resultReceiver, - Handler scheduler, int initialCode, String initialData, Bundle initialExtras) { + @Nullable String receiverPermission, int appOp, + @Nullable BroadcastReceiver resultReceiver, @Nullable Handler scheduler, + int initialCode, @Nullable String initialData, @Nullable Bundle initialExtras) { mBase.sendOrderedBroadcastAsUser(intent, user, receiverPermission, appOp, resultReceiver, scheduler, initialCode, initialData, initialExtras); } @@ -588,8 +591,9 @@ public class ContextWrapper extends Context { /** @hide */ @Override public void sendOrderedBroadcastAsUser(Intent intent, UserHandle user, - String receiverPermission, int appOp, Bundle options, BroadcastReceiver resultReceiver, - Handler scheduler, int initialCode, String initialData, Bundle initialExtras) { + @Nullable String receiverPermission, int appOp, @Nullable Bundle options, + @Nullable BroadcastReceiver resultReceiver, @Nullable Handler scheduler, + int initialCode, @Nullable String initialData, @Nullable Bundle initialExtras) { mBase.sendOrderedBroadcastAsUser(intent, user, receiverPermission, appOp, options, resultReceiver, scheduler, initialCode, initialData, initialExtras); } @@ -621,10 +625,9 @@ public class ContextWrapper extends Context { @Override @Deprecated - public void sendStickyOrderedBroadcast( - Intent intent, BroadcastReceiver resultReceiver, - Handler scheduler, int initialCode, String initialData, - Bundle initialExtras) { + public void sendStickyOrderedBroadcast(Intent intent, + @Nullable BroadcastReceiver resultReceiver, @Nullable Handler scheduler, + int initialCode, @Nullable String initialData, @Nullable Bundle initialExtras) { mBase.sendStickyOrderedBroadcast(intent, resultReceiver, scheduler, initialCode, initialData, initialExtras); @@ -645,16 +648,17 @@ public class ContextWrapper extends Context { /** @hide */ @Override @Deprecated - public void sendStickyBroadcastAsUser(Intent intent, UserHandle user, Bundle options) { + public void sendStickyBroadcastAsUser(Intent intent, UserHandle user, + @Nullable Bundle options) { mBase.sendStickyBroadcastAsUser(intent, user, options); } @Override @Deprecated public void sendStickyOrderedBroadcastAsUser(Intent intent, - UserHandle user, BroadcastReceiver resultReceiver, - Handler scheduler, int initialCode, String initialData, - Bundle initialExtras) { + UserHandle user, @Nullable BroadcastReceiver resultReceiver, + @Nullable Handler scheduler, int initialCode, @Nullable String initialData, + @Nullable Bundle initialExtras) { mBase.sendStickyOrderedBroadcastAsUser(intent, user, resultReceiver, scheduler, initialCode, initialData, initialExtras); } @@ -666,29 +670,26 @@ public class ContextWrapper extends Context { } @Override - public Intent registerReceiver( - BroadcastReceiver receiver, IntentFilter filter) { + public Intent registerReceiver(@Nullable BroadcastReceiver receiver, IntentFilter filter) { return mBase.registerReceiver(receiver, filter); } @Override - public Intent registerReceiver( - BroadcastReceiver receiver, IntentFilter filter, int flags) { + public Intent registerReceiver(@Nullable BroadcastReceiver receiver, IntentFilter filter, + int flags) { return mBase.registerReceiver(receiver, filter, flags); } @Override - public Intent registerReceiver( - BroadcastReceiver receiver, IntentFilter filter, - String broadcastPermission, Handler scheduler) { + public Intent registerReceiver(@Nullable BroadcastReceiver receiver, IntentFilter filter, + @Nullable String broadcastPermission, @Nullable Handler scheduler) { return mBase.registerReceiver(receiver, filter, broadcastPermission, scheduler); } @Override - public Intent registerReceiver( - BroadcastReceiver receiver, IntentFilter filter, - String broadcastPermission, Handler scheduler, int flags) { + public Intent registerReceiver(@Nullable BroadcastReceiver receiver, IntentFilter filter, + @Nullable String broadcastPermission, @Nullable Handler scheduler, int flags) { return mBase.registerReceiver(receiver, filter, broadcastPermission, scheduler, flags); } @@ -706,9 +707,9 @@ public class ContextWrapper extends Context { /** @hide */ @Override @UnsupportedAppUsage - public Intent registerReceiverAsUser( - BroadcastReceiver receiver, UserHandle user, IntentFilter filter, - String broadcastPermission, Handler scheduler) { + public Intent registerReceiverAsUser(@Nullable BroadcastReceiver receiver, UserHandle user, + IntentFilter filter, @Nullable String broadcastPermission, + @Nullable Handler scheduler) { return mBase.registerReceiverAsUser(receiver, user, filter, broadcastPermission, scheduler); } @@ -719,12 +720,12 @@ public class ContextWrapper extends Context { } @Override - public ComponentName startService(Intent service) { + public @Nullable ComponentName startService(Intent service) { return mBase.startService(service); } @Override - public ComponentName startForegroundService(Intent service) { + public @Nullable ComponentName startForegroundService(Intent service) { return mBase.startForegroundService(service); } @@ -736,14 +737,14 @@ public class ContextWrapper extends Context { /** @hide */ @Override @UnsupportedAppUsage - public ComponentName startServiceAsUser(Intent service, UserHandle user) { + public @Nullable ComponentName startServiceAsUser(Intent service, UserHandle user) { return mBase.startServiceAsUser(service, user); } /** @hide */ @Override @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - public ComponentName startForegroundServiceAsUser(Intent service, UserHandle user) { + public @Nullable ComponentName startForegroundServiceAsUser(Intent service, UserHandle user) { return mBase.startForegroundServiceAsUser(service, user); } @@ -797,12 +798,12 @@ public class ContextWrapper extends Context { @Override public boolean startInstrumentation(ComponentName className, - String profileFile, Bundle arguments) { + @Nullable String profileFile, @Nullable Bundle arguments) { return mBase.startInstrumentation(className, profileFile, arguments); } @Override - public Object getSystemService(String name) { + public @Nullable Object getSystemService(String name) { return mBase.getSystemService(name); } @@ -839,18 +840,18 @@ public class ContextWrapper extends Context { @Override public void enforcePermission( - String permission, int pid, int uid, String message) { + String permission, int pid, int uid, @Nullable String message) { mBase.enforcePermission(permission, pid, uid, message); } @Override - public void enforceCallingPermission(String permission, String message) { + public void enforceCallingPermission(String permission, @Nullable String message) { mBase.enforceCallingPermission(permission, message); } @Override public void enforceCallingOrSelfPermission( - String permission, String message) { + String permission, @Nullable String message) { mBase.enforceCallingOrSelfPermission(permission, message); } @@ -891,8 +892,8 @@ public class ContextWrapper extends Context { } @Override - public int checkUriPermission(Uri uri, String readPermission, - String writePermission, int pid, int uid, int modeFlags) { + public int checkUriPermission(@Nullable Uri uri, @Nullable String readPermission, + @Nullable String writePermission, int pid, int uid, int modeFlags) { return mBase.checkUriPermission(uri, readPermission, writePermission, pid, uid, modeFlags); } @@ -917,8 +918,8 @@ public class ContextWrapper extends Context { @Override public void enforceUriPermission( - Uri uri, String readPermission, String writePermission, - int pid, int uid, int modeFlags, String message) { + @Nullable Uri uri, @Nullable String readPermission, @Nullable String writePermission, + int pid, int uid, int modeFlags, @Nullable String message) { mBase.enforceUriPermission( uri, readPermission, writePermission, pid, uid, modeFlags, message); @@ -1063,7 +1064,7 @@ public class ContextWrapper extends Context { * @hide */ @Override - public IBinder getActivityToken() { + public @Nullable IBinder getActivityToken() { return mBase.getActivityToken(); } @@ -1071,7 +1072,7 @@ public class ContextWrapper extends Context { * @hide */ @Override - public IBinder getWindowContextToken() { + public @Nullable IBinder getWindowContextToken() { return mBase != null ? mBase.getWindowContextToken() : null; } @@ -1079,8 +1080,8 @@ public class ContextWrapper extends Context { * @hide */ @Override - public IServiceConnection getServiceDispatcher(ServiceConnection conn, Handler handler, - int flags) { + public @Nullable IServiceConnection getServiceDispatcher(ServiceConnection conn, + Handler handler, int flags) { return mBase.getServiceDispatcher(conn, handler, flags); } @@ -1142,7 +1143,7 @@ public class ContextWrapper extends Context { * @hide */ @Override - public ContentCaptureOptions getContentCaptureOptions() { + public @Nullable ContentCaptureOptions getContentCaptureOptions() { return mBase == null ? null : mBase.getContentCaptureOptions(); } @@ -1151,7 +1152,7 @@ public class ContextWrapper extends Context { */ @TestApi @Override - public void setContentCaptureOptions(ContentCaptureOptions options) { + public void setContentCaptureOptions(@Nullable ContentCaptureOptions options) { if (mBase != null) { mBase.setContentCaptureOptions(options); } diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 044b3b2e8284..19e4d148aee8 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -98,6 +98,11 @@ import java.util.Set; * packages that are currently installed on the device. * * You can find this class through {@link Context#getPackageManager}. + * + * <p class="note"><strong>Note: </strong>If your app targets Android 11 (API level 30) or + * higher, the methods in this class each return a filtered list of apps. Learn more about how to + * <a href="/training/basics/intents/package-visibility">manage package visibility</a>. + * </p> */ public abstract class PackageManager { private static final String TAG = "PackageManager"; @@ -2810,6 +2815,15 @@ public abstract class PackageManager { /** * Feature for {@link #getSystemAvailableFeatures} and + * {@link #hasSystemFeature}: The device is capable of communicating with + * other devices via ultra wideband. + * @hide + */ + @SdkConstant(SdkConstantType.FEATURE) + public static final String FEATURE_UWB = "android.hardware.uwb"; + + /** + * Feature for {@link #getSystemAvailableFeatures} and * {@link #hasSystemFeature}: The device supports connecting to USB devices * as the USB host. */ diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java index ca5eeb1863c7..9bae1ff4b906 100644 --- a/core/java/android/hardware/display/DisplayManager.java +++ b/core/java/android/hardware/display/DisplayManager.java @@ -19,6 +19,7 @@ package android.hardware.display; import static android.view.Display.DEFAULT_DISPLAY; import android.Manifest; +import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; @@ -38,9 +39,12 @@ import android.util.SparseArray; import android.view.Display; import android.view.Surface; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.List; + /** * Manages the properties of attached displays. */ @@ -336,6 +340,40 @@ public final class DisplayManager { */ public static final int VIRTUAL_DISPLAY_FLAG_OWN_DISPLAY_GROUP = 1 << 11; + + /** @hide */ + @IntDef(prefix = {"SWITCHING_TYPE_"}, value = { + SWITCHING_TYPE_NONE, + SWITCHING_TYPE_WITHIN_GROUPS, + SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface SwitchingType {} + + /** + * No mode switching will happen. + * @hide + */ + @TestApi + public static final int SWITCHING_TYPE_NONE = 0; + + /** + * Allow only refresh rate switching between modes in the same configuration group. This way + * only switches without visual interruptions for the user will be allowed. + * @hide + */ + @TestApi + public static final int SWITCHING_TYPE_WITHIN_GROUPS = 1; + + /** + * Allow refresh rate switching between all refresh rates even if the switch with have visual + * interruptions for the user. + * @hide + */ + @TestApi + public static final int SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS = 2; + + /** @hide */ public DisplayManager(Context context) { mContext = context; @@ -875,6 +913,29 @@ public final class DisplayManager { } /** + * Sets the refresh rate switching type. + * This matches {@link android.provider.Settings.Secure.MATCH_CONTENT_FRAME_RATE} + * + * @hide + */ + @TestApi + @RequiresPermission(Manifest.permission.MODIFY_REFRESH_RATE_SWITCHING_TYPE) + public void setRefreshRateSwitchingType(@SwitchingType int newValue) { + mGlobal.setRefreshRateSwitchingType(newValue); + } + + /** + * Returns the refresh rate switching type. + * + * @hide + */ + @TestApi + @RequiresPermission(Manifest.permission.MODIFY_REFRESH_RATE_SWITCHING_TYPE) + @SwitchingType public int getRefreshRateSwitchingType() { + return mGlobal.getRefreshRateSwitchingType(); + } + + /** * Listens for changes in available display devices. */ public interface DisplayListener { diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java index 7b4889f0a1b3..77ae9471dfb0 100644 --- a/core/java/android/hardware/display/DisplayManagerGlobal.java +++ b/core/java/android/hardware/display/DisplayManagerGlobal.java @@ -725,6 +725,33 @@ public final class DisplayManagerGlobal { } } + /** + * Sets the refresh rate switching type. + * + * @hide + */ + public void setRefreshRateSwitchingType(@DisplayManager.SwitchingType int newValue) { + try { + mDm.setRefreshRateSwitchingType(newValue); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); + } + } + + /** + * Returns the refresh rate switching type. + * + * @hide + */ + @DisplayManager.SwitchingType + public int getRefreshRateSwitchingType() { + try { + return mDm.getRefreshRateSwitchingType(); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); + } + } + private final class DisplayManagerCallback extends IDisplayManagerCallback.Stub { @Override public void onDisplayEvent(int displayId, int event) { diff --git a/core/java/android/hardware/display/IDisplayManager.aidl b/core/java/android/hardware/display/IDisplayManager.aidl index 85da6424377a..a9f78fa03a6d 100644 --- a/core/java/android/hardware/display/IDisplayManager.aidl +++ b/core/java/android/hardware/display/IDisplayManager.aidl @@ -134,4 +134,10 @@ interface IDisplayManager { // battery etc. void setShouldAlwaysRespectAppRequestedMode(boolean enabled); boolean shouldAlwaysRespectAppRequestedMode(); + + // Sets the refresh rate switching type. + void setRefreshRateSwitchingType(int newValue); + + // Returns the refresh rate switching type. + int getRefreshRateSwitchingType(); } diff --git a/core/java/android/inputmethodservice/AbstractInputMethodService.java b/core/java/android/inputmethodservice/AbstractInputMethodService.java index 7cf0b10031ac..3cd13a212a4b 100644 --- a/core/java/android/inputmethodservice/AbstractInputMethodService.java +++ b/core/java/android/inputmethodservice/AbstractInputMethodService.java @@ -199,10 +199,11 @@ public abstract class AbstractInputMethodService extends Service * Dumps the internal state of IME to a protocol buffer output stream. * * @param proto ProtoOutputStream to dump data to. + * @param icProto {@link InputConnection} call data in proto format. * @hide */ @SuppressWarnings("HiddenAbstractMethod") - public abstract void dumpProtoInternal(ProtoOutputStream proto); + public abstract void dumpProtoInternal(ProtoOutputStream proto, ProtoOutputStream icProto); /** * Implement this to handle {@link android.os.Binder#dump Binder.dump()} diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java index 67e75d205f97..5576857d1f6b 100644 --- a/core/java/android/inputmethodservice/InputMethodService.java +++ b/core/java/android/inputmethodservice/InputMethodService.java @@ -25,6 +25,7 @@ import static android.inputmethodservice.InputMethodServiceProto.EXTRACTED_TOKEN import static android.inputmethodservice.InputMethodServiceProto.EXTRACT_VIEW_HIDDEN; import static android.inputmethodservice.InputMethodServiceProto.FULLSCREEN_APPLIED; import static android.inputmethodservice.InputMethodServiceProto.INPUT_BINDING; +import static android.inputmethodservice.InputMethodServiceProto.INPUT_CONNECTION_CALL; import static android.inputmethodservice.InputMethodServiceProto.INPUT_EDITOR_INFO; import static android.inputmethodservice.InputMethodServiceProto.INPUT_STARTED; import static android.inputmethodservice.InputMethodServiceProto.INPUT_VIEW_STARTED; @@ -742,7 +743,8 @@ public class InputMethodService extends AbstractInputMethodService { return; } ImeTracing.getInstance().triggerServiceDump( - "InputMethodService.InputMethodImpl#hideSoftInput", InputMethodService.this); + "InputMethodService.InputMethodImpl#hideSoftInput", InputMethodService.this, + null /* icProto */); final boolean wasVisible = isInputViewShown(); Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.hideSoftInput"); @@ -798,7 +800,8 @@ public class InputMethodService extends AbstractInputMethodService { } Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.showSoftInput"); ImeTracing.getInstance().triggerServiceDump( - "InputMethodService.InputMethodImpl#showSoftInput", InputMethodService.this); + "InputMethodService.InputMethodImpl#showSoftInput", InputMethodService.this, + null /* icProto */); final boolean wasVisible = isInputViewShown(); if (dispatchOnShowInputRequested(flags, false)) { @@ -2182,7 +2185,8 @@ public class InputMethodService extends AbstractInputMethodService { return; } - ImeTracing.getInstance().triggerServiceDump("InputMethodService#showWindow", this); + ImeTracing.getInstance().triggerServiceDump("InputMethodService#showWindow", this, + null /* icProto */); Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.showWindow"); mDecorViewWasVisible = mDecorViewVisible; mInShowWindow = true; @@ -2260,7 +2264,8 @@ public class InputMethodService extends AbstractInputMethodService { */ private void applyVisibilityInInsetsConsumerIfNecessary(boolean setVisible) { ImeTracing.getInstance().triggerServiceDump( - "InputMethodService#applyVisibilityInInsetsConsumerIfNecessary", this); + "InputMethodService#applyVisibilityInInsetsConsumerIfNecessary", this, + null /* icProto */); mPrivOps.applyImeVisibility(setVisible ? mCurShowInputToken : mCurHideInputToken, setVisible); } @@ -2285,7 +2290,8 @@ public class InputMethodService extends AbstractInputMethodService { public void hideWindow() { if (DEBUG) Log.v(TAG, "CALL: hideWindow"); - ImeTracing.getInstance().triggerServiceDump("InputMethodService#hideWindow", this); + ImeTracing.getInstance().triggerServiceDump("InputMethodService#hideWindow", this, + null /* icProto */); mWindowVisible = false; finishViews(false /* finishingInput */); if (mDecorViewVisible) { @@ -2356,7 +2362,8 @@ public class InputMethodService extends AbstractInputMethodService { void doFinishInput() { if (DEBUG) Log.v(TAG, "CALL: doFinishInput"); - ImeTracing.getInstance().triggerServiceDump("InputMethodService#doFinishInput", this); + ImeTracing.getInstance().triggerServiceDump("InputMethodService#doFinishInput", this, + null /* icProto */); finishViews(true /* finishingInput */); if (mInputStarted) { mInlineSuggestionSessionController.notifyOnFinishInput(); @@ -2372,7 +2379,8 @@ public class InputMethodService extends AbstractInputMethodService { if (!restarting && mInputStarted) { doFinishInput(); } - ImeTracing.getInstance().triggerServiceDump("InputMethodService#doStartInput", this); + ImeTracing.getInstance().triggerServiceDump("InputMethodService#doStartInput", this, + null /* icProto */); mInputStarted = true; mStartedInputConnection = ic; mInputEditorInfo = attribute; @@ -2531,7 +2539,8 @@ public class InputMethodService extends AbstractInputMethodService { * @param flags Provides additional operating flags. */ public void requestHideSelf(int flags) { - ImeTracing.getInstance().triggerServiceDump("InputMethodService#requestHideSelf", this); + ImeTracing.getInstance().triggerServiceDump("InputMethodService#requestHideSelf", this, + null /* icProto */); mPrivOps.hideMySoftInput(flags); } @@ -2544,7 +2553,8 @@ public class InputMethodService extends AbstractInputMethodService { * @param flags Provides additional operating flags. */ public final void requestShowSelf(int flags) { - ImeTracing.getInstance().triggerServiceDump("InputMethodService#requestShowSelf", this); + ImeTracing.getInstance().triggerServiceDump("InputMethodService#requestShowSelf", this, + null /* icProto */); mPrivOps.showMySoftInput(flags); } @@ -3364,7 +3374,7 @@ public class InputMethodService extends AbstractInputMethodService { * @hide */ @Override - public final void dumpProtoInternal(ProtoOutputStream proto) { + public final void dumpProtoInternal(ProtoOutputStream proto, ProtoOutputStream icProto) { final long token = proto.start(InputMethodServiceTraceProto.INPUT_METHOD_SERVICE); mWindow.dumpDebug(proto, SOFT_INPUT_WINDOW); proto.write(VIEWS_CREATED, mViewsCreated); @@ -3393,6 +3403,9 @@ public class InputMethodService extends AbstractInputMethodService { proto.write(STATUS_ICON, mStatusIcon); mTmpInsets.dumpDebug(proto, LAST_COMPUTED_INSETS); proto.write(SETTINGS_OBSERVER, Objects.toString(mSettingsObserver)); + if (icProto != null) { + proto.write(INPUT_CONNECTION_CALL, icProto.getBytes()); + } proto.end(token); } } diff --git a/core/java/android/net/CaptivePortal.java b/core/java/android/net/CaptivePortal.java index c2586fa0c825..269bbf20c8b1 100644 --- a/core/java/android/net/CaptivePortal.java +++ b/core/java/android/net/CaptivePortal.java @@ -15,7 +15,6 @@ */ package android.net; -import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.RequiresPermission; import android.annotation.SystemApi; @@ -24,8 +23,6 @@ import android.os.Parcel; import android.os.Parcelable; import android.os.RemoteException; -import com.android.internal.logging.nano.MetricsProto.MetricsEvent; - /** * A class allowing apps handling the {@link ConnectivityManager#ACTION_CAPTIVE_PORTAL_SIGN_IN} * activity to indicate to the system different outcomes of captive portal sign in. This class is @@ -75,17 +72,6 @@ public class CaptivePortal implements Parcelable { private final IBinder mBinder; /** @hide */ - @IntDef(value = { - MetricsEvent.ACTION_CAPTIVE_PORTAL_LOGIN_ACTIVITY, - MetricsEvent.ACTION_CAPTIVE_PORTAL_LOGIN_RESULT_DISMISSED, - MetricsEvent.ACTION_CAPTIVE_PORTAL_LOGIN_RESULT_UNWANTED, - MetricsEvent.ACTION_CAPTIVE_PORTAL_LOGIN_RESULT_WANTED_AS_IS, - MetricsEvent.CAPTIVE_PORTAL_LOGIN_ACTIVITY_SSL_ERROR, - }) - public @interface EventId { - } - - /** @hide */ public CaptivePortal(@NonNull IBinder binder) { mBinder = binder; } @@ -176,7 +162,7 @@ public class CaptivePortal implements Parcelable { * @hide */ @SystemApi - public void logEvent(@EventId int eventId, @NonNull String packageName) { + public void logEvent(int eventId, @NonNull String packageName) { try { ICaptivePortal.Stub.asInterface(mBinder).logEvent(eventId, packageName); } catch (RemoteException e) { diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index 4b38d6377f37..3f2c966f9ed2 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -684,7 +684,7 @@ public class ConnectivityManager { * {@hide} */ @Deprecated - @UnsupportedAppUsage + @SystemApi public static final int TYPE_PROXY = 16; /** diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java index d130bc5d37e7..b951aca6d680 100644 --- a/core/java/android/os/PowerManager.java +++ b/core/java/android/os/PowerManager.java @@ -67,7 +67,7 @@ public final class PowerManager { /* NOTE: Wake lock levels were previously defined as a bit field, except that only a few * combinations were actually supported so the bit field was removed. This explains * why the numbering scheme is so odd. If adding a new wake lock level, any unused - * value (in frameworks/base/core/proto/android/os/enums.proto) can be used. + * value (in frameworks/proto_logging/stats/enums/os/enums.proto) can be used. */ /** diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 249a781509ac..b86b9ff814ae 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -9797,12 +9797,13 @@ public final class Settings { "use_blast_adapter_sv"; /** - * If {@code true}, vendor provided window manager display settings will be ignored. - * (0 = false, 1 = true) + * Path to the WindowManager display settings file. If unset, the default file path will + * be used. + * * @hide */ - public static final String DEVELOPMENT_IGNORE_VENDOR_DISPLAY_SETTINGS = - "ignore_vendor_display_settings"; + public static final String DEVELOPMENT_WM_DISPLAY_SETTINGS_PATH = + "wm_display_settings_path"; /** * Whether user has enabled development settings. diff --git a/core/java/android/util/imetracing/ImeTracing.java b/core/java/android/util/imetracing/ImeTracing.java index 4058eef3e2a3..723f1dd15e23 100644 --- a/core/java/android/util/imetracing/ImeTracing.java +++ b/core/java/android/util/imetracing/ImeTracing.java @@ -110,15 +110,20 @@ public abstract class ImeTracing { * * @param where Place where the trace was triggered. * @param immInstance The {@link InputMethodManager} instance to dump. + * @param icProto {@link android.view.inputmethod.InputConnection} call data in proto format. */ - public abstract void triggerClientDump(String where, InputMethodManager immInstance); + public abstract void triggerClientDump(String where, InputMethodManager immInstance, + ProtoOutputStream icProto); /** * Starts a proto dump of the currently connected InputMethodService information. * * @param where Place where the trace was triggered. + * @param service The {@link android.inputmethodservice.InputMethodService} to be dumped. + * @param icProto {@link android.view.inputmethod.InputConnection} call data in proto format. */ - public abstract void triggerServiceDump(String where, AbstractInputMethodService service); + public abstract void triggerServiceDump(String where, AbstractInputMethodService service, + ProtoOutputStream icProto); /** * Starts a proto dump of the InputMethodManagerService information. diff --git a/core/java/android/util/imetracing/ImeTracingClientImpl.java b/core/java/android/util/imetracing/ImeTracingClientImpl.java index 904b44da97d7..6cc652d942cc 100644 --- a/core/java/android/util/imetracing/ImeTracingClientImpl.java +++ b/core/java/android/util/imetracing/ImeTracingClientImpl.java @@ -45,7 +45,8 @@ class ImeTracingClientImpl extends ImeTracing { } @Override - public void triggerClientDump(String where, @NonNull InputMethodManager immInstance) { + public void triggerClientDump(String where, @NonNull InputMethodManager immInstance, + ProtoOutputStream icProto) { if (!isEnabled() || !isAvailable()) { return; } @@ -59,7 +60,7 @@ class ImeTracingClientImpl extends ImeTracing { try { ProtoOutputStream proto = new ProtoOutputStream(); - immInstance.dumpDebug(proto); + immInstance.dumpDebug(proto, icProto); sendToService(proto.getBytes(), IME_TRACING_FROM_CLIENT, where); } catch (RemoteException e) { Log.e(TAG, "Exception while sending ime-related client dump to server", e); @@ -69,7 +70,8 @@ class ImeTracingClientImpl extends ImeTracing { } @Override - public void triggerServiceDump(String where, @NonNull AbstractInputMethodService service) { + public void triggerServiceDump(String where, @NonNull AbstractInputMethodService service, + ProtoOutputStream icProto) { if (!isEnabled() || !isAvailable()) { return; } @@ -83,7 +85,7 @@ class ImeTracingClientImpl extends ImeTracing { try { ProtoOutputStream proto = new ProtoOutputStream(); - service.dumpProtoInternal(proto); + service.dumpProtoInternal(proto, icProto); sendToService(proto.getBytes(), IME_TRACING_FROM_IMS, where); } catch (RemoteException e) { Log.e(TAG, "Exception while sending ime-related service dump to server", e); diff --git a/core/java/android/util/imetracing/ImeTracingServerImpl.java b/core/java/android/util/imetracing/ImeTracingServerImpl.java index d758d77fb2f2..e793c280afbc 100644 --- a/core/java/android/util/imetracing/ImeTracingServerImpl.java +++ b/core/java/android/util/imetracing/ImeTracingServerImpl.java @@ -133,12 +133,14 @@ class ImeTracingServerImpl extends ImeTracing { } @Override - public void triggerClientDump(String where, InputMethodManager immInstance) { + public void triggerClientDump(String where, InputMethodManager immInstance, + ProtoOutputStream icProto) { // Intentionally left empty, this is implemented in ImeTracingClientImpl } @Override - public void triggerServiceDump(String where, AbstractInputMethodService service) { + public void triggerServiceDump(String where, AbstractInputMethodService service, + ProtoOutputStream icProto) { // Intentionally left empty, this is implemented in ImeTracingClientImpl } diff --git a/core/java/android/util/imetracing/InputConnectionHelper.java b/core/java/android/util/imetracing/InputConnectionHelper.java new file mode 100644 index 000000000000..39f1e01eb4a9 --- /dev/null +++ b/core/java/android/util/imetracing/InputConnectionHelper.java @@ -0,0 +1,231 @@ +/* + * 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.util.imetracing; + +import static android.view.inputmethod.InputConnectionCallProto.GET_CURSOR_CAPS_MODE; +import static android.view.inputmethod.InputConnectionCallProto.GET_EXTRACTED_TEXT; +import static android.view.inputmethod.InputConnectionCallProto.GET_SELECTED_TEXT; +import static android.view.inputmethod.InputConnectionCallProto.GET_SURROUNDING_TEXT; +import static android.view.inputmethod.InputConnectionCallProto.GET_TEXT_AFTER_CURSOR; +import static android.view.inputmethod.InputConnectionCallProto.GET_TEXT_BEFORE_CURSOR; +import static android.view.inputmethod.InputConnectionCallProto.GetExtractedText.REQUEST; + +import android.annotation.IntRange; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.util.proto.ProtoOutputStream; +import android.view.inputmethod.ExtractedText; +import android.view.inputmethod.ExtractedTextRequest; +import android.view.inputmethod.InputConnectionCallProto.GetCursorCapsMode; +import android.view.inputmethod.InputConnectionCallProto.GetExtractedText; +import android.view.inputmethod.InputConnectionCallProto.GetSelectedText; +import android.view.inputmethod.InputConnectionCallProto.GetSurroundingText; +import android.view.inputmethod.InputConnectionCallProto.GetTextAfterCursor; +import android.view.inputmethod.InputConnectionCallProto.GetTextBeforeCursor; +import android.view.inputmethod.SurroundingText; + +/** + * Helper class for constructing {@link android.view.inputmethod.InputConnection} dumps, which are + * integrated into {@link ImeTracing}. + * @hide + */ +public class InputConnectionHelper { + static final String TAG = "InputConnectionHelper"; + public static final boolean DUMP_TEXT = false; + + private InputConnectionHelper() {} + + /** + * Builder for InputConnectionCallProto to hold + * {@link android.view.inputmethod.InputConnection#getTextAfterCursor(int, int)} data. + * + * @param length The expected length of the text. This must be non-negative. + * @param flags Supplies additional options controlling how the text is + * returned. May be either {@code 0} or + * {@link android.view.inputmethod.InputConnection#GET_TEXT_WITH_STYLES}. + * @param result The text after the cursor position; the length of the + * returned text might be less than <var>length</var>. + * @return ProtoOutputStream holding the InputConnectionCallProto data. + */ + public static ProtoOutputStream buildGetTextAfterCursorProto(@IntRange(from = 0) int length, + int flags, @Nullable CharSequence result) { + ProtoOutputStream proto = new ProtoOutputStream(); + final long token = proto.start(GET_TEXT_AFTER_CURSOR); + proto.write(GetTextAfterCursor.LENGTH, length); + proto.write(GetTextAfterCursor.FLAGS, flags); + if (result == null) { + proto.write(GetTextAfterCursor.RESULT, "null result"); + } else if (DUMP_TEXT) { + proto.write(GetTextAfterCursor.RESULT, result.toString()); + } + proto.end(token); + return proto; + } + + /** + * Builder for InputConnectionCallProto to hold + * {@link android.view.inputmethod.InputConnection#getTextBeforeCursor(int, int)} data. + * + * @param length The expected length of the text. This must be non-negative. + * @param flags Supplies additional options controlling how the text is + * returned. May be either {@code 0} or + * {@link android.view.inputmethod.InputConnection#GET_TEXT_WITH_STYLES}. + * @param result The text before the cursor position; the length of the + * returned text might be less than <var>length</var>. + * @return ProtoOutputStream holding the InputConnectionCallProto data. + */ + public static ProtoOutputStream buildGetTextBeforeCursorProto(@IntRange(from = 0) int length, + int flags, @Nullable CharSequence result) { + ProtoOutputStream proto = new ProtoOutputStream(); + final long token = proto.start(GET_TEXT_BEFORE_CURSOR); + proto.write(GetTextBeforeCursor.LENGTH, length); + proto.write(GetTextBeforeCursor.FLAGS, flags); + if (result == null) { + proto.write(GetTextBeforeCursor.RESULT, "null result"); + } else if (DUMP_TEXT) { + proto.write(GetTextBeforeCursor.RESULT, result.toString()); + } + proto.end(token); + return proto; + } + + /** + * Builder for InputConnectionCallProto to hold + * {@link android.view.inputmethod.InputConnection#getSelectedText(int)} data. + * + * @param flags Supplies additional options controlling how the text is + * returned. May be either {@code 0} or + * {@link android.view.inputmethod.InputConnection#GET_TEXT_WITH_STYLES}. + * @param result the text that is currently selected, if any, or null if + * no text is selected. In {@link android.os.Build.VERSION_CODES#N} and + * later, returns false when the target application does not implement + * this method. + * @return ProtoOutputStream holding the InputConnectionCallProto data. + */ + public static ProtoOutputStream buildGetSelectedTextProto(int flags, + @Nullable CharSequence result) { + ProtoOutputStream proto = new ProtoOutputStream(); + final long token = proto.start(GET_SELECTED_TEXT); + proto.write(GetSelectedText.FLAGS, flags); + if (result == null) { + proto.write(GetSelectedText.RESULT, "null result"); + } else if (DUMP_TEXT) { + proto.write(GetSelectedText.RESULT, result.toString()); + } + proto.end(token); + return proto; + } + + /** + * Builder for InputConnectionCallProto to hold + * {@link android.view.inputmethod.InputConnection#getSurroundingText(int, int, int)} data. + * + * @param beforeLength The expected length of the text before the cursor. + * @param afterLength The expected length of the text after the cursor. + * @param flags Supplies additional options controlling how the text is + * returned. May be either {@code 0} or + * {@link android.view.inputmethod.InputConnection#GET_TEXT_WITH_STYLES}. + * @param result an {@link android.view.inputmethod.SurroundingText} object describing the + * surrounding text and state of selection, or null if the input connection is no longer valid, + * or the editor can't comply with the request for some reason, or the application does not + * implement this method. The length of the returned text might be less than the sum of + * <var>beforeLength</var> and <var>afterLength</var> . + * @return ProtoOutputStream holding the InputConnectionCallProto data. + */ + public static ProtoOutputStream buildGetSurroundingTextProto(@IntRange(from = 0) + int beforeLength, @IntRange(from = 0) int afterLength, int flags, + @Nullable SurroundingText result) { + ProtoOutputStream proto = new ProtoOutputStream(); + final long token = proto.start(GET_SURROUNDING_TEXT); + proto.write(GetSurroundingText.BEFORE_LENGTH, beforeLength); + proto.write(GetSurroundingText.AFTER_LENGTH, afterLength); + proto.write(GetSurroundingText.FLAGS, flags); + if (result == null) { + final long token_result = proto.start(GetSurroundingText.RESULT); + proto.write(GetSurroundingText.SurroundingText.TEXT, "null result"); + proto.end(token_result); + } else if (DUMP_TEXT) { + final long token_result = proto.start(GetSurroundingText.RESULT); + proto.write(GetSurroundingText.SurroundingText.TEXT, result.getText().toString()); + proto.write(GetSurroundingText.SurroundingText.SELECTION_START, + result.getSelectionStart()); + proto.write(GetSurroundingText.SurroundingText.SELECTION_END, + result.getSelectionEnd()); + proto.write(GetSurroundingText.SurroundingText.OFFSET, result.getOffset()); + proto.end(token_result); + } + proto.end(token); + return proto; + } + + /** + * Builder for InputConnectionCallProto to hold + * {@link android.view.inputmethod.InputConnection#getCursorCapsMode(int)} data. + * + * @param reqModes The desired modes to retrieve, as defined by + * {@link android.text.TextUtils#getCapsMode TextUtils.getCapsMode}. + * @param result the caps mode flags that are in effect at the current + * cursor position. See TYPE_TEXT_FLAG_CAPS_* in {@link android.text.InputType}. + * @return ProtoOutputStream holding the InputConnectionCallProto data. + */ + public static ProtoOutputStream buildGetCursorCapsModeProto(int reqModes, int result) { + ProtoOutputStream proto = new ProtoOutputStream(); + final long token = proto.start(GET_CURSOR_CAPS_MODE); + proto.write(GetCursorCapsMode.REQ_MODES, reqModes); + if (DUMP_TEXT) { + proto.write(GetCursorCapsMode.RESULT, result); + } + proto.end(token); + return proto; + } + + /** + * Builder for InputConnectionCallProto to hold + * {@link android.view.inputmethod.InputConnection#getExtractedText(ExtractedTextRequest, int)} + * data. + * + * @param request Description of how the text should be returned. + * {@link android.view.inputmethod.ExtractedTextRequest} + * @param flags Additional options to control the client, either {@code 0} or + * {@link android.view.inputmethod.InputConnection#GET_EXTRACTED_TEXT_MONITOR}. + * @param result an {@link android.view.inputmethod.ExtractedText} + * object describing the state of the text view and containing the + * extracted text itself, or null if the input connection is no + * longer valid of the editor can't comply with the request for + * some reason. + * @return ProtoOutputStream holding the InputConnectionCallProto data. + */ + public static ProtoOutputStream buildGetExtractedTextProto(@NonNull ExtractedTextRequest + request, int flags, @Nullable ExtractedText result) { + ProtoOutputStream proto = new ProtoOutputStream(); + final long token = proto.start(GET_EXTRACTED_TEXT); + final long token_request = proto.start(REQUEST); + proto.write(GetExtractedText.ExtractedTextRequest.TOKEN, request.token); + proto.write(GetExtractedText.ExtractedTextRequest.FLAGS, request.flags); + proto.write(GetExtractedText.ExtractedTextRequest.HINT_MAX_LINES, request.hintMaxLines); + proto.write(GetExtractedText.ExtractedTextRequest.HINT_MAX_CHARS, request.hintMaxChars); + proto.end(token_request); + proto.write(GetExtractedText.FLAGS, flags); + if (result == null) { + proto.write(GetExtractedText.RESULT, "null result"); + } else if (DUMP_TEXT) { + proto.write(GetExtractedText.RESULT, result.text.toString()); + } + proto.end(token); + return proto; + } +} diff --git a/core/java/android/uwb/UwbManager.java b/core/java/android/uwb/UwbManager.java index 2f1e2ded26ac..6488490b8863 100644 --- a/core/java/android/uwb/UwbManager.java +++ b/core/java/android/uwb/UwbManager.java @@ -19,7 +19,11 @@ package android.uwb; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.SuppressLint; +import android.annotation.SystemService; +import android.content.Context; +import android.os.IBinder; import android.os.PersistableBundle; +import android.os.ServiceManager; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -36,7 +40,11 @@ import java.util.concurrent.Executor; * * @hide */ +@SystemService(Context.UWB_SERVICE) public final class UwbManager { + private IUwbAdapter mUwbAdapter; + private static final String SERVICE_NAME = "uwb"; + /** * Interface for receiving UWB adapter state changes */ @@ -96,10 +104,30 @@ public final class UwbManager { /** * Use <code>Context.getSystemService(UwbManager.class)</code> to get an instance. + * + * @param adapter an instance of an {@link android.uwb.IUwbAdapter} */ - private UwbManager() { - throw new UnsupportedOperationException(); + private UwbManager(IUwbAdapter adapter) { + mUwbAdapter = adapter; + } + + /** + * @hide + */ + public static UwbManager getInstance() { + IBinder b = ServiceManager.getService(SERVICE_NAME); + if (b == null) { + return null; + } + + IUwbAdapter adapter = IUwbAdapter.Stub.asInterface(b); + if (adapter == null) { + return null; + } + + return new UwbManager(adapter); } + /** * Register an {@link AdapterStateCallback} to listen for UWB adapter state changes * <p>The provided callback will be invoked by the given {@link Executor}. diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java index 56c7e27151e0..9991367e6bfd 100644 --- a/core/java/android/view/Display.java +++ b/core/java/android/view/Display.java @@ -1512,6 +1512,9 @@ public final class Display { * then 1920x1080 50Hz will have alternative refresh rate of 60Hz. If 1920x1080 60Hz * has an alternative of 50Hz and 1920x1080 50Hz has an alternative of 24Hz, then 1920x1080 * 60Hz will also have an alternative of 24Hz. + * + * @see Surface#setFrameRate + * @see SurfaceControl.Transaction#setFrameRate */ @NonNull public float[] getAlternativeRefreshRates() { diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java index 1c82619a61ad..c4f32c433598 100644 --- a/core/java/android/view/InsetsController.java +++ b/core/java/android/view/InsetsController.java @@ -829,7 +829,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation } if (fromIme) { ImeTracing.getInstance().triggerClientDump("InsetsController#show", - mHost.getInputMethodManager()); + mHost.getInputMethodManager(), null /* icProto */); Trace.asyncTraceEnd(TRACE_TAG_VIEW, "IC.showRequestFromApiToImeReady", 0); Trace.asyncTraceBegin(TRACE_TAG_VIEW, "IC.showRequestFromIme", 0); } else { @@ -886,7 +886,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation void hide(@InsetsType int types, boolean fromIme) { if (fromIme) { ImeTracing.getInstance().triggerClientDump("InsetsController#hide", - mHost.getInputMethodManager()); + mHost.getInputMethodManager(), null /* icProto */); Trace.asyncTraceBegin(TRACE_TAG_VIEW, "IC.hideRequestFromIme", 0); } else { Trace.asyncTraceBegin(TRACE_TAG_VIEW, "IC.hideRequestFromApi", 0); @@ -928,7 +928,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation if (fromIme) { ImeTracing.getInstance().triggerClientDump( "InsetsController#controlWindowInsetsAnimation", - mHost.getInputMethodManager()); + mHost.getInputMethodManager(), null /* icProto */); } controlAnimationUnchecked(types, cancellationSignal, listener, mFrame, fromIme, durationMs, @@ -1022,7 +1022,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation animationType, mHost.getTranslator()); if ((typesReady & WindowInsets.Type.ime()) != 0) { ImeTracing.getInstance().triggerClientDump("InsetsAnimationControlImpl", - mHost.getInputMethodManager()); + mHost.getInputMethodManager(), null /* icProto */); } mRunningAnimations.add(new RunningAnimation(runner, animationType)); if (DEBUG) Log.d(TAG, "Animation added to runner. useInsetsAnimationThread: " @@ -1200,7 +1200,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation if (types.valueAt(j) == ITYPE_IME) { ImeTracing.getInstance().triggerClientDump( "InsetsSourceConsumer#notifyAnimationFinished", - mHost.getInputMethodManager()); + mHost.getInputMethodManager(), null /* icProto */); } stateChanged |= getSourceConsumer(types.valueAt(j)).notifyAnimationFinished(); } @@ -1345,7 +1345,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation boolean fromIme) { if ((types & ime()) != 0) { ImeTracing.getInstance().triggerClientDump("InsetsController#hideDirectly", - mHost.getInputMethodManager()); + mHost.getInputMethodManager(), null /* icProto */); } final ArraySet<Integer> internalTypes = InsetsState.toInternalType(types); for (int i = internalTypes.size() - 1; i >= 0; i--) { @@ -1361,7 +1361,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation private void showDirectly(@InsetsType int types, boolean fromIme) { if ((types & ime()) != 0) { ImeTracing.getInstance().triggerClientDump("InsetsController#showDirectly", - mHost.getInputMethodManager()); + mHost.getInputMethodManager(), null /* icProto */); } final ArraySet<Integer> internalTypes = InsetsState.toInternalType(types); for (int i = internalTypes.size() - 1; i >= 0; i--) { diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java index 57ca71ae4b02..d839e3532b64 100644 --- a/core/java/android/view/ThreadedRenderer.java +++ b/core/java/android/view/ThreadedRenderer.java @@ -21,6 +21,7 @@ import android.annotation.Nullable; import android.app.ActivityManager; import android.content.Context; import android.content.res.TypedArray; +import android.graphics.FrameInfo; import android.graphics.HardwareRenderer; import android.graphics.Picture; import android.graphics.Point; @@ -610,8 +611,7 @@ public final class ThreadedRenderer extends HardwareRenderer { * @param attachInfo AttachInfo tied to the specified view. */ void draw(View view, AttachInfo attachInfo, DrawCallbacks callbacks) { - final Choreographer choreographer = attachInfo.mViewRootImpl.mChoreographer; - choreographer.mFrameInfo.markDrawStart(); + attachInfo.mViewRootImpl.mViewFrameInfo.markDrawStart(); updateRootDisplayList(view, callbacks); @@ -629,7 +629,9 @@ public final class ThreadedRenderer extends HardwareRenderer { attachInfo.mPendingAnimatingRenderNodes = null; } - int syncResult = syncAndDrawFrame(choreographer.mFrameInfo); + final FrameInfo frameInfo = attachInfo.mViewRootImpl.getUpdatedFrameInfo(); + + int syncResult = syncAndDrawFrame(frameInfo); if ((syncResult & SYNC_LOST_SURFACE_REWARD_IF_FOUND) != 0) { Log.w("OpenGLRenderer", "Surface lost, forcing relayout"); // We lost our surface. For a relayout next frame which should give us a new diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index a5c66537b11f..29cc4b507acc 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -27953,10 +27953,26 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * Requests pointer capture mode. * <p> * When the window has pointer capture, the mouse pointer icon will disappear and will not - * change its position. Further mouse will be dispatched with the source - * {@link InputDevice#SOURCE_MOUSE_RELATIVE}, and relative position changes will be available - * through {@link MotionEvent#getX} and {@link MotionEvent#getY}. Non-mouse events - * (touchscreens, or stylus) will not be affected. + * change its position. Enabling pointer capture will change the behavior of input devices in + * the following ways: + * <ul> + * <li>Events from a mouse will be delivered with the source + * {@link InputDevice#SOURCE_MOUSE_RELATIVE}, and relative position changes will be + * available through {@link MotionEvent#getX} and {@link MotionEvent#getY}.</li> + * + * <li>Events from a touchpad will be delivered with the source + * {@link InputDevice#SOURCE_TOUCHPAD}, where the absolute position of each of the pointers + * on the touchpad will be available through {@link MotionEvent#getX(int)} and + * {@link MotionEvent#getY(int)}, and their relative movements are stored in + * {@link MotionEvent#AXIS_RELATIVE_X} and {@link MotionEvent#AXIS_RELATIVE_Y}.</li> + * + * <li>Events from other types of devices, such as touchscreens, will not be affected.</li> + * </ul> + * <p> + * Events captured through pointer capture will be dispatched to + * {@link OnCapturedPointerListener#onCapturedPointer(View, MotionEvent)} if an + * {@link OnCapturedPointerListener} is set, and otherwise to + * {@link #onCapturedPointerEvent(MotionEvent)}. * <p> * If the window already has pointer capture, this call does nothing. * <p> @@ -27965,6 +27981,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * * @see #releasePointerCapture() * @see #hasPointerCapture() + * @see #onPointerCaptureChange(boolean) */ public void requestPointerCapture() { final ViewRootImpl viewRootImpl = getViewRootImpl(); @@ -27980,6 +27997,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * If the window does not have pointer capture, this call will do nothing. * @see #requestPointerCapture() * @see #hasPointerCapture() + * @see #onPointerCaptureChange(boolean) */ public void releasePointerCapture() { final ViewRootImpl viewRootImpl = getViewRootImpl(); diff --git a/core/java/android/view/ViewFrameInfo.java b/core/java/android/view/ViewFrameInfo.java new file mode 100644 index 000000000000..890d071f8090 --- /dev/null +++ b/core/java/android/view/ViewFrameInfo.java @@ -0,0 +1,81 @@ +/* + * 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.view; + +import android.graphics.FrameInfo; + +/** + * The timing information of events taking place in ViewRootImpl + * @hide + */ +public class ViewFrameInfo { + public long drawStart; + public long oldestInputEventTime; // the time of the oldest input event consumed for this frame + public long newestInputEventTime; // the time of the newest input event consumed for this frame + // Various flags set to provide extra metadata about the current frame. See flag definitions + // inside FrameInfo. + // @see android.graphics.FrameInfo.FLAG_WINDOW_LAYOUT_CHANGED + public long flags; + + /** + * Update the oldest event time. + * @param eventTime the time of the input event + */ + public void updateOldestInputEvent(long eventTime) { + if (oldestInputEventTime == 0 || eventTime < oldestInputEventTime) { + oldestInputEventTime = eventTime; + } + } + + /** + * Update the newest event time. + * @param eventTime the time of the input event + */ + public void updateNewestInputEvent(long eventTime) { + if (newestInputEventTime == 0 || eventTime > newestInputEventTime) { + newestInputEventTime = eventTime; + } + } + + /** + * Populate the missing fields using the data from ViewFrameInfo + * @param frameInfo : the structure FrameInfo object to populate + */ + public void populateFrameInfo(FrameInfo frameInfo) { + frameInfo.frameInfo[FrameInfo.FLAGS] |= flags; + frameInfo.frameInfo[FrameInfo.DRAW_START] = drawStart; + frameInfo.frameInfo[FrameInfo.OLDEST_INPUT_EVENT] = oldestInputEventTime; + frameInfo.frameInfo[FrameInfo.NEWEST_INPUT_EVENT] = newestInputEventTime; + } + + /** + * Reset this data. Should typically be invoked after calling "populateFrameInfo". + */ + public void reset() { + drawStart = 0; + oldestInputEventTime = 0; + newestInputEventTime = 0; + flags = 0; + } + + /** + * Record the current time, and store it in 'drawStart' + */ + public void markDrawStart() { + drawStart = System.nanoTime(); + } +} diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 2bea0d6b4b04..a274079d0108 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -423,7 +423,7 @@ public final class ViewRootImpl implements ViewParent, @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) int mHeight; @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - Rect mDirty; + private Rect mDirty; public boolean mIsAnimating; private boolean mUseMTRenderer; @@ -446,6 +446,23 @@ public final class ViewRootImpl implements ViewParent, @UnsupportedAppUsage FallbackEventHandler mFallbackEventHandler; final Choreographer mChoreographer; + protected final ViewFrameInfo mViewFrameInfo = new ViewFrameInfo(); + + /** + * Update the Choreographer's FrameInfo object with the timing information for the current + * ViewRootImpl instance. Erase the data in the current ViewFrameInfo to prepare for the next + * frame. + * @return the updated FrameInfo object + */ + protected @NonNull FrameInfo getUpdatedFrameInfo() { + // Since Choreographer is a thread-local singleton while we can have multiple + // ViewRootImpl's, populate the frame information from the current viewRootImpl before + // starting the draw + FrameInfo frameInfo = mChoreographer.mFrameInfo; + mViewFrameInfo.populateFrameInfo(frameInfo); + mViewFrameInfo.reset(); + return frameInfo; + } // used in relayout to get SurfaceControl size // for BLAST adapter surface setup @@ -2675,7 +2692,7 @@ public final class ViewRootImpl implements ViewParent, // to resume them mDirty.set(0, 0, mWidth, mHeight); } - mChoreographer.mFrameInfo.addFlags(FrameInfo.FLAG_WINDOW_LAYOUT_CHANGED); + mViewFrameInfo.flags |= FrameInfo.FLAG_WINDOW_LAYOUT_CHANGED; } relayoutResult = relayoutWindow(params, viewVisibility, insetsPending); @@ -8138,7 +8155,8 @@ public final class ViewRootImpl implements ViewParent, oldestEventTime = me.getHistoricalEventTimeNano(0); } } - mChoreographer.mFrameInfo.updateInputEventTime(eventTime, oldestEventTime); + mViewFrameInfo.updateOldestInputEvent(oldestEventTime); + mViewFrameInfo.updateNewestInputEvent(eventTime); deliverInputEvent(q); } @@ -9225,7 +9243,8 @@ public final class ViewRootImpl implements ViewParent, final ViewRootImpl viewAncestor = mViewAncestor.get(); if (fromIme) { ImeTracing.getInstance().triggerClientDump("ViewRootImpl.W#showInsets", - viewAncestor.getInsetsController().getHost().getInputMethodManager()); + viewAncestor.getInsetsController().getHost().getInputMethodManager(), + null /* icProto */); } if (viewAncestor != null) { viewAncestor.showInsets(types, fromIme); @@ -9238,7 +9257,8 @@ public final class ViewRootImpl implements ViewParent, final ViewRootImpl viewAncestor = mViewAncestor.get(); if (fromIme) { ImeTracing.getInstance().triggerClientDump("ViewRootImpl.W#hideInsets", - viewAncestor.getInsetsController().getHost().getInputMethodManager()); + viewAncestor.getInsetsController().getHost().getInputMethodManager(), + null /* icProto */); } if (viewAncestor != null) { viewAncestor.hideInsets(types, fromIme); diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index 8d2c2d96637f..907b5b085b59 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -23,6 +23,7 @@ import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodCl import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceProto.ClientSideProto.EDITOR_INFO; import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceProto.ClientSideProto.IME_INSETS_SOURCE_CONSUMER; import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceProto.ClientSideProto.INPUT_CONNECTION; +import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceProto.ClientSideProto.INPUT_CONNECTION_CALL; import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceProto.ClientSideProto.INPUT_METHOD_MANAGER; import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceProto.ClientSideProto.VIEW_ROOT_IMPL; import static android.view.inputmethod.InputMethodManagerProto.ACTIVE; @@ -579,7 +580,8 @@ public final class InputMethodManager { int windowFlags) { final View servedView; ImeTracing.getInstance().triggerClientDump( - "InputMethodManager.DelegateImpl#startInput", InputMethodManager.this); + "InputMethodManager.DelegateImpl#startInput", InputMethodManager.this, + null /* icProto */); synchronized (mH) { mCurrentTextBoxAttribute = null; mCompletions = null; @@ -1016,6 +1018,11 @@ public final class InputMethodManager { return mParentInputMethodManager.mActive && !isFinished(); } + @Override + public InputMethodManager getIMM() { + return mParentInputMethodManager; + } + void deactivate() { if (isFinished()) { // This is a small performance optimization. Still only the 1st call of @@ -1713,7 +1720,8 @@ public final class InputMethodManager { * {@link #RESULT_HIDDEN}. */ public boolean showSoftInput(View view, int flags, ResultReceiver resultReceiver) { - ImeTracing.getInstance().triggerClientDump("InputMethodManager#showSoftInput", this); + ImeTracing.getInstance().triggerClientDump("InputMethodManager#showSoftInput", this, + null /* icProto */); // Re-dispatch if there is a context mismatch. final InputMethodManager fallbackImm = getFallbackInputMethodManagerIfNecessary(view); if (fallbackImm != null) { @@ -1822,7 +1830,7 @@ public final class InputMethodManager { public boolean hideSoftInputFromWindow(IBinder windowToken, int flags, ResultReceiver resultReceiver) { ImeTracing.getInstance().triggerClientDump("InputMethodManager#hideSoftInputFromWindow", - this); + this, null /* icProto */); checkFocus(); synchronized (mH) { final View servedView = getServedViewLocked(); @@ -2228,7 +2236,8 @@ public final class InputMethodManager { * @hide */ public void notifyImeHidden(IBinder windowToken) { - ImeTracing.getInstance().triggerClientDump("InputMethodManager#notifyImeHidden", this); + ImeTracing.getInstance().triggerClientDump("InputMethodManager#notifyImeHidden", this, + null /* icProto */); synchronized (mH) { try { if (mCurMethod != null && mCurRootView != null @@ -3296,7 +3305,7 @@ public final class InputMethodManager { for (String arg : args) { if (arg.equals(PROTO_ARG)) { final ProtoOutputStream proto = new ProtoOutputStream(fd); - dumpDebug(proto); + dumpDebug(proto, null /* icProto */); proto.flush(); return true; } @@ -3309,10 +3318,11 @@ public final class InputMethodManager { * {@link ProtoOutputStream}. * * @param proto The proto stream to which the dumps are written. + * @param icProto {@link InputConnection} call data in proto format. * @hide */ @GuardedBy("mH") - public void dumpDebug(ProtoOutputStream proto) { + public void dumpDebug(ProtoOutputStream proto, ProtoOutputStream icProto) { if (mCurMethod == null) { return; } @@ -3337,6 +3347,9 @@ public final class InputMethodManager { if (mServedInputConnectionWrapper != null) { mServedInputConnectionWrapper.dumpDebug(proto, INPUT_CONNECTION); } + if (icProto != null) { + proto.write(INPUT_CONNECTION_CALL, icProto.getBytes()); + } } } } diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 2357f368c428..02a930017906 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -964,6 +964,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * @hide */ public static void preloadFontCache() { + if (Typeface.ENABLE_LAZY_TYPEFACE_INITIALIZATION) { + return; + } Paint p = new Paint(); p.setAntiAlias(true); // Ensure that the Typeface is loaded here. diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java index 2e3d560ccb56..9668c3b0af1c 100644 --- a/core/java/com/android/internal/app/ChooserActivity.java +++ b/core/java/com/android/internal/app/ChooserActivity.java @@ -162,6 +162,9 @@ public class ChooserActivity extends ResolverActivity implements private AppPredictor mWorkAppPredictor; private boolean mShouldDisplayLandscape; + private static final int MAX_TARGETS_PER_ROW_PORTRAIT = 4; + private static final int MAX_TARGETS_PER_ROW_LANDSCAPE = 8; + @UnsupportedAppUsage public ChooserActivity() { } @@ -905,7 +908,7 @@ public class ChooserActivity extends ResolverActivity implements adapter, getPersonalProfileUserHandle(), /* workProfileUserHandle= */ null, - isSendAction(getTargetIntent())); + isSendAction(getTargetIntent()), getMaxTargetsPerRow()); } private ChooserMultiProfilePagerAdapter createChooserMultiProfilePagerAdapterForTwoProfiles( @@ -934,7 +937,7 @@ public class ChooserActivity extends ResolverActivity implements selectedProfile, getPersonalProfileUserHandle(), getWorkProfileUserHandle(), - isSendAction(getTargetIntent())); + isSendAction(getTargetIntent()), getMaxTargetsPerRow()); } private int findSelectedProfile() { @@ -2683,7 +2686,7 @@ public class ChooserActivity extends ResolverActivity implements // and b/150936654 recyclerView.setAdapter(gridAdapter); ((GridLayoutManager) recyclerView.getLayoutManager()).setSpanCount( - gridAdapter.getMaxTargetsPerRow()); + getMaxTargetsPerRow()); } UserHandle currentUserHandle = mChooserMultiProfilePagerAdapter.getCurrentUserHandle(); @@ -2848,9 +2851,7 @@ public class ChooserActivity extends ResolverActivity implements @Override // ChooserListCommunicator public int getMaxRankedTargets() { - return mChooserMultiProfilePagerAdapter.getCurrentRootAdapter() == null - ? ChooserGridAdapter.MAX_TARGETS_PER_ROW_PORTRAIT - : mChooserMultiProfilePagerAdapter.getCurrentRootAdapter().getMaxTargetsPerRow(); + return getMaxTargetsPerRow(); } @Override // ChooserListCommunicator @@ -3198,6 +3199,13 @@ public class ChooserActivity extends ResolverActivity implements } } + int getMaxTargetsPerRow() { + int maxTargets = MAX_TARGETS_PER_ROW_PORTRAIT; + if (mShouldDisplayLandscape) { + maxTargets = MAX_TARGETS_PER_ROW_LANDSCAPE; + } + return maxTargets; + } /** * Adapter for all types of items and targets in ShareSheet. * Note that ranked sections like Direct Share - while appearing grid-like - are handled on the @@ -3226,9 +3234,6 @@ public class ChooserActivity extends ResolverActivity implements private static final int VIEW_TYPE_CALLER_AND_RANK = 5; private static final int VIEW_TYPE_FOOTER = 6; - private static final int MAX_TARGETS_PER_ROW_PORTRAIT = 4; - private static final int MAX_TARGETS_PER_ROW_LANDSCAPE = 8; - private static final int NUM_EXPANSIONS_TO_HIDE_AZ_LABEL = 20; ChooserGridAdapter(ChooserListAdapter wrappedAdapter) { @@ -3277,14 +3282,6 @@ public class ChooserActivity extends ResolverActivity implements return false; } - int getMaxTargetsPerRow() { - int maxTargets = MAX_TARGETS_PER_ROW_PORTRAIT; - if (mShouldDisplayLandscape) { - maxTargets = MAX_TARGETS_PER_ROW_LANDSCAPE; - } - return maxTargets; - } - /** * Hides the list item content preview. * <p>Not to be confused with the sticky content preview which is above the @@ -3654,8 +3651,7 @@ public class ChooserActivity extends ResolverActivity implements position -= getSystemRowCount() + getProfileRowCount(); final int serviceCount = mChooserListAdapter.getServiceTargetCount(); - final int serviceRows = (int) Math.ceil((float) serviceCount - / ChooserListAdapter.MAX_SERVICE_TARGETS); + final int serviceRows = (int) Math.ceil((float) serviceCount / getMaxRankedTargets()); if (position < serviceRows) { return position * getMaxTargetsPerRow(); } diff --git a/core/java/com/android/internal/app/ChooserListAdapter.java b/core/java/com/android/internal/app/ChooserListAdapter.java index 00b5cb646bca..570066807f16 100644 --- a/core/java/com/android/internal/app/ChooserListAdapter.java +++ b/core/java/com/android/internal/app/ChooserListAdapter.java @@ -82,8 +82,6 @@ public class ChooserListAdapter extends ResolverListAdapter { private static final int MAX_SERVICE_TARGET_APP = 8; private static final int DEFAULT_DIRECT_SHARE_RANKING_SCORE = 1000; - static final int MAX_SERVICE_TARGETS = 8; - /** {@link #getBaseScore} */ public static final float CALLER_TARGET_SCORE_BOOST = 900.f; /** {@link #getBaseScore} */ @@ -130,10 +128,10 @@ public class ChooserListAdapter extends ResolverListAdapter { super(context, payloadIntents, null, rList, filterLastUsed, resolverListController, chooserListCommunicator, false); - createPlaceHolders(); mMaxShortcutTargetsPerApp = context.getResources().getInteger(R.integer.config_maxShortcutTargetsPerApp); mChooserListCommunicator = chooserListCommunicator; + createPlaceHolders(); mSelectableTargetInfoCommunicator = selectableTargetInfoCommunicator; if (initialIntents != null) { @@ -227,7 +225,7 @@ public class ChooserListAdapter extends ResolverListAdapter { mParkingDirectShareTargets.clear(); mPendingChooserTargetService.clear(); mShortcutComponents.clear(); - for (int i = 0; i < MAX_SERVICE_TARGETS; i++) { + for (int i = 0; i < mChooserListCommunicator.getMaxRankedTargets(); i++) { mServiceTargets.add(mPlaceHolderTargetInfo); } } @@ -382,7 +380,7 @@ public class ChooserListAdapter extends ResolverListAdapter { public int getServiceTargetCount() { if (mChooserListCommunicator.isSendAction(mChooserListCommunicator.getTargetIntent()) && !ActivityManager.isLowRamDeviceStatic()) { - return Math.min(mServiceTargets.size(), MAX_SERVICE_TARGETS); + return Math.min(mServiceTargets.size(), mChooserListCommunicator.getMaxRankedTargets()); } return 0; @@ -847,7 +845,8 @@ public class ChooserListAdapter extends ResolverListAdapter { int currentSize = mServiceTargets.size(); final float newScore = chooserTargetInfo.getModifiedScore(); - for (int i = 0; i < Math.min(currentSize, MAX_SERVICE_TARGETS); i++) { + for (int i = 0; i < Math.min(currentSize, mChooserListCommunicator.getMaxRankedTargets()); + i++) { final ChooserTargetInfo serviceTarget = mServiceTargets.get(i); if (serviceTarget == null) { mServiceTargets.set(i, chooserTargetInfo); @@ -858,7 +857,7 @@ public class ChooserListAdapter extends ResolverListAdapter { } } - if (currentSize < MAX_SERVICE_TARGETS) { + if (currentSize < mChooserListCommunicator.getMaxRankedTargets()) { mServiceTargets.add(chooserTargetInfo); return true; } diff --git a/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java b/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java index 3a65a324f9d6..dd837fc2194c 100644 --- a/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java +++ b/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java @@ -39,17 +39,19 @@ public class ChooserMultiProfilePagerAdapter extends AbstractMultiProfilePagerAd private final ChooserProfileDescriptor[] mItems; private final boolean mIsSendAction; private int mBottomOffset; + private int mMaxTargetsPerRow; ChooserMultiProfilePagerAdapter(Context context, ChooserActivity.ChooserGridAdapter adapter, UserHandle personalProfileUserHandle, UserHandle workProfileUserHandle, - boolean isSendAction) { + boolean isSendAction, int maxTargetsPerRow) { super(context, /* currentPage */ 0, personalProfileUserHandle, workProfileUserHandle); mItems = new ChooserProfileDescriptor[] { createProfileDescriptor(adapter) }; mIsSendAction = isSendAction; + mMaxTargetsPerRow = maxTargetsPerRow; } ChooserMultiProfilePagerAdapter(Context context, @@ -58,7 +60,7 @@ public class ChooserMultiProfilePagerAdapter extends AbstractMultiProfilePagerAd @Profile int defaultProfile, UserHandle personalProfileUserHandle, UserHandle workProfileUserHandle, - boolean isSendAction) { + boolean isSendAction, int maxTargetsPerRow) { super(context, /* currentPage */ defaultProfile, personalProfileUserHandle, workProfileUserHandle); mItems = new ChooserProfileDescriptor[] { @@ -66,6 +68,7 @@ public class ChooserMultiProfilePagerAdapter extends AbstractMultiProfilePagerAd createProfileDescriptor(workAdapter) }; mIsSendAction = isSendAction; + mMaxTargetsPerRow = maxTargetsPerRow; } private ChooserProfileDescriptor createProfileDescriptor( @@ -114,7 +117,7 @@ public class ChooserMultiProfilePagerAdapter extends AbstractMultiProfilePagerAd ChooserActivity.ChooserGridAdapter chooserGridAdapter = getItem(pageIndex).chooserGridAdapter; GridLayoutManager glm = (GridLayoutManager) recyclerView.getLayoutManager(); - glm.setSpanCount(chooserGridAdapter.getMaxTargetsPerRow()); + glm.setSpanCount(mMaxTargetsPerRow); glm.setSpanSizeLookup( new GridLayoutManager.SpanSizeLookup() { @Override diff --git a/core/java/com/android/internal/view/IInputConnectionWrapper.java b/core/java/com/android/internal/view/IInputConnectionWrapper.java index 4deb40a0d772..1d5935db6322 100644 --- a/core/java/com/android/internal/view/IInputConnectionWrapper.java +++ b/core/java/com/android/internal/view/IInputConnectionWrapper.java @@ -27,6 +27,9 @@ import android.os.Message; import android.os.RemoteException; import android.os.Trace; import android.util.Log; +import android.util.imetracing.ImeTracing; +import android.util.imetracing.InputConnectionHelper; +import android.util.proto.ProtoOutputStream; import android.view.KeyEvent; import android.view.inputmethod.CompletionInfo; import android.view.inputmethod.CorrectionInfo; @@ -36,6 +39,7 @@ import android.view.inputmethod.InputConnection; import android.view.inputmethod.InputConnectionInspector; import android.view.inputmethod.InputConnectionInspector.MissingMethodFlags; import android.view.inputmethod.InputContentInfo; +import android.view.inputmethod.InputMethodManager; import android.view.inputmethod.SurroundingText; import com.android.internal.annotations.GuardedBy; @@ -124,7 +128,9 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub { } } - abstract protected boolean isActive(); + protected abstract boolean isActive(); + + protected abstract InputMethodManager getIMM(); public void getTextAfterCursor(int length, int flags, ICharSequenceResultCallback callback) { dispatchMessage(mH.obtainMessage(DO_GET_TEXT_AFTER_CURSOR, length, flags, callback)); @@ -264,6 +270,7 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub { } void executeMessage(Message msg) { + ProtoOutputStream icProto; switch (msg.what) { case DO_GET_TEXT_AFTER_CURSOR: { Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#getTextAfterCursor"); @@ -278,6 +285,12 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub { } else { result = ic.getTextAfterCursor(msg.arg1, msg.arg2); } + if (ImeTracing.getInstance().isEnabled()) { + icProto = InputConnectionHelper.buildGetTextAfterCursorProto(msg.arg1, + msg.arg2, result); + ImeTracing.getInstance().triggerClientDump( + TAG + "#getTextAfterCursor", getIMM(), icProto); + } try { callback.onResult(result); } catch (RemoteException e) { @@ -302,6 +315,12 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub { } else { result = ic.getTextBeforeCursor(msg.arg1, msg.arg2); } + if (ImeTracing.getInstance().isEnabled()) { + icProto = InputConnectionHelper.buildGetTextBeforeCursorProto(msg.arg1, + msg.arg2, result); + ImeTracing.getInstance().triggerClientDump( + TAG + "#getTextBeforeCursor", getIMM(), icProto); + } try { callback.onResult(result); } catch (RemoteException e) { @@ -326,6 +345,11 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub { } else { result = ic.getSelectedText(msg.arg1); } + if (ImeTracing.getInstance().isEnabled()) { + icProto = InputConnectionHelper.buildGetSelectedTextProto(msg.arg1, result); + ImeTracing.getInstance().triggerClientDump( + TAG + "#getSelectedText", getIMM(), icProto); + } try { callback.onResult(result); } catch (RemoteException e) { @@ -354,6 +378,12 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub { } else { result = ic.getSurroundingText(beforeLength, afterLength, flags); } + if (ImeTracing.getInstance().isEnabled()) { + icProto = InputConnectionHelper.buildGetSurroundingTextProto(beforeLength, + afterLength, flags, result); + ImeTracing.getInstance().triggerClientDump( + TAG + "#getSurroundingText", getIMM(), icProto); + } try { callback.onResult(result); } catch (RemoteException e) { @@ -378,6 +408,12 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub { } else { result = ic.getCursorCapsMode(msg.arg1); } + if (ImeTracing.getInstance().isEnabled()) { + icProto = InputConnectionHelper.buildGetCursorCapsModeProto(msg.arg1, + result); + ImeTracing.getInstance().triggerClientDump( + TAG + "#getCursorCapsMode", getIMM(), icProto); + } try { callback.onResult(result); } catch (RemoteException e) { @@ -404,6 +440,12 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub { } else { result = ic.getExtractedText(request, msg.arg1); } + if (ImeTracing.getInstance().isEnabled()) { + icProto = InputConnectionHelper.buildGetExtractedTextProto(request, + msg.arg1, result); + ImeTracing.getInstance().triggerClientDump( + TAG + "#getExtractedText", getIMM(), icProto); + } try { callback.onResult(result); } catch (RemoteException e) { diff --git a/core/java/com/android/internal/view/InputConnectionWrapper.java b/core/java/com/android/internal/view/InputConnectionWrapper.java index 8c763a6efe54..b70348a95384 100644 --- a/core/java/com/android/internal/view/InputConnectionWrapper.java +++ b/core/java/com/android/internal/view/InputConnectionWrapper.java @@ -24,6 +24,9 @@ import android.inputmethodservice.AbstractInputMethodService; import android.os.Bundle; import android.os.Handler; import android.os.RemoteException; +import android.util.imetracing.ImeTracing; +import android.util.imetracing.InputConnectionHelper; +import android.util.proto.ProtoOutputStream; import android.view.KeyEvent; import android.view.inputmethod.CompletionInfo; import android.view.inputmethod.CorrectionInfo; @@ -87,8 +90,18 @@ public class InputConnectionWrapper implements InputConnection { } catch (RemoteException e) { return null; } - return Completable.getResultOrNull( + CharSequence result = Completable.getResultOrNull( value, TAG, "getTextAfterCursor()", mCancellationGroup, MAX_WAIT_TIME_MILLIS); + + final AbstractInputMethodService inputMethodService = mInputMethodService.get(); + if (inputMethodService != null && ImeTracing.getInstance().isEnabled()) { + ProtoOutputStream icProto = InputConnectionHelper.buildGetTextAfterCursorProto(length, + flags, result); + ImeTracing.getInstance().triggerServiceDump(TAG + "#getTextAfterCursor", + inputMethodService, icProto); + } + + return result; } /** @@ -107,8 +120,18 @@ public class InputConnectionWrapper implements InputConnection { } catch (RemoteException e) { return null; } - return Completable.getResultOrNull( + CharSequence result = Completable.getResultOrNull( value, TAG, "getTextBeforeCursor()", mCancellationGroup, MAX_WAIT_TIME_MILLIS); + + final AbstractInputMethodService inputMethodService = mInputMethodService.get(); + if (inputMethodService != null && ImeTracing.getInstance().isEnabled()) { + ProtoOutputStream icProto = InputConnectionHelper.buildGetTextBeforeCursorProto(length, + flags, result); + ImeTracing.getInstance().triggerServiceDump(TAG + "#getTextBeforeCursor", + inputMethodService, icProto); + } + + return result; } @AnyThread @@ -127,8 +150,18 @@ public class InputConnectionWrapper implements InputConnection { } catch (RemoteException e) { return null; } - return Completable.getResultOrNull( + CharSequence result = Completable.getResultOrNull( value, TAG, "getSelectedText()", mCancellationGroup, MAX_WAIT_TIME_MILLIS); + + final AbstractInputMethodService inputMethodService = mInputMethodService.get(); + if (inputMethodService != null && ImeTracing.getInstance().isEnabled()) { + ProtoOutputStream icProto = InputConnectionHelper.buildGetSelectedTextProto(flags, + result); + ImeTracing.getInstance().triggerServiceDump(TAG + "#getSelectedText", + inputMethodService, icProto); + } + + return result; } /** @@ -161,8 +194,18 @@ public class InputConnectionWrapper implements InputConnection { } catch (RemoteException e) { return null; } - return Completable.getResultOrNull( + SurroundingText result = Completable.getResultOrNull( value, TAG, "getSurroundingText()", mCancellationGroup, MAX_WAIT_TIME_MILLIS); + + final AbstractInputMethodService inputMethodService = mInputMethodService.get(); + if (inputMethodService != null && ImeTracing.getInstance().isEnabled()) { + ProtoOutputStream icProto = InputConnectionHelper.buildGetSurroundingTextProto( + beforeLength, afterLength, flags, result); + ImeTracing.getInstance().triggerServiceDump(TAG + "#getSurroundingText", + inputMethodService, icProto); + } + + return result; } @AnyThread @@ -177,8 +220,18 @@ public class InputConnectionWrapper implements InputConnection { } catch (RemoteException e) { return 0; } - return Completable.getResultOrZero( + int result = Completable.getResultOrZero( value, TAG, "getCursorCapsMode()", mCancellationGroup, MAX_WAIT_TIME_MILLIS); + + final AbstractInputMethodService inputMethodService = mInputMethodService.get(); + if (inputMethodService != null && ImeTracing.getInstance().isEnabled()) { + ProtoOutputStream icProto = InputConnectionHelper.buildGetCursorCapsModeProto( + reqModes, result); + ImeTracing.getInstance().triggerServiceDump(TAG + "#getCursorCapsMode", + inputMethodService, icProto); + } + + return result; } @AnyThread @@ -193,8 +246,18 @@ public class InputConnectionWrapper implements InputConnection { } catch (RemoteException e) { return null; } - return Completable.getResultOrNull( + ExtractedText result = Completable.getResultOrNull( value, TAG, "getExtractedText()", mCancellationGroup, MAX_WAIT_TIME_MILLIS); + + final AbstractInputMethodService inputMethodService = mInputMethodService.get(); + if (inputMethodService != null && ImeTracing.getInstance().isEnabled()) { + ProtoOutputStream icProto = InputConnectionHelper.buildGetExtractedTextProto( + request, flags, result); + ImeTracing.getInstance().triggerServiceDump(TAG + "#getExtractedText", + inputMethodService, icProto); + } + + return result; } @AnyThread diff --git a/core/java/com/android/internal/widget/EditableInputConnection.java b/core/java/com/android/internal/widget/EditableInputConnection.java index d30d662806d4..6cdf0df17b65 100644 --- a/core/java/com/android/internal/widget/EditableInputConnection.java +++ b/core/java/com/android/internal/widget/EditableInputConnection.java @@ -28,6 +28,7 @@ import android.text.Editable; import android.text.Selection; import android.text.method.KeyListener; import android.util.Log; +import android.util.imetracing.InputConnectionHelper; import android.util.proto.ProtoOutputStream; import android.view.inputmethod.BaseInputConnection; import android.view.inputmethod.CompletionInfo; @@ -45,7 +46,6 @@ import android.widget.TextView; public class EditableInputConnection extends BaseInputConnection implements DumpableInputConnection { private static final boolean DEBUG = false; - private static final boolean DUMP_TEXT = false; private static final String TAG = "EditableInputConnection"; private final TextView mTextView; @@ -243,7 +243,7 @@ public class EditableInputConnection extends BaseInputConnection final long token = proto.start(fieldId); CharSequence editableText = mTextView.getText(); CharSequence selectedText = getSelectedText(0 /* flags */); - if (DUMP_TEXT) { + if (InputConnectionHelper.DUMP_TEXT) { if (editableText != null) { proto.write(EDITABLE_TEXT, editableText.toString()); } diff --git a/core/java/com/android/internal/widget/LockPatternView.java b/core/java/com/android/internal/widget/LockPatternView.java index 4ddc782aacb4..bad21d28416b 100644 --- a/core/java/com/android/internal/widget/LockPatternView.java +++ b/core/java/com/android/internal/widget/LockPatternView.java @@ -1090,6 +1090,20 @@ public class LockPatternView extends View { } } + /** + * Change theme colors + * @param regularColor The dot color + * @param successColor Color used when pattern is correct + * @param errorColor Color used when authentication fails + */ + public void setColors(int regularColor, int successColor, int errorColor) { + mRegularColor = regularColor; + mErrorColor = errorColor; + mSuccessColor = successColor; + mPathPaint.setColor(regularColor); + invalidate(); + } + private float getCenterXForColumn(int column) { return mPaddingLeft + column * mSquareWidth + mSquareWidth / 2f; } diff --git a/core/proto/android/inputmethodservice/inputmethodservice.proto b/core/proto/android/inputmethodservice/inputmethodservice.proto index e5d171361695..1f68fb4b513d 100644 --- a/core/proto/android/inputmethodservice/inputmethodservice.proto +++ b/core/proto/android/inputmethodservice/inputmethodservice.proto @@ -18,6 +18,7 @@ syntax = "proto2"; import "frameworks/base/core/proto/android/inputmethodservice/softinputwindow.proto"; import "frameworks/base/core/proto/android/view/inputmethod/editorinfo.proto"; +import "frameworks/base/core/proto/android/view/inputmethod/inputconnection.proto"; package android.inputmethodservice; @@ -51,6 +52,7 @@ message InputMethodServiceProto { optional int32 status_icon = 25; optional InsetsProto last_computed_insets = 26; optional string settings_observer = 27; + optional .android.view.inputmethod.InputConnectionCallProto input_connection_call = 28; message InsetsProto { optional int32 content_top_insets = 1; diff --git a/core/proto/android/view/inputmethod/inputconnection.proto b/core/proto/android/view/inputmethod/inputconnection.proto index ad9a95aa95e6..d1f257ff2c5c 100644 --- a/core/proto/android/view/inputmethod/inputconnection.proto +++ b/core/proto/android/view/inputmethod/inputconnection.proto @@ -31,4 +31,68 @@ message InputConnectionProto { optional int32 selected_text_start = 3; optional int32 selected_text_end = 4; optional int32 cursor_caps_mode = 5; +} + +/** + * Shows information about parameters and result for method calls to + * {@link android.view.inputmethod.InputConnection}. + */ +message InputConnectionCallProto { + oneof method_call { + GetTextBeforeCursor get_text_before_cursor = 1; + GetTextAfterCursor get_text_after_cursor = 2; + GetSelectedText get_selected_text = 3; + GetSurroundingText get_surrounding_text = 4; + GetCursorCapsMode get_cursor_caps_mode = 5; + GetExtractedText get_extracted_text = 6; + } + + message GetTextBeforeCursor { + optional int32 length = 1; + optional int32 flags = 2; + optional string result = 3 [(.android.privacy).dest = DEST_LOCAL]; + } + + message GetTextAfterCursor { + optional int32 length = 1; + optional int32 flags = 2; + optional string result = 3 [(.android.privacy).dest = DEST_LOCAL]; + } + + message GetSelectedText { + optional int32 flags = 1; + optional string result = 2 [(.android.privacy).dest = DEST_LOCAL]; + } + + message GetSurroundingText { + optional int32 before_length = 1; + optional int32 after_length = 2; + optional int32 flags = 3; + optional SurroundingText result = 4; + + message SurroundingText { + optional string text = 1 [(.android.privacy).dest = DEST_LOCAL]; + optional int32 selection_start = 2; + optional int32 selection_end = 3; + optional int32 offset = 4; + } + } + + message GetCursorCapsMode { + optional int32 req_modes = 1; + optional int32 result = 2; + } + + message GetExtractedText { + optional ExtractedTextRequest request = 1; + optional int32 flags = 2; + optional string result = 3 [(.android.privacy).dest = DEST_LOCAL]; + + message ExtractedTextRequest { + optional int32 token = 1; + optional int32 flags = 2; + optional int32 hint_max_lines = 3; + optional int32 hint_max_chars = 4; + } + } }
\ No newline at end of file diff --git a/core/proto/android/view/inputmethod/inputmethodeditortrace.proto b/core/proto/android/view/inputmethod/inputmethodeditortrace.proto index c1dce6f2d093..8e4377ca124c 100644 --- a/core/proto/android/view/inputmethod/inputmethodeditortrace.proto +++ b/core/proto/android/view/inputmethod/inputmethodeditortrace.proto @@ -72,6 +72,7 @@ message InputMethodClientsTraceProto { optional EditorInfoProto editor_info = 6; optional ImeFocusControllerProto ime_focus_controller = 7; optional InputConnectionProto input_connection = 8; + optional InputConnectionCallProto input_connection_call = 9; } } diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 60dd0eb8a780..1250eb776176 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -4047,6 +4047,13 @@ <permission android:name="android.permission.OVERRIDE_DISPLAY_MODE_REQUESTS" android:protectionLevel="signature" /> + <!-- Allows an application to modify the refresh rate switching type. This + matches Setting.Secure.MATCH_CONTENT_FRAME_RATE. + @hide + @TestApi --> + <permission android:name="android.permission.MODIFY_REFRESH_RATE_SWITCHING_TYPE" + android:protectionLevel="signature" /> + <!-- @SystemApi Allows an application to control VPN. <p>Not for use by third-party applications.</p> @hide --> @@ -5255,7 +5262,8 @@ <attribution android:tag="TwilightService" android:label="@string/twilight_service"/> <!-- Attribution for the Offline LocationTimeZoneProvider, used to detect time zone using on-device data --> - <attribution android:tag="OfflineLocationTimeZoneProvider" android:label="@string/offline_location_time_zone_detection_service"/> + <attribution android:tag="OfflineLocationTimeZoneProvider" + android:label="@string/offline_location_time_zone_detection_service_attribution"/> <application android:process="system" android:persistent="true" diff --git a/core/res/res/values-night/colors.xml b/core/res/res/values-night/colors.xml index 39cdce9cc46b..29f2b6f14b57 100644 --- a/core/res/res/values-night/colors.xml +++ b/core/res/res/values-night/colors.xml @@ -26,9 +26,6 @@ <color name="notification_default_color_dark">#ddffffff</color> - <!-- The background color of a notification card. --> - <color name="notification_material_background_color">@color/black</color> - <color name="chooser_row_divider">@color/list_divider_color_dark</color> <color name="chooser_gradient_background">@color/loading_gradient_background_color_dark</color> <color name="chooser_gradient_highlight">@color/loading_gradient_highlight_color_dark</color> diff --git a/core/res/res/values-night/values.xml b/core/res/res/values-night/values.xml index 4e6b712f1f5d..952cdd08451c 100644 --- a/core/res/res/values-night/values.xml +++ b/core/res/res/values-night/values.xml @@ -25,18 +25,12 @@ <item name="colorControlNormal">?attr/textColorPrimary</item> <item name="alertDialogTheme">@style/Theme.DeviceDefault.Dialog.Alert</item> <item name="forceDarkAllowed">false</item> - - <!-- QS panel background --> - <item name="colorBackgroundFloating">@color/black</item> - - <!-- volume background --> - <item name="panelColorBackground">@color/material_grey_800</item> </style> <style name="Theme.DeviceDefault.QuickSettings.Dialog" parent="Theme.DeviceDefault.Dialog" /> <style name="TextAppearance.Material.Notification"> - <item name="textColor">@color/notification_secondary_text_color_dark</item> + <item name="textColor">?attr/textColorPrimary</item> <item name="textSize">@dimen/notification_text_size</item> </style> </resources>
\ No newline at end of file diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml index 1242c6dc8217..0079d8cd0276 100644 --- a/core/res/res/values/colors.xml +++ b/core/res/res/values/colors.xml @@ -143,8 +143,6 @@ <color name="notification_default_color_dark">@color/primary_text_default_material_light</color> <color name="notification_default_color_light">#a3202124</color> - <color name="notification_material_background_color">#ffffffff</color> - <color name="notification_default_color">#757575</color> <!-- Gray 600 --> <color name="notification_action_button_text_color">@color/notification_default_color</color> diff --git a/core/res/res/values/colors_device_defaults.xml b/core/res/res/values/colors_device_defaults.xml index 7a8f411992ce..310ca893ac39 100644 --- a/core/res/res/values/colors_device_defaults.xml +++ b/core/res/res/values/colors_device_defaults.xml @@ -37,10 +37,10 @@ <color name="accent_device_default_dark">@color/accent_material_dark</color> <color name="accent_device_default">@color/accent_device_default_light</color> - <color name="background_device_default_dark">@color/background_material_dark</color> - <color name="background_device_default_light">@color/background_material_light</color> - <color name="background_floating_device_default_dark">@color/background_floating_material_dark</color> - <color name="background_floating_device_default_light">@color/background_floating_material_light</color> + <color name="background_device_default_dark">#1A1A1A</color> + <color name="background_device_default_light">#F2F2F2</color> + <color name="background_floating_device_default_dark">#0D0D0D</color> + <color name="background_floating_device_default_light">#CCCCCC</color> <!-- Error color --> <color name="error_color_device_default_dark">@color/error_color_material_dark</color> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index f8cbfebabdcf..84e6f12c0b51 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -1559,6 +1559,14 @@ <!-- Class name of WallpaperManagerService. --> <string name="config_wallpaperManagerServiceName" translatable="false">com.android.server.wallpaper.WallpaperManagerService</string> + <!-- Specifies priority of automatic time sources. Suggestions from higher entries in the list + take precedence over lower ones. + See com.android.server.timedetector.TimeDetectorStrategy for available sources. --> + <string-array name="config_autoTimeSourcesPriority"> + <item>telephony</item> + <item>network</item> + </string-array> + <!-- Enables the TimeZoneRuleManager service. This is the global switch for the updateable time zone update mechanism. --> <bool name="config_enableUpdateableTimeZoneRules">false</bool> @@ -4554,4 +4562,7 @@ <!-- Indicates that default fitness tracker app needs to request sensor and location permissions. --> <bool name="config_trackerAppNeedsPermissions">false</bool> + + <!-- Component with platform query permissions for AppSearch --> + <string name="config_defaultAppSearchPlatformQuerierComponent" translatable="false"></string> </resources> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 8e3a0cbdd10c..89b986b8fcb8 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -433,9 +433,12 @@ <string name="sensor_notification_service">Sensor Notification Service</string> <!-- Attribution for Twilight service. [CHAR LIMIT=NONE]--> <string name="twilight_service">Twilight Service</string> - <!-- Attribution for Offline LocationTimeZoneDetector service, i.e. one capable of performing - time zone lookup using geo-spacial information held on the device. [CHAR LIMIT=NONE]--> - <string name="offline_location_time_zone_detection_service">Offline Time Zone Detection Service</string> + <!-- Attribution for the Offline LocationTimeZoneProvider service, i.e. the service capable of + performing time zone detection using time zone geospatial information held on the device. + This text is shown in UIs related to an application name to help users and developers to + understand which sub-unit of an application is requesting permissions and using power. + [CHAR LIMIT=NONE]--> + <string name="offline_location_time_zone_detection_service_attribution">Time Zone Detector (No connectivity)</string> <!-- Factory reset warning dialog strings--> <skip /> <!-- Shows up in the dialog's title to warn about an impeding factory reset. [CHAR LIMIT=NONE] --> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 84556d41a07e..cd00fbf204cf 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -2166,6 +2166,7 @@ <java-symbol type="string" name="config_defaultNetworkScorerPackageName" /> <java-symbol type="string" name="config_persistentDataPackageName" /> <java-symbol type="string" name="config_deviceConfiguratorPackageName" /> + <java-symbol type="array" name="config_autoTimeSourcesPriority" /> <java-symbol type="bool" name="config_enableGeolocationTimeZoneDetection" /> <java-symbol type="bool" name="config_enablePrimaryLocationTimeZoneOverlay" /> <java-symbol type="string" name="config_primaryLocationTimeZoneProviderPackageName" /> @@ -3002,7 +3003,6 @@ <java-symbol type="string" name="usb_mtp_launch_notification_description" /> <java-symbol type="color" name="notification_action_list" /> - <java-symbol type="color" name="notification_material_background_color" /> <!-- Resolver target actions --> <java-symbol type="array" name="resolver_target_actions_pin" /> @@ -4113,4 +4113,6 @@ <java-symbol type="bool" name="config_trackerAppNeedsPermissions"/> + <!-- Component with platform query permissions for AppSearch --> + <java-symbol type="string" name="config_defaultAppSearchPlatformQuerierComponent" /> </resources> diff --git a/core/res/res/values/themes_device_defaults.xml b/core/res/res/values/themes_device_defaults.xml index 1afaf4f7f184..c0731c82bff3 100644 --- a/core/res/res/values/themes_device_defaults.xml +++ b/core/res/res/values/themes_device_defaults.xml @@ -215,8 +215,10 @@ easier. <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item> <item name="colorAccent">@color/accent_device_default_dark</item> <item name="colorError">@color/error_color_device_default_dark</item> + <item name="colorBackground">@color/background_device_default_dark</item> <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item> <item name="colorPopupBackground">?attr/colorBackgroundFloating</item> + <item name="panelColorBackground">?attr/colorBackgroundFloating</item> </style> <style name="Theme.DeviceDefault" parent="Theme.DeviceDefaultBase" /> @@ -943,8 +945,10 @@ easier. <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item> <item name="colorAccent">@color/accent_device_default_light</item> <item name="colorError">@color/error_color_device_default_light</item> + <item name="colorBackground">@color/background_device_default_light</item> <item name="colorBackgroundFloating">@color/background_floating_device_default_light</item> <item name="colorPopupBackground">?attr/colorBackgroundFloating</item> + <item name="panelColorBackground">?attr/colorBackgroundFloating</item> </style> <!-- Variant of the DeviceDefault (light) theme that has a solid (opaque) action bar with an @@ -1517,9 +1521,6 @@ easier. <!-- Toolbar attributes --> <item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item> - - <!-- volume background --> - <item name="panelColorBackground">@color/primary_material_light</item> </style> <style name="Theme.DeviceDefault.QuickSettings.Dialog" parent="Theme.DeviceDefault.Light.Dialog"> diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java index 5871e2e04687..7e992989426d 100644 --- a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java +++ b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java @@ -52,6 +52,7 @@ import android.os.Parcelable; import android.os.PersistableBundle; import android.os.RemoteCallback; import android.os.RemoteException; +import android.os.SharedMemory; import android.platform.test.annotations.Presubmit; import android.view.DisplayAdjustments.FixedRotationAdjustments; import android.view.DisplayCutout; @@ -440,7 +441,8 @@ public class TransactionParcelTests { IUiAutomationConnection iUiAutomationConnection, int i, boolean b, boolean b1, boolean b2, boolean b3, Configuration configuration, CompatibilityInfo compatibilityInfo, Map map, Bundle bundle1, String s1, - AutofillOptions ao, ContentCaptureOptions co, long[] disableCompatChanges) + AutofillOptions ao, ContentCaptureOptions co, long[] disableCompatChanges, + SharedMemory serializedSystemFontMap) throws RemoteException { } diff --git a/core/tests/uwbtests/src/android/uwb/UwbManagerTest.java b/core/tests/uwbtests/src/android/uwb/UwbManagerTest.java new file mode 100644 index 000000000000..4983bed742fd --- /dev/null +++ b/core/tests/uwbtests/src/android/uwb/UwbManagerTest.java @@ -0,0 +1,49 @@ +/* + * Copyright 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.uwb; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; + +import android.content.Context; + +import androidx.test.InstrumentationRegistry; +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.filters.SmallTest; + +import org.junit.Test; +import org.junit.runner.RunWith; + +/** + * Test of {@link UwbManager}. + */ +@SmallTest +@RunWith(AndroidJUnit4.class) +public class UwbManagerTest { + + public final Context mContext = InstrumentationRegistry.getContext(); + + @Test + public void testServiceAvailable() { + UwbManager manager = mContext.getSystemService(UwbManager.class); + if (UwbTestUtils.isUwbSupported(mContext)) { + assertNotNull(manager); + } else { + assertNull(manager); + } + } +} diff --git a/core/tests/uwbtests/src/android/uwb/UwbTestUtils.java b/core/tests/uwbtests/src/android/uwb/UwbTestUtils.java index 62e0b629539b..fb7509248b38 100644 --- a/core/tests/uwbtests/src/android/uwb/UwbTestUtils.java +++ b/core/tests/uwbtests/src/android/uwb/UwbTestUtils.java @@ -16,6 +16,8 @@ package android.uwb; +import android.content.Context; +import android.content.pm.PackageManager; import android.os.SystemClock; import java.util.ArrayList; @@ -24,6 +26,11 @@ import java.util.List; public class UwbTestUtils { private UwbTestUtils() {} + public static boolean isUwbSupported(Context context) { + PackageManager packageManager = context.getPackageManager(); + return packageManager.hasSystemFeature(PackageManager.FEATURE_UWB); + } + public static AngleMeasurement getAngleMeasurement() { return new AngleMeasurement.Builder() .setRadians(getDoubleInRange(-Math.PI, Math.PI)) diff --git a/graphics/java/android/graphics/FrameInfo.java b/graphics/java/android/graphics/FrameInfo.java index 0061ea13f647..d59abb5916a0 100644 --- a/graphics/java/android/graphics/FrameInfo.java +++ b/graphics/java/android/graphics/FrameInfo.java @@ -43,7 +43,7 @@ public final class FrameInfo { public long[] frameInfo = new long[FRAME_INFO_SIZE]; // Various flags set to provide extra metadata about the current frame - private static final int FLAGS = 0; + public static final int FLAGS = 0; // Is this the first-draw following a window layout? public static final long FLAG_WINDOW_LAYOUT_CHANGED = 1; @@ -60,35 +60,35 @@ public final class FrameInfo { @Retention(RetentionPolicy.SOURCE) public @interface FrameInfoFlags {} - private static final int FRAME_TIMELINE_VSYNC_ID = 1; + public static final int FRAME_TIMELINE_VSYNC_ID = 1; // The intended vsync time, unadjusted by jitter - private static final int INTENDED_VSYNC = 2; + public static final int INTENDED_VSYNC = 2; // Jitter-adjusted vsync time, this is what was used as input into the // animation & drawing system - private static final int VSYNC = 3; + public static final int VSYNC = 3; // The time of the oldest input event - private static final int OLDEST_INPUT_EVENT = 4; + public static final int OLDEST_INPUT_EVENT = 4; // The time of the newest input event - private static final int NEWEST_INPUT_EVENT = 5; + public static final int NEWEST_INPUT_EVENT = 5; // When input event handling started - private static final int HANDLE_INPUT_START = 6; + public static final int HANDLE_INPUT_START = 6; // When animation evaluations started - private static final int ANIMATION_START = 7; + public static final int ANIMATION_START = 7; // When ViewRootImpl#performTraversals() started - private static final int PERFORM_TRAVERSALS_START = 8; + public static final int PERFORM_TRAVERSALS_START = 8; // When View:draw() started - private static final int DRAW_START = 9; + public static final int DRAW_START = 9; // When the frame needs to be ready by - private static final int FRAME_DEADLINE = 10; + public static final int FRAME_DEADLINE = 10; // Must be the last one private static final int FRAME_INFO_SIZE = FRAME_DEADLINE + 1; @@ -99,23 +99,11 @@ public final class FrameInfo { frameInfo[FRAME_TIMELINE_VSYNC_ID] = frameTimelineVsyncId; frameInfo[INTENDED_VSYNC] = intendedVsync; frameInfo[VSYNC] = usedVsync; - frameInfo[OLDEST_INPUT_EVENT] = Long.MAX_VALUE; - frameInfo[NEWEST_INPUT_EVENT] = 0; frameInfo[FLAGS] = 0; frameInfo[FRAME_DEADLINE] = frameDeadline; } /** checkstyle */ - public void updateInputEventTime(long inputEventTime, long inputEventOldestTime) { - if (inputEventOldestTime < frameInfo[OLDEST_INPUT_EVENT]) { - frameInfo[OLDEST_INPUT_EVENT] = inputEventOldestTime; - } - if (inputEventTime > frameInfo[NEWEST_INPUT_EVENT]) { - frameInfo[NEWEST_INPUT_EVENT] = inputEventTime; - } - } - - /** checkstyle */ public void markInputHandlingStart() { frameInfo[HANDLE_INPUT_START] = System.nanoTime(); } @@ -131,13 +119,7 @@ public final class FrameInfo { } /** checkstyle */ - public void markDrawStart() { - frameInfo[DRAW_START] = System.nanoTime(); - } - - /** checkstyle */ public void addFlags(@FrameInfoFlags long flags) { frameInfo[FLAGS] |= flags; } - } diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java index 36ef0a48fa20..24987daf87b5 100644 --- a/graphics/java/android/graphics/Typeface.java +++ b/graphics/java/android/graphics/Typeface.java @@ -81,6 +81,9 @@ public class Typeface { private static String TAG = "Typeface"; + /** @hide */ + public static final boolean ENABLE_LAZY_TYPEFACE_INITIALIZATION = false; + private static final NativeAllocationRegistry sRegistry = NativeAllocationRegistry.createMalloced( Typeface.class.getClassLoader(), nativeGetReleaseFunc()); @@ -1329,7 +1332,9 @@ public class Typeface { } static { - loadPreinstalledSystemFontMap(); + if (!ENABLE_LAZY_TYPEFACE_INITIALIZATION) { + loadPreinstalledSystemFontMap(); + } } @Override diff --git a/libs/WindowManager/Shell/Android.bp b/libs/WindowManager/Shell/Android.bp index 39e32c694d2e..856c9c2484f3 100644 --- a/libs/WindowManager/Shell/Android.bp +++ b/libs/WindowManager/Shell/Android.bp @@ -125,9 +125,10 @@ android_library { "protolog-lib", "SettingsLib", "WindowManager-Shell-proto", + "jsr330" ], kotlincflags: ["-Xjvm-default=enable"], manifest: "AndroidManifest.xml", min_sdk_version: "26", -}
\ No newline at end of file +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/animation/FlingAnimationUtils.java b/libs/WindowManager/Shell/src/com/android/wm/shell/animation/FlingAnimationUtils.java index 357f777e1270..176c620fa119 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/animation/FlingAnimationUtils.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/animation/FlingAnimationUtils.java @@ -23,6 +23,8 @@ import android.view.ViewPropertyAnimator; import android.view.animation.Interpolator; import android.view.animation.PathInterpolator; +import javax.inject.Inject; + /** * Utility class to calculate general fling animation when the finger is released. */ @@ -368,6 +370,7 @@ public class FlingAnimationUtils { float mX2; float mY2; + @Inject public Builder(DisplayMetrics displayMetrics) { mDisplayMetrics = displayMetrics; reset(); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMenuController.java new file mode 100644 index 000000000000..8d9ad4d1b96c --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMenuController.java @@ -0,0 +1,106 @@ +/* + * 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.wm.shell.pip; + +import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; +import static android.view.WindowManager.LayoutParams.FLAG_SLIPPERY; +import static android.view.WindowManager.LayoutParams.FLAG_SPLIT_TOUCH; +import static android.view.WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH; +import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY; +import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; + +import android.annotation.Nullable; +import android.app.ActivityManager.RunningTaskInfo; +import android.app.RemoteAction; +import android.content.pm.ParceledListSlice; +import android.graphics.PixelFormat; +import android.graphics.Rect; +import android.view.SurfaceControl; +import android.view.WindowManager; + +/** + * Interface to allow {@link com.android.wm.shell.pip.PipTaskOrganizer} to call into + * PiP menu when certain events happen (task appear/vanish, PiP move, etc.) + */ +public interface PipMenuController { + + String MENU_WINDOW_TITLE = "PipMenuView"; + + /** + * Called when + * {@link PipTaskOrganizer#onTaskAppeared(RunningTaskInfo, SurfaceControl)} + * is called. + */ + void attach(SurfaceControl leash); + + /** + * Called when + * {@link PipTaskOrganizer#onTaskVanished(RunningTaskInfo)} is called. + */ + void detach(); + + /** + * Check if menu is visible or not. + */ + boolean isMenuVisible(); + + /** + * Show the PIP menu. + */ + void showMenu(); + + /** + * Given a set of actions, update the menu. + */ + void setAppActions(ParceledListSlice<RemoteAction> appActions); + + /** + * Resize the PiP menu with the given bounds. The PiP SurfaceControl is given if there is a + * need to synchronize the movements on the same frame as PiP. + */ + default void resizePipMenu(@Nullable SurfaceControl pipLeash, + @Nullable SurfaceControl.Transaction t, + Rect destinationBounds) {} + + /** + * Move the PiP menu with the given bounds. The PiP SurfaceControl is given if there is a + * need to synchronize the movements on the same frame as PiP. + */ + default void movePipMenu(@Nullable SurfaceControl pipLeash, + @Nullable SurfaceControl.Transaction t, + Rect destinationBounds) {} + + /** + * Update the PiP menu with the given bounds for re-layout purposes. + */ + default void updateMenuBounds(Rect destinationBounds) {} + + /** + * Returns a default LayoutParams for the PIP Menu. + * @param width the PIP stack width. + * @param height the PIP stack height. + */ + default WindowManager.LayoutParams getPipMenuLayoutParams(String title, int width, int height) { + final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(width, height, + TYPE_APPLICATION_OVERLAY, + FLAG_WATCH_OUTSIDE_TOUCH | FLAG_SPLIT_TOUCH | FLAG_SLIPPERY | FLAG_NOT_TOUCHABLE, + PixelFormat.TRANSLUCENT); + lp.privateFlags |= PRIVATE_FLAG_TRUSTED_OVERLAY; + lp.setTitle(title); + return lp; + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java index 7cc2a419354e..98ed822a387f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java @@ -63,7 +63,6 @@ import com.android.internal.os.SomeArgs; import com.android.wm.shell.R; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.DisplayController; -import com.android.wm.shell.pip.phone.PipMenuActivityController; import com.android.wm.shell.pip.phone.PipMotionHelper; import com.android.wm.shell.pip.phone.PipUpdateThread; import com.android.wm.shell.splitscreen.SplitScreen; @@ -135,8 +134,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, private final Handler mUpdateHandler; private final PipBoundsState mPipBoundsState; private final PipBoundsAlgorithm mPipBoundsAlgorithm; - // TODO(b/172286265): Remove dependency on .pip.PHONE.PipMenuActivityController - private final PipMenuActivityController mMenuActivityController; + private final @NonNull PipMenuController mPipMenuController; private final PipAnimationController mPipAnimationController; private final PipUiEventLogger mPipUiEventLoggerLogger; private final List<PipTransitionCallback> mPipTransitionCallbacks = new ArrayList<>(); @@ -264,7 +262,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, public PipTaskOrganizer(Context context, @NonNull PipBoundsState pipBoundsState, @NonNull PipBoundsAlgorithm boundsHandler, - PipMenuActivityController menuActivityController, + @NonNull PipMenuController pipMenuController, @NonNull PipSurfaceTransactionHelper surfaceTransactionHelper, Optional<SplitScreen> splitScreenOptional, @NonNull DisplayController displayController, @@ -274,7 +272,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, mUpdateHandler = new Handler(PipUpdateThread.get().getLooper(), mUpdateCallbacks); mPipBoundsState = pipBoundsState; mPipBoundsAlgorithm = boundsHandler; - mMenuActivityController = menuActivityController; + mPipMenuController = pipMenuController; mEnterExitAnimationDuration = context.getResources() .getInteger(R.integer.config_pipResizeAnimationDuration); mSurfaceTransactionHelper = surfaceTransactionHelper; @@ -501,9 +499,8 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, mOnDisplayIdChangeCallback.accept(info.displayId); } - if (mMenuActivityController != null) { - mMenuActivityController.onTaskAppeared(); - } + mPipMenuController.attach(leash); + if (mShouldIgnoreEnteringPipTransition) { final Rect destinationBounds = mPipBoundsState.getBounds(); @@ -674,9 +671,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, mPictureInPictureParams = null; mState = State.UNDEFINED; mPipUiEventLoggerLogger.setTaskInfo(null); - if (mMenuActivityController != null) { - mMenuActivityController.onTaskVanished(); - } + mPipMenuController.detach(); } @Override @@ -956,9 +951,9 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, mSurfaceTransactionHelper .crop(tx, mLeash, destinationBounds) .round(tx, mLeash, mState.isInPip()); - if (mMenuActivityController != null && mMenuActivityController.isMenuVisible()) { + if (mPipMenuController.isMenuVisible()) { runOnMainHandler(() -> - mMenuActivityController.resizePipMenu(mLeash, tx, destinationBounds)); + mPipMenuController.resizePipMenu(mLeash, tx, destinationBounds)); } else { tx.apply(); } @@ -982,9 +977,9 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction(); mSurfaceTransactionHelper.scale(tx, mLeash, startBounds, destinationBounds); - if (mMenuActivityController != null && mMenuActivityController.isMenuVisible()) { + if (mPipMenuController.isMenuVisible()) { runOnMainHandler(() -> - mMenuActivityController.movePipMenu(mLeash, tx, destinationBounds)); + mPipMenuController.movePipMenu(mLeash, tx, destinationBounds)); } else { tx.apply(); } @@ -1001,8 +996,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, if (direction == TRANSITION_DIRECTION_REMOVE_STACK) { removePipImmediately(); return; - } else if (isInPipDirection(direction) && type == ANIM_TYPE_ALPHA - && mMenuActivityController != null) { + } else if (isInPipDirection(direction) && type == ANIM_TYPE_ALPHA) { // TODO: Synchronize this correctly in #applyEnterPipSyncTransaction finishResizeForMenu(destinationBounds); return; @@ -1015,13 +1009,9 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, } private void finishResizeForMenu(Rect destinationBounds) { - if (mMenuActivityController == null) { - if (DEBUG) Log.d(TAG, "mMenuActivityController is null"); - return; - } runOnMainHandler(() -> { - mMenuActivityController.movePipMenu(null, null, destinationBounds); - mMenuActivityController.updateMenuBounds(destinationBounds); + mPipMenuController.movePipMenu(null, null, destinationBounds); + mPipMenuController.updateMenuBounds(destinationBounds); }); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuActivityController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java index d4217553ef2d..5db8f3d7ef40 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuActivityController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java @@ -18,12 +18,6 @@ package com.android.wm.shell.pip.phone; import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; -import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; -import static android.view.WindowManager.LayoutParams.FLAG_SLIPPERY; -import static android.view.WindowManager.LayoutParams.FLAG_SPLIT_TOUCH; -import static android.view.WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH; -import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY; -import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; import static android.view.WindowManager.SHELL_ROOT_LAYER_PIP; import android.annotation.Nullable; @@ -33,7 +27,6 @@ import android.app.RemoteAction; import android.content.Context; import android.content.pm.ParceledListSlice; import android.graphics.Matrix; -import android.graphics.PixelFormat; import android.graphics.Rect; import android.graphics.RectF; import android.os.Debug; @@ -44,27 +37,26 @@ import android.view.MotionEvent; import android.view.SurfaceControl; import android.view.SyncRtSurfaceTransactionApplier; import android.view.SyncRtSurfaceTransactionApplier.SurfaceParams; -import android.view.WindowManager; import android.view.WindowManagerGlobal; import com.android.wm.shell.common.SystemWindows; import com.android.wm.shell.pip.PipMediaController; import com.android.wm.shell.pip.PipMediaController.ActionListener; +import com.android.wm.shell.pip.PipMenuController; import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; /** - * Manages the PiP menu activity which can show menu options or a scrim. + * Manages the PiP menu view which can show menu options or a scrim. * * The current media session provides actions whenever there are no valid actions provided by the * current PiP activity. Otherwise, those actions always take precedence. */ -public class PipMenuActivityController { +public class PhonePipMenuController implements PipMenuController { private static final String TAG = "PipMenuActController"; - private static final String MENU_WINDOW_TITLE = "PipMenuView"; private static final boolean DEBUG = false; public static final int MENU_STATE_NONE = 0; @@ -124,7 +116,7 @@ public class PipMenuActivityController { } }; - public PipMenuActivityController(Context context, + public PhonePipMenuController(Context context, PipMediaController mediaController, SystemWindows systemWindows) { mContext = context; mMediaController = mediaController; @@ -138,20 +130,22 @@ public class PipMenuActivityController { /** * Attach the menu when the PiP task first appears. */ - public void onTaskAppeared() { + @Override + public void attach(SurfaceControl leash) { attachPipMenuView(); } /** * Detach the menu when the PiP task is gone. */ - public void onTaskVanished() { + @Override + public void detach() { hideMenu(); detachPipMenuView(); } - public void onPinnedStackAnimationEnded() { + void onPinnedStackAnimationEnded() { if (isMenuVisible()) { mPipMenuView.onPipAnimationEnded(); } @@ -163,7 +157,9 @@ public class PipMenuActivityController { detachPipMenuView(); } mPipMenuView = new PipMenuView(mContext, this); - mSystemWindows.addView(mPipMenuView, getPipMenuLayoutParams(0, 0), 0, SHELL_ROOT_LAYER_PIP); + mSystemWindows.addView(mPipMenuView, + getPipMenuLayoutParams(MENU_WINDOW_TITLE, 0 /* width */, 0 /* height */), + 0, SHELL_ROOT_LAYER_PIP); } private void detachPipMenuView() { @@ -181,9 +177,11 @@ public class PipMenuActivityController { * Updates the layout parameters of the menu. * @param destinationBounds New Menu bounds. */ + @Override public void updateMenuBounds(Rect destinationBounds) { mSystemWindows.updateViewLayout(mPipMenuView, - getPipMenuLayoutParams(destinationBounds.width(), destinationBounds.height())); + getPipMenuLayoutParams(MENU_WINDOW_TITLE, destinationBounds.width(), + destinationBounds.height())); } /** @@ -206,6 +204,16 @@ public class PipMenuActivityController { } /** + * When other components requests the menu controller directly to show the menu, we must + * first fire off the request to the other listeners who will then propagate the call + * back to the controller with the right parameters. + */ + @Override + public void showMenu() { + mListeners.forEach(Listener::onPipShowMenu); + } + + /** * Similar to {@link #showMenu(int, Rect, boolean, boolean, boolean)} but only show the menu * upon PiP window transition is finished. */ @@ -250,6 +258,7 @@ public class PipMenuActivityController { /** * Move the PiP menu, which does a translation and possibly a scale transformation. */ + @Override public void movePipMenu(@Nullable SurfaceControl pipLeash, @Nullable SurfaceControl.Transaction t, Rect destinationBounds) { @@ -290,6 +299,7 @@ public class PipMenuActivityController { /** * Does an immediate window crop of the PiP menu. */ + @Override public void resizePipMenu(@Nullable SurfaceControl pipLeash, @Nullable SurfaceControl.Transaction t, Rect destinationBounds) { @@ -391,8 +401,9 @@ public class PipMenuActivityController { } /** - * Sets the menu actions to the actions provided by the current PiP activity. + * Sets the menu actions to the actions provided by the current PiP menu. */ + @Override public void setAppActions(ParceledListSlice<RemoteAction> appActions) { mAppActions = appActions; updateMenuActions(); @@ -406,10 +417,6 @@ public class PipMenuActivityController { mListeners.forEach(Listener::onPipDismiss); } - void onPipShowMenu() { - mListeners.forEach(Listener::onPipShowMenu); - } - /** * @return the best set of actions to show in the PiP menu. */ @@ -421,21 +428,6 @@ public class PipMenuActivityController { } /** - * Returns a default LayoutParams for the PIP Menu. - * @param width the PIP stack width. - * @param height the PIP stack height. - */ - public static WindowManager.LayoutParams getPipMenuLayoutParams(int width, int height) { - final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(width, height, - TYPE_APPLICATION_OVERLAY, - FLAG_WATCH_OUTSIDE_TOUCH | FLAG_SPLIT_TOUCH | FLAG_SLIPPERY | FLAG_NOT_TOUCHABLE, - PixelFormat.TRANSLUCENT); - lp.privateFlags |= PRIVATE_FLAG_TRUSTED_OVERLAY; - lp.setTitle(MENU_WINDOW_TITLE); - return lp; - } - - /** * Updates the PiP menu with the best set of actions provided. */ private void updateMenuActions() { @@ -521,7 +513,7 @@ public class PipMenuActivityController { } } - public void dump(PrintWriter pw, String prefix) { + void dump(PrintWriter pw, String prefix) { final String innerPrefix = prefix + " "; pw.println(prefix + TAG); pw.println(innerPrefix + "mMenuState=" + mMenuState); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java index 598b5d9b5d30..db02e284731e 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java @@ -92,7 +92,7 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac private boolean mIsInFixedRotation; private Consumer<Boolean> mPinnedStackAnimationRecentsCallback; - protected PipMenuActivityController mMenuController; + protected PhonePipMenuController mMenuController; protected PipTaskOrganizer mPipTaskOrganizer; protected PinnedStackListenerForwarder.PinnedStackListener mPinnedStackListener = new PipControllerPinnedStackListener(); @@ -229,7 +229,7 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac PipBoundsAlgorithm pipBoundsAlgorithm, @NonNull PipBoundsState pipBoundsState, PipMediaController pipMediaController, - PipMenuActivityController pipMenuActivityController, + PhonePipMenuController phonePipMenuController, PipTaskOrganizer pipTaskOrganizer, PipTouchHandler pipTouchHandler, WindowManagerShellWrapper windowManagerShellWrapper, @@ -250,7 +250,7 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac mPipTaskOrganizer = pipTaskOrganizer; mMainExecutor = mainExecutor; mMediaController = pipMediaController; - mMenuController = pipMenuActivityController; + mMenuController = phonePipMenuController; mTouchHandler = pipTouchHandler; mAppOpsListener = pipAppOpsListener; mPipInputConsumer = new PipInputConsumer(WindowManagerGlobal.getWindowManagerService(), @@ -632,7 +632,7 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac public static PipController create(Context context, DisplayController displayController, PipAppOpsListener pipAppOpsListener, PipBoundsAlgorithm pipBoundsAlgorithm, PipBoundsState pipBoundsState, PipMediaController pipMediaController, - PipMenuActivityController pipMenuActivityController, PipTaskOrganizer pipTaskOrganizer, + PhonePipMenuController phonePipMenuController, PipTaskOrganizer pipTaskOrganizer, PipTouchHandler pipTouchHandler, WindowManagerShellWrapper windowManagerShellWrapper, TaskStackListenerImpl taskStackListener, ShellExecutor mainExecutor) { if (!context.getPackageManager().hasSystemFeature(FEATURE_PICTURE_IN_PICTURE)) { @@ -641,7 +641,7 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac } return new PipController(context, displayController, pipAppOpsListener, pipBoundsAlgorithm, - pipBoundsState, pipMediaController, pipMenuActivityController, pipTaskOrganizer, + pipBoundsState, pipMediaController, phonePipMenuController, pipTaskOrganizer, pipTouchHandler, windowManagerShellWrapper, taskStackListener, mainExecutor); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java index 6e3cd8e9aa09..4e991f2e919b 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java @@ -23,9 +23,9 @@ import static android.view.accessibility.AccessibilityManager.FLAG_CONTENT_CONTR import static android.view.accessibility.AccessibilityManager.FLAG_CONTENT_ICONS; import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLICK; -import static com.android.wm.shell.pip.phone.PipMenuActivityController.MENU_STATE_CLOSE; -import static com.android.wm.shell.pip.phone.PipMenuActivityController.MENU_STATE_FULL; -import static com.android.wm.shell.pip.phone.PipMenuActivityController.MENU_STATE_NONE; +import static com.android.wm.shell.pip.phone.PhonePipMenuController.MENU_STATE_CLOSE; +import static com.android.wm.shell.pip.phone.PhonePipMenuController.MENU_STATE_FULL; +import static com.android.wm.shell.pip.phone.PhonePipMenuController.MENU_STATE_NONE; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; @@ -105,7 +105,7 @@ public class PipMenuView extends FrameLayout { private int mBetweenActionPaddingLand; private AnimatorSet mMenuContainerAnimator; - private PipMenuActivityController mController; + private PhonePipMenuController mController; private ValueAnimator.AnimatorUpdateListener mMenuBgUpdateListener = new ValueAnimator.AnimatorUpdateListener() { @@ -127,7 +127,7 @@ public class PipMenuView extends FrameLayout { protected View mTopEndContainer; protected PipMenuIconsAlgorithm mPipMenuIconsAlgorithm; - public PipMenuView(Context context, PipMenuActivityController controller) { + public PipMenuView(Context context, PhonePipMenuController controller) { super(context, null, 0); mContext = context; mController = controller; @@ -182,7 +182,7 @@ public class PipMenuView extends FrameLayout { @Override public boolean performAccessibilityAction(View host, int action, Bundle args) { if (action == ACTION_CLICK && mMenuState == MENU_STATE_CLOSE) { - mController.onPipShowMenu(); + mController.showMenu(); } return super.performAccessibilityAction(host, action, args); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java index 903f7d773896..e32d3b955081 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java @@ -70,7 +70,7 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, private final PipTaskOrganizer mPipTaskOrganizer; private @NonNull PipBoundsState mPipBoundsState; - private PipMenuActivityController mMenuController; + private PhonePipMenuController mMenuController; private PipSnapAlgorithm mSnapAlgorithm; private final Handler mMainHandler = new Handler(Looper.getMainLooper()); @@ -162,7 +162,7 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, }; public PipMotionHelper(Context context, @NonNull PipBoundsState pipBoundsState, - PipTaskOrganizer pipTaskOrganizer, PipMenuActivityController menuController, + PipTaskOrganizer pipTaskOrganizer, PhonePipMenuController menuController, PipSnapAlgorithm snapAlgorithm, FloatingContentCoordinator floatingContentCoordinator) { mContext = context; mPipTaskOrganizer = pipTaskOrganizer; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java index e5a49c430d82..bb545bdda922 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java @@ -103,7 +103,7 @@ public class PipResizeGestureHandler { private InputMonitor mInputMonitor; private InputEventReceiver mInputEventReceiver; private PipTaskOrganizer mPipTaskOrganizer; - private PipMenuActivityController mPipMenuActivityController; + private PhonePipMenuController mPhonePipMenuController; private PipUiEventLogger mPipUiEventLogger; private int mCtrlType; @@ -112,7 +112,7 @@ public class PipResizeGestureHandler { PipBoundsState pipBoundsState, PipMotionHelper motionHelper, PipTaskOrganizer pipTaskOrganizer, Function<Rect, Rect> movementBoundsSupplier, Runnable updateMovementBoundsRunnable, PipUiEventLogger pipUiEventLogger, - PipMenuActivityController menuActivityController) { + PhonePipMenuController menuActivityController) { mContext = context; mDisplayId = context.getDisplayId(); mMainExecutor = context.getMainExecutor(); @@ -122,7 +122,7 @@ public class PipResizeGestureHandler { mPipTaskOrganizer = pipTaskOrganizer; mMovementBoundsSupplier = movementBoundsSupplier; mUpdateMovementBoundsRunnable = updateMovementBoundsRunnable; - mPipMenuActivityController = menuActivityController; + mPhonePipMenuController = menuActivityController; mPipUiEventLogger = pipUiEventLogger; context.getDisplay().getRealSize(mMaxSize); @@ -422,8 +422,8 @@ public class PipResizeGestureHandler { mLastDownBounds.set(mPipBoundsState.getBounds()); } if (!currentPipBounds.contains((int) ev.getX(), (int) ev.getY()) - && mPipMenuActivityController.isMenuVisible()) { - mPipMenuActivityController.hideMenu(); + && mPhonePipMenuController.isMenuVisible()) { + mPhonePipMenuController.hideMenu(); } } else if (mAllowGesture) { @@ -442,9 +442,9 @@ public class PipResizeGestureHandler { mInputMonitor.pilferPointers(); } if (mThresholdCrossed) { - if (mPipMenuActivityController.isMenuVisible()) { - mPipMenuActivityController.hideMenuWithoutResize(); - mPipMenuActivityController.hideMenu(); + if (mPhonePipMenuController.isMenuVisible()) { + mPhonePipMenuController.hideMenuWithoutResize(); + mPhonePipMenuController.hideMenu(); } final Rect currentPipBounds = mPipBoundsState.getBounds(); mLastResizeBounds.set(TaskResizingAlgorithm.resizeDrag(x, y, diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java index a78c4ecdb39f..99177016c30c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java @@ -18,9 +18,9 @@ package com.android.wm.shell.pip.phone; import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.PIP_STASHING; import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_TO_PIP; -import static com.android.wm.shell.pip.phone.PipMenuActivityController.MENU_STATE_CLOSE; -import static com.android.wm.shell.pip.phone.PipMenuActivityController.MENU_STATE_FULL; -import static com.android.wm.shell.pip.phone.PipMenuActivityController.MENU_STATE_NONE; +import static com.android.wm.shell.pip.phone.PhonePipMenuController.MENU_STATE_CLOSE; +import static com.android.wm.shell.pip.phone.PhonePipMenuController.MENU_STATE_FULL; +import static com.android.wm.shell.pip.phone.PhonePipMenuController.MENU_STATE_NONE; import android.annotation.NonNull; import android.annotation.SuppressLint; @@ -78,7 +78,7 @@ public class PipTouchHandler { private IPinnedStackController mPinnedStackController; private WeakReference<Consumer<Rect>> mPipExclusionBoundsChangeListener; - private final PipMenuActivityController mMenuController; + private final PhonePipMenuController mMenuController; private final AccessibilityManager mAccessibilityManager; private boolean mShowPipMenuOnAnimationEnd = false; @@ -125,7 +125,7 @@ public class PipTouchHandler { /** * A listener for the PIP menu activity. */ - private class PipMenuListener implements PipMenuActivityController.Listener { + private class PipMenuListener implements PhonePipMenuController.Listener { @Override public void onPipMenuStateChanged(int menuState, boolean resize, Runnable callback) { setMenuState(menuState, resize, callback); @@ -152,7 +152,7 @@ public class PipTouchHandler { @SuppressLint("InflateParams") public PipTouchHandler(Context context, - PipMenuActivityController menuController, + PhonePipMenuController menuController, PipBoundsAlgorithm pipBoundsAlgorithm, @NonNull PipBoundsState pipBoundsState, PipTaskOrganizer pipTaskOrganizer, diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipController.java index 56e97b91c9d2..5d8d5e621846 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipController.java @@ -36,7 +36,6 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.pm.ParceledListSlice; import android.content.res.Configuration; -import android.content.res.Resources; import android.graphics.Rect; import android.os.Debug; import android.os.Handler; @@ -59,13 +58,14 @@ import com.android.wm.shell.pip.PipTaskOrganizer; import java.util.ArrayList; import java.util.List; +import java.util.Objects; /** * Manages the picture-in-picture (PIP) UI and states. */ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallback { - private static final String TAG = "PipController"; - static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); + private static final String TAG = "TvPipController"; + static final boolean DEBUG = false; /** * Unknown or invalid state @@ -111,15 +111,13 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac private final PipBoundsAlgorithm mPipBoundsAlgorithm; private final PipTaskOrganizer mPipTaskOrganizer; private final PipMediaController mPipMediaController; + private final TvPipMenuController mTvPipMenuController; private IActivityTaskManager mActivityTaskManager; private int mState = STATE_NO_PIP; private int mResumeResizePinnedStackRunnableState = STATE_NO_PIP; private final Handler mHandler = new Handler(); private List<Listener> mListeners = new ArrayList<>(); - private Rect mPipBounds; - private Rect mDefaultPipBounds = new Rect(); - private Rect mMenuModePipBounds; private int mLastOrientation = Configuration.ORIENTATION_UNDEFINED; private int mPipTaskId = TASK_ID_NO_PIP; private int mPinnedStackId = INVALID_STACK_ID; @@ -187,11 +185,11 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac if (mImeVisible != imeVisible) { if (imeVisible) { // Save the IME height adjustment, and offset to not occlude the IME - mPipBounds.offset(0, -imeHeight); + mPipBoundsState.getNormalBounds().offset(0, -imeHeight); mImeHeightAdjustment = imeHeight; } else { // Apply the inverse adjustment when the IME is hidden - mPipBounds.offset(0, mImeHeightAdjustment); + mPipBoundsState.getNormalBounds().offset(0, mImeHeightAdjustment); } mImeVisible = imeVisible; resizePinnedStack(STATE_PIP); @@ -206,10 +204,6 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac mTmpDisplayInfo.copyFrom(mPipBoundsState.getDisplayInfo()); mPipBoundsAlgorithm.getInsetBounds(mTmpInsetBounds); - mPipBounds.set(mPipBoundsAlgorithm.getNormalBounds()); - if (mDefaultPipBounds.isEmpty()) { - mDefaultPipBounds.set(mPipBoundsAlgorithm.getDefaultBounds()); - } }); } @@ -217,9 +211,7 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac public void onActionsChanged(ParceledListSlice<RemoteAction> actions) { mCustomActions = actions; mHandler.post(() -> { - for (int i = mListeners.size() - 1; i >= 0; --i) { - mListeners.get(i).onPipMenuActionsChanged(mCustomActions); - } + mTvPipMenuController.setAppActions(mCustomActions); }); } } @@ -228,6 +220,7 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac PipBoundsState pipBoundsState, PipBoundsAlgorithm pipBoundsAlgorithm, PipTaskOrganizer pipTaskOrganizer, + TvPipMenuController tvPipMenuController, PipMediaController pipMediaController, PipNotification pipNotification, TaskStackListenerImpl taskStackListener, @@ -237,6 +230,8 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac mPipNotification = pipNotification; mPipBoundsAlgorithm = pipBoundsAlgorithm; mPipMediaController = pipMediaController; + mTvPipMenuController = tvPipMenuController; + mTvPipMenuController.attachPipController(this); // Ensure that we have the display info in case we get calls to update the bounds // before the listener calls back final DisplayInfo displayInfo = new DisplayInfo(); @@ -289,9 +284,6 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac PipController.this.onActivityRestartAttempt(task, clearedTask); } }); - - // TODO(b/169395392) Refactor PipMenuActivity to PipMenuView - PipMenuActivity.setPipController(this); } private void loadConfigurationsAndApply(Configuration newConfig) { @@ -302,14 +294,10 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac return; } - Resources res = mContext.getResources(); - mMenuModePipBounds = Rect.unflattenFromString(res.getString( - R.string.pip_menu_bounds)); + final Rect menuBounds = Rect.unflattenFromString( + mContext.getResources().getString(R.string.pip_menu_bounds)); + mPipBoundsState.setExpandedBounds(menuBounds); - // Reset the PIP bounds and apply. PIP bounds can be changed by two reasons. - // 1. Configuration changed due to the language change (RTL <-> RTL) - // 2. SystemUI restarts after the crash - mPipBounds = mDefaultPipBounds; resizePinnedStack(getPinnedTaskInfo() == null ? STATE_NO_PIP : STATE_PIP); } @@ -379,16 +367,22 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac } private void onActivityPinned(String packageName) { - if (DEBUG) Log.d(TAG, "onActivityPinned()"); - - RootTaskInfo taskInfo = getPinnedTaskInfo(); + final RootTaskInfo taskInfo = getPinnedTaskInfo(); + if (DEBUG) Log.d(TAG, "onActivityPinned, task=" + taskInfo); if (taskInfo == null) { Log.w(TAG, "Cannot find pinned stack"); return; } - if (DEBUG) Log.d(TAG, "PINNED_STACK:" + taskInfo); + + // At this point PipBoundsState knows the correct aspect ratio for this pinned task, so we + // use PipBoundsAlgorithm to calculate the normal bounds for the task (PipBoundsAlgorithm + // will query PipBoundsState for the aspect ratio) and pass the bounds over to the + // PipBoundsState. + mPipBoundsState.setNormalBounds(mPipBoundsAlgorithm.getNormalBounds()); + mPinnedStackId = taskInfo.taskId; mPipTaskId = taskInfo.childTaskIds[taskInfo.childTaskIds.length - 1]; + // Set state to STATE_PIP so we show it when the pinned stack animation ends. mState = STATE_PIP; mPipMediaController.onActivityPinned(); @@ -434,8 +428,7 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac } } if (getState() == STATE_PIP) { - if (mPipBounds != mDefaultPipBounds) { - mPipBounds = mDefaultPipBounds; + if (!Objects.equals(mPipBoundsState.getBounds(), mPipBoundsState.getNormalBounds())) { resizePinnedStack(STATE_PIP); } } @@ -451,7 +444,6 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac Log.d(TAG, "suspendPipResizing() reason=" + reason + " callers=" + Debug.getCallers(2)); } - mSuspendPipResizingReason |= reason; } @@ -478,6 +470,7 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac * @param state In Pip state also used to determine the new size for the Pip. */ public void resizePinnedStack(int state) { + if (DEBUG) { Log.d(TAG, "resizePinnedStack() state=" + stateToName(state) + ", current state=" + getStateDescription(), new Exception()); @@ -509,11 +502,11 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac } break; case STATE_PIP_MENU: - newBounds = mMenuModePipBounds; + newBounds = mPipBoundsState.getExpandedBounds(); break; case STATE_PIP: // fallthrough default: - newBounds = mPipBounds; + newBounds = mPipBoundsState.getNormalBounds(); break; } if (newBounds != null) { @@ -544,10 +537,8 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac for (int i = mListeners.size() - 1; i >= 0; --i) { mListeners.get(i).onShowPipMenu(); } - Intent intent = new Intent(mContext, PipMenuActivity.class); - intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - intent.putExtra(PipMenuActivity.EXTRA_CUSTOM_ACTIONS, mCustomActions); - mContext.startActivity(intent); + + mTvPipMenuController.showMenu(); } /** @@ -650,8 +641,6 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac void onPipActivityClosed(); /** Invoked when the PIP menu gets shown. */ void onShowPipMenu(); - /** Invoked when the PIP menu actions change. */ - void onPipMenuActionsChanged(ParceledListSlice<RemoteAction> actions); /** Invoked when the PIPed activity is about to return back to the fullscreen. */ void onMoveToFullscreen(); /** Invoked when we are above to start resizing the Pip. */ diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipMenuActivity.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipMenuView.java index d2270c278161..689c3ede9efa 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipMenuActivity.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipMenuView.java @@ -18,109 +18,110 @@ package com.android.wm.shell.pip.tv; import android.animation.Animator; import android.animation.AnimatorInflater; -import android.app.Activity; +import android.annotation.Nullable; import android.app.RemoteAction; -import android.content.Intent; +import android.content.Context; import android.content.pm.ParceledListSlice; -import android.os.Bundle; import android.util.Log; +import android.view.KeyEvent; +import android.view.SurfaceControl; +import android.view.ViewRootImpl; +import android.view.WindowManagerGlobal; +import android.widget.FrameLayout; import com.android.wm.shell.R; import java.util.Collections; /** - * Activity to show the PIP menu to control PIP. - * TODO(b/169395392) Refactor PipMenuActivity to PipMenuView + * The Menu View that shows controls of the PiP. Always fullscreen. */ -public class PipMenuActivity extends Activity implements PipController.Listener { - private static final String TAG = "PipMenuActivity"; +public class PipMenuView extends FrameLayout implements PipController.Listener { + private static final String TAG = "PipMenuView"; private static final boolean DEBUG = PipController.DEBUG; - static final String EXTRA_CUSTOM_ACTIONS = "custom_actions"; - - private static PipController sPipController; - - private Animator mFadeInAnimation; - private Animator mFadeOutAnimation; + private final PipController mPipController; + private final Animator mFadeInAnimation; + private final Animator mFadeOutAnimation; + private final PipControlsViewController mPipControlsViewController; private boolean mRestorePipSizeWhenClose; - private PipControlsViewController mPipControlsViewController; - @Override - protected void onCreate(Bundle bundle) { - if (DEBUG) Log.d(TAG, "onCreate()"); + public PipMenuView(Context context, PipController pipController) { + super(context, null, 0); + mPipController = pipController; + + inflate(context, R.layout.tv_pip_menu, this); - super.onCreate(bundle); - if (sPipController == null) { - finish(); - } - setContentView(R.layout.tv_pip_menu); mPipControlsViewController = new PipControlsViewController( - findViewById(R.id.pip_controls), sPipController); - sPipController.addListener(this); + findViewById(R.id.pip_controls), mPipController); mRestorePipSizeWhenClose = true; mFadeInAnimation = AnimatorInflater.loadAnimator( - this, R.anim.tv_pip_menu_fade_in_animation); + mContext, R.anim.tv_pip_menu_fade_in_animation); mFadeInAnimation.setTarget(mPipControlsViewController.getView()); mFadeOutAnimation = AnimatorInflater.loadAnimator( - this, R.anim.tv_pip_menu_fade_out_animation); + mContext, R.anim.tv_pip_menu_fade_out_animation); mFadeOutAnimation.setTarget(mPipControlsViewController.getView()); - - onPipMenuActionsChanged(getIntent().getParcelableExtra(EXTRA_CUSTOM_ACTIONS)); } @Override - protected void onNewIntent(Intent intent) { - if (DEBUG) Log.d(TAG, "onNewIntent(), intent=" + intent); - super.onNewIntent(intent); - - onPipMenuActionsChanged(getIntent().getParcelableExtra(EXTRA_CUSTOM_ACTIONS)); + public boolean dispatchKeyEvent(KeyEvent event) { + if (event.getKeyCode() == KeyEvent.KEYCODE_BACK + && event.getAction() == KeyEvent.ACTION_UP) { + restorePipAndFinish(); + return true; + } + return super.dispatchKeyEvent(event); } - private void restorePipAndFinish() { - if (DEBUG) Log.d(TAG, "restorePipAndFinish()"); - - if (mRestorePipSizeWhenClose) { - if (DEBUG) Log.d(TAG, " > restoring to the default position"); - - // When PIP menu activity is closed, restore to the default position. - sPipController.resizePinnedStack(PipController.STATE_PIP); + @Nullable + SurfaceControl getWindowSurfaceControl() { + final ViewRootImpl root = getViewRootImpl(); + if (root == null) { + return null; + } + final SurfaceControl out = root.getSurfaceControl(); + if (out != null && out.isValid()) { + return out; } - finish(); + return null; } - @Override - public void onResume() { - if (DEBUG) Log.d(TAG, "onResume()"); - - super.onResume(); + void showMenu() { + mPipController.addListener(this); mFadeInAnimation.start(); + setAlpha(1.0f); + try { + WindowManagerGlobal.getWindowSession().grantEmbeddedWindowFocus(null /* window */, + getViewRootImpl().getInputToken(), true /* grantFocus */); + } catch (Exception e) { + Log.e(TAG, "Unable to update focus as menu appears", e); + } } - @Override - public void onPause() { - if (DEBUG) Log.d(TAG, "onPause()"); - - super.onPause(); + void hideMenu() { + mPipController.removeListener(this); + mPipController.resumePipResizing( + PipController.SUSPEND_PIP_RESIZE_REASON_WAITING_FOR_MENU_ACTIVITY_FINISH); mFadeOutAnimation.start(); - restorePipAndFinish(); + setAlpha(0.0f); + try { + WindowManagerGlobal.getWindowSession().grantEmbeddedWindowFocus(null /* window */, + getViewRootImpl().getInputToken(), false /* grantFocus */); + } catch (Exception e) { + Log.e(TAG, "Unable to update focus as menu disappears", e); + } } - @Override - protected void onDestroy() { - if (DEBUG) Log.d(TAG, "onDestroy()"); - - super.onDestroy(); - sPipController.removeListener(this); - sPipController.resumePipResizing( - PipController.SUSPEND_PIP_RESIZE_REASON_WAITING_FOR_MENU_ACTIVITY_FINISH); - } + private void restorePipAndFinish() { + if (DEBUG) Log.d(TAG, "restorePipAndFinish()"); - @Override - public void onBackPressed() { - if (DEBUG) Log.d(TAG, "onBackPressed()"); + if (mRestorePipSizeWhenClose) { + if (DEBUG) Log.d(TAG, " > restoring to the default position"); - restorePipAndFinish(); + // When PIP menu activity is closed, restore to the default position. + mPipController.resizePinnedStack(PipController.STATE_PIP); + } + hideMenu(); } @Override @@ -132,11 +133,10 @@ public class PipMenuActivity extends Activity implements PipController.Listener public void onPipActivityClosed() { if (DEBUG) Log.d(TAG, "onPipActivityClosed()"); - finish(); + hideMenu(); } - @Override - public void onPipMenuActionsChanged(ParceledListSlice<RemoteAction> actions) { + void setAppActions(ParceledListSlice<RemoteAction> actions) { if (DEBUG) Log.d(TAG, "onPipMenuActionsChanged()"); boolean hasCustomActions = actions != null && !actions.getList().isEmpty(); @@ -156,34 +156,15 @@ public class PipMenuActivity extends Activity implements PipController.Listener // Moving PIP to fullscreen is implemented by resizing PINNED_STACK with null bounds. // This conflicts with restoring PIP position, so disable it. mRestorePipSizeWhenClose = false; - finish(); + hideMenu(); } @Override public void onPipResizeAboutToStart() { if (DEBUG) Log.d(TAG, "onPipResizeAboutToStart()"); - finish(); - sPipController.suspendPipResizing( + hideMenu(); + mPipController.suspendPipResizing( PipController.SUSPEND_PIP_RESIZE_REASON_WAITING_FOR_MENU_ACTIVITY_FINISH); } - - @Override - public void finish() { - if (DEBUG) Log.d(TAG, "finish()", new RuntimeException()); - - super.finish(); - } - - /** - * TODO(b/169395392) Refactor PipMenuActivity to PipMenuView - * - * @param pipController The singleton pipController instance for TV - */ - public static void setPipController(PipController pipController) { - if (sPipController != null) { - return; - } - sPipController = pipController; - } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipNotification.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipNotification.java index b30dee4f331f..d56a88874420 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipNotification.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipNotification.java @@ -19,12 +19,10 @@ package com.android.wm.shell.pip.tv; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; -import android.app.RemoteAction; import android.content.Context; import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; -import android.content.pm.ParceledListSlice; import android.content.res.Resources; import android.graphics.Bitmap; import android.media.MediaMetadata; @@ -99,11 +97,6 @@ public class PipNotification implements PipController.Listener { } @Override - public void onPipMenuActionsChanged(ParceledListSlice<RemoteAction> actions) { - // no-op. - } - - @Override public void onMoveToFullscreen() { dismissPipNotification(); mPackageName = null; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java new file mode 100644 index 000000000000..91aef670b946 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java @@ -0,0 +1,98 @@ +/* + * 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.wm.shell.pip.tv; + +import static android.view.WindowManager.SHELL_ROOT_LAYER_PIP; + +import android.app.RemoteAction; +import android.content.Context; +import android.content.pm.ParceledListSlice; +import android.view.SurfaceControl; + +import com.android.wm.shell.common.SystemWindows; +import com.android.wm.shell.pip.PipBoundsState; +import com.android.wm.shell.pip.PipMenuController; + +/** + * Manages the visibility of the PiP Menu as user interacts with PiP. + */ +public class TvPipMenuController implements PipMenuController { + + private final Context mContext; + private final SystemWindows mSystemWindows; + private final PipBoundsState mPipBoundsState; + private PipMenuView mMenuView; + private PipController mPipController; + private SurfaceControl mLeash; + + public TvPipMenuController(Context context, PipBoundsState pipBoundsState, + SystemWindows systemWindows) { + mContext = context; + mPipBoundsState = pipBoundsState; + mSystemWindows = systemWindows; + } + + void attachPipController(PipController pipController) { + mPipController = pipController; + } + + @Override + public void showMenu() { + if (mMenuView != null) { + mSystemWindows.updateViewLayout(mMenuView, getPipMenuLayoutParams(MENU_WINDOW_TITLE, + mPipBoundsState.getDisplayBounds().width(), + mPipBoundsState.getDisplayBounds().height())); + mMenuView.showMenu(); + + // By default, SystemWindows views are above everything else. + // Set the relative z-order so the menu is below PiP. + if (mMenuView.getWindowSurfaceControl() != null && mLeash != null) { + SurfaceControl.Transaction t = new SurfaceControl.Transaction(); + t.setRelativeLayer(mMenuView.getWindowSurfaceControl(), mLeash, -1); + t.apply(); + } + } + } + + @Override + public void attach(SurfaceControl leash) { + if (mMenuView == null) { + mMenuView = new PipMenuView(mContext, mPipController); + mSystemWindows.addView(mMenuView, + getPipMenuLayoutParams(MENU_WINDOW_TITLE, 0 /* width */, 0 /* height */), + 0, SHELL_ROOT_LAYER_PIP); + mLeash = leash; + } + } + + @Override + public void detach() { + mSystemWindows.removeView(mMenuView); + mMenuView = null; + mLeash = null; + } + + @Override + public void setAppActions(ParceledListSlice<RemoteAction> appActions) { + mMenuView.setAppActions(appActions); + } + + @Override + public boolean isMenuVisible() { + return mMenuView != null && mMenuView.getAlpha() == 1.0f; + } +} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/OpenAppToSplitScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/OpenAppToSplitScreenTest.kt index 1e328a8dae40..c85561d96091 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/OpenAppToSplitScreenTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/OpenAppToSplitScreenTest.kt @@ -17,7 +17,7 @@ package com.android.wm.shell.flicker.splitscreen import android.platform.test.annotations.Presubmit -import androidx.test.filters.FlakyTest +import android.view.Surface import androidx.test.filters.RequiresDevice import androidx.test.platform.app.InstrumentationRegistry import com.android.server.wm.flicker.DOCKED_STACK_DIVIDER @@ -54,7 +54,6 @@ import org.junit.runners.Parameterized @RequiresDevice @RunWith(Parameterized::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -@FlakyTest(bugId = 161435597) class OpenAppToSplitScreenTest( testName: String, flickerSpec: Flicker @@ -67,7 +66,8 @@ class OpenAppToSplitScreenTest( val testApp = StandardAppHelper(instrumentation, "com.android.wm.shell.flicker.testapp", "SimpleApp") - return FlickerTestRunnerFactory(instrumentation) + // b/161435597 causes the test not to work on 90 degrees + return FlickerTestRunnerFactory(instrumentation, listOf(Surface.ROTATION_0)) .buildTest { configuration -> withTestName { buildTestTag("appToSplitScreen", testApp, configuration) diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenRotateOneLaunchedAppTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenRotateOneLaunchedAppTest.kt new file mode 100644 index 000000000000..d2371bd766f5 --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenRotateOneLaunchedAppTest.kt @@ -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.wm.shell.flicker.splitscreen + +import androidx.test.filters.FlakyTest +import android.view.Surface +import androidx.test.filters.RequiresDevice +import androidx.test.platform.app.InstrumentationRegistry +import com.android.server.wm.flicker.Flicker +import com.android.server.wm.flicker.FlickerTestRunner +import com.android.server.wm.flicker.FlickerTestRunnerFactory +import com.android.server.wm.flicker.helpers.StandardAppHelper +import com.android.server.wm.flicker.startRotation +import com.android.server.wm.flicker.endRotation +import com.android.server.wm.flicker.helpers.buildTestTag +import com.android.server.wm.flicker.helpers.exitSplitScreen +import com.android.server.wm.flicker.helpers.isInSplitScreen +import com.android.server.wm.flicker.helpers.launchSplitScreen +import com.android.server.wm.flicker.helpers.setRotation +import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen +import com.android.server.wm.flicker.repetitions +import org.junit.FixMethodOrder +import org.junit.runner.RunWith +import org.junit.runners.MethodSorters +import org.junit.runners.Parameterized + +/** + * Test open app to split screen. + * To run this test: `atest WMShellFlickerTests:SplitScreenRotateOneLaunchedAppTest` + */ +@RequiresDevice +@RunWith(Parameterized::class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +@FlakyTest +class SplitScreenRotateOneLaunchedAppTest( + testName: String, + flickerSpec: Flicker +) : FlickerTestRunner(testName, flickerSpec) { + companion object { + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams(): Collection<Array<Any>> { + val instrumentation = InstrumentationRegistry.getInstrumentation() + val testApp = StandardAppHelper(instrumentation, + "com.android.wm.shell.flicker.testapp", "SimpleApp") + + return FlickerTestRunnerFactory(instrumentation, repetitions = 3) + .buildTest { configuration -> + withTestName { + buildTestTag("splitScreenRotateOneApp", testApp, configuration) + } + repeat { configuration.repetitions } + setup { + test { + device.wakeUpAndGoToHomeScreen() + testApp.open() + device.launchSplitScreen() + device.waitForIdle() + } + eachRun { + this.setRotation(configuration.startRotation) + } + } + teardown { + eachRun { + setRotation(Surface.ROTATION_0) + } + test { + testApp.exit() + if (device.isInSplitScreen()) { + device.exitSplitScreen() + } + } + } + transitions { + this.setRotation(configuration.endRotation) + } + } + } + } +}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenRotateTwoLaunchedAppTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenRotateTwoLaunchedAppTest.kt new file mode 100644 index 000000000000..67346424acd2 --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenRotateTwoLaunchedAppTest.kt @@ -0,0 +1,104 @@ +/* + * 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.wm.shell.flicker.splitscreen + +import androidx.test.filters.FlakyTest +import android.view.Surface +import androidx.test.filters.RequiresDevice +import androidx.test.platform.app.InstrumentationRegistry +import com.android.server.wm.flicker.Flicker +import com.android.server.wm.flicker.FlickerTestRunner +import com.android.server.wm.flicker.FlickerTestRunnerFactory +import com.android.server.wm.flicker.helpers.StandardAppHelper +import com.android.server.wm.flicker.startRotation +import com.android.server.wm.flicker.endRotation +import com.android.server.wm.flicker.helpers.buildTestTag +import com.android.server.wm.flicker.helpers.exitSplitScreen +import com.android.server.wm.flicker.helpers.reopenAppFromOverview +import com.android.server.wm.flicker.helpers.isInSplitScreen +import com.android.server.wm.flicker.helpers.launchSplitScreen +import com.android.server.wm.flicker.helpers.setRotation +import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen +import com.android.server.wm.flicker.repetitions +import org.junit.FixMethodOrder +import org.junit.runner.RunWith +import org.junit.runners.MethodSorters +import org.junit.runners.Parameterized + +/** + * Test open app to split screen. + * To run this test: `atest WMShellFlickerTests:SplitScreenRotateTwoLaunchedAppTest` + */ +@RequiresDevice +@RunWith(Parameterized::class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +@FlakyTest +class SplitScreenRotateTwoLaunchedAppTest( + testName: String, + flickerSpec: Flicker +) : FlickerTestRunner(testName, flickerSpec) { + companion object { + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams(): Collection<Array<Any>> { + val instrumentation = InstrumentationRegistry.getInstrumentation() + val testApp = StandardAppHelper(instrumentation, + "com.android.wm.shell.flicker.testapp", "SimpleApp") + val secondaryApp = StandardAppHelper(instrumentation, + "com.android.wm.shell.flicker.testapp", + "SplitScreenSecondaryApp") + + return FlickerTestRunnerFactory(instrumentation, repetitions = 3) + .buildTest { configuration -> + withTestName { + buildTestTag("splitScreenRotateTwoApps", testApp, configuration) + } + repeat { configuration.repetitions } + setup { + test { + device.wakeUpAndGoToHomeScreen() + testApp.open() + device.pressHome() + secondaryApp.open() + device.pressHome() + device.launchSplitScreen() + device.reopenAppFromOverview() + device.waitForIdle() + } + eachRun { + this.setRotation(configuration.startRotation) + } + } + teardown { + eachRun { + setRotation(Surface.ROTATION_0) + } + test { + testApp.exit() + secondaryApp.exit() + if (device.isInSplitScreen()) { + device.exitSplitScreen() + } + } + } + transitions { + this.setRotation(configuration.endRotation) + } + } + } + } +}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java index 8d3774cee1e0..45e4241d5bc6 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java @@ -44,7 +44,7 @@ import android.window.WindowContainerToken; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.ShellTestCase; import com.android.wm.shell.common.DisplayController; -import com.android.wm.shell.pip.phone.PipMenuActivityController; +import com.android.wm.shell.pip.phone.PhonePipMenuController; import com.android.wm.shell.splitscreen.SplitScreen; import org.junit.Before; @@ -66,7 +66,7 @@ public class PipTaskOrganizerTest extends ShellTestCase { @Mock private DisplayController mMockdDisplayController; @Mock private PipBoundsAlgorithm mMockPipBoundsAlgorithm; - @Mock private PipMenuActivityController mMenuActivityController; + @Mock private PhonePipMenuController mMockPhonePipMenuController; @Mock private PipSurfaceTransactionHelper mMockPipSurfaceTransactionHelper; @Mock private PipUiEventLogger mMockPipUiEventLogger; @Mock private Optional<SplitScreen> mMockOptionalSplitScreen; @@ -83,9 +83,9 @@ public class PipTaskOrganizerTest extends ShellTestCase { mComponent2 = new ComponentName(mContext, "component2"); mPipBoundsState = new PipBoundsState(mContext); mSpiedPipTaskOrganizer = spy(new PipTaskOrganizer(mContext, mPipBoundsState, - mMockPipBoundsAlgorithm, mMenuActivityController, mMockPipSurfaceTransactionHelper, - mMockOptionalSplitScreen, mMockdDisplayController, mMockPipUiEventLogger, - mMockShellTaskOrganizer)); + mMockPipBoundsAlgorithm, mMockPhonePipMenuController, + mMockPipSurfaceTransactionHelper, mMockOptionalSplitScreen, mMockdDisplayController, + mMockPipUiEventLogger, mMockShellTaskOrganizer)); preparePipTaskOrg(); } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java index 88c8eb902a6f..4687d2d9667c 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java @@ -61,7 +61,7 @@ public class PipControllerTest extends ShellTestCase { private PipController mPipController; @Mock private DisplayController mMockDisplayController; - @Mock private PipMenuActivityController mMockPipMenuActivityController; + @Mock private PhonePipMenuController mMockPhonePipMenuController; @Mock private PipAppOpsListener mMockPipAppOpsListener; @Mock private PipBoundsAlgorithm mMockPipBoundsAlgorithm; @Mock private PipMediaController mMockPipMediaController; @@ -77,7 +77,7 @@ public class PipControllerTest extends ShellTestCase { MockitoAnnotations.initMocks(this); mPipController = new PipController(mContext, mMockDisplayController, mMockPipAppOpsListener, mMockPipBoundsAlgorithm, mMockPipBoundsState, - mMockPipMediaController, mMockPipMenuActivityController, mMockPipTaskOrganizer, + mMockPipMediaController, mMockPhonePipMenuController, mMockPipTaskOrganizer, mMockPipTouchHandler, mMockWindowManagerShellWrapper, mMockTaskStackListener, mMockExecutor); doAnswer(invocation -> { @@ -110,7 +110,7 @@ public class PipControllerTest extends ShellTestCase { assertNull(PipController.create(spyContext, mMockDisplayController, mMockPipAppOpsListener, mMockPipBoundsAlgorithm, mMockPipBoundsState, - mMockPipMediaController, mMockPipMenuActivityController, mMockPipTaskOrganizer, + mMockPipMediaController, mMockPhonePipMenuController, mMockPipTaskOrganizer, mMockPipTouchHandler, mMockWindowManagerShellWrapper, mMockTaskStackListener, mMockExecutor)); } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java index abbc681f53fe..e60221943898 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java @@ -60,7 +60,7 @@ public class PipTouchHandlerTest extends ShellTestCase { private PipTouchHandler mPipTouchHandler; @Mock - private PipMenuActivityController mPipMenuActivityController; + private PhonePipMenuController mPhonePipMenuController; @Mock private PipTaskOrganizer mPipTaskOrganizer; @@ -92,7 +92,7 @@ public class PipTouchHandlerTest extends ShellTestCase { mPipBoundsAlgorithm = new PipBoundsAlgorithm(mContext, mPipBoundsState); mPipSnapAlgorithm = mPipBoundsAlgorithm.getSnapAlgorithm(); mPipSnapAlgorithm = new PipSnapAlgorithm(); - mPipTouchHandler = new PipTouchHandler(mContext, mPipMenuActivityController, + mPipTouchHandler = new PipTouchHandler(mContext, mPhonePipMenuController, mPipBoundsAlgorithm, mPipBoundsState, mPipTaskOrganizer, mFloatingContentCoordinator, mPipUiEventLogger); mMotionHelper = Mockito.spy(mPipTouchHandler.getMotionHelper()); diff --git a/libs/hwui/FrameInfo.cpp b/libs/hwui/FrameInfo.cpp index fd18d2f9192d..8b20492543f7 100644 --- a/libs/hwui/FrameInfo.cpp +++ b/libs/hwui/FrameInfo.cpp @@ -20,7 +20,7 @@ namespace android { namespace uirenderer { -const std::string FrameInfoNames[] = { +const std::array<std::string, static_cast<int>(FrameInfoIndex::NumIndexes)> FrameInfoNames = { "Flags", "FrameTimelineVsyncId", "IntendedVsync", @@ -42,10 +42,6 @@ const std::string FrameInfoNames[] = { "GpuCompleted", }; -static_assert((sizeof(FrameInfoNames) / sizeof(FrameInfoNames[0])) == - static_cast<int>(FrameInfoIndex::NumIndexes), - "size mismatch: FrameInfoNames doesn't match the enum!"); - static_assert(static_cast<int>(FrameInfoIndex::NumIndexes) == 19, "Must update value in FrameMetrics.java#FRAME_STATS_COUNT (and here)"); diff --git a/libs/hwui/FrameInfo.h b/libs/hwui/FrameInfo.h index bb875e35f6f7..738246d56d0d 100644 --- a/libs/hwui/FrameInfo.h +++ b/libs/hwui/FrameInfo.h @@ -21,6 +21,7 @@ #include <cutils/compiler.h> #include <utils/Timers.h> +#include <array> #include <memory.h> #include <string> @@ -60,7 +61,7 @@ enum class FrameInfoIndex { NumIndexes }; -extern const std::string FrameInfoNames[]; +extern const std::array<std::string, static_cast<int>(FrameInfoIndex::NumIndexes)> FrameInfoNames; namespace FrameInfoFlags { enum { diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java index a9da77230214..c2613716cd07 100644 --- a/media/java/android/media/tv/tuner/Tuner.java +++ b/media/java/android/media/tv/tuner/Tuner.java @@ -308,6 +308,7 @@ public class Tuner implements AutoCloseable { .write(FrameworkStatsLog.TV_TUNER_STATE_CHANGED, mUserId, FrameworkStatsLog.TV_TUNER_STATE_CHANGED__STATE__UNKNOWN); } + releaseAll(); mHandler.sendMessage(mHandler.obtainMessage(MSG_RESOURCE_LOST)); } }; @@ -610,7 +611,6 @@ public class Tuner implements AutoCloseable { break; } case MSG_RESOURCE_LOST: { - releaseAll(); if (mOnResourceLostListener != null && mOnResourceLostListenerExecutor != null) { mOnResourceLostListenerExecutor.execute( diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java index b061df1423ba..40b0fcff3aac 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java @@ -496,11 +496,14 @@ final class SettingsState { public List<String> setSettingsLocked(String prefix, Map<String, String> keyValues, String packageName) { List<String> changedKeys = new ArrayList<>(); + final Iterator<Map.Entry<String, Setting>> iterator = mSettings.entrySet().iterator(); // Delete old keys with the prefix that are not part of the new set. - for (int i = 0; i < mSettings.keySet().size(); ++i) { - String key = mSettings.keyAt(i); - if (key.startsWith(prefix) && !keyValues.containsKey(key)) { - Setting oldState = mSettings.remove(key); + while (iterator.hasNext()) { + Map.Entry<String, Setting> entry = iterator.next(); + final String key = entry.getKey(); + final Setting oldState = entry.getValue(); + if (key != null && key.startsWith(prefix) && !keyValues.containsKey(key)) { + iterator.remove(); FrameworkStatsLog.write(FrameworkStatsLog.SETTING_CHANGED, key, /* value= */ "", /* newValue= */ "", oldState.value, /* tag */ "", false, diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java index 48c0dc4fb2b8..75eea8db8085 100644 --- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java +++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java @@ -230,7 +230,7 @@ public class SettingsBackupTest { Settings.Global.DEVELOPMENT_RENDER_SHADOWS_IN_COMPOSITOR, Settings.Global.DEVELOPMENT_USE_BLAST_ADAPTER_SV, Settings.Global.DEVELOPMENT_USE_BLAST_ADAPTER_VR, - Settings.Global.DEVELOPMENT_IGNORE_VENDOR_DISPLAY_SETTINGS, + Settings.Global.DEVELOPMENT_WM_DISPLAY_SETTINGS_PATH, Settings.Global.DEVICE_DEMO_MODE, Settings.Global.BATTERY_SAVER_ADAPTIVE_CONSTANTS, Settings.Global.BATTERY_SAVER_CONSTANTS, diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index 2e3ea24f62e2..92b1ca731a30 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -332,6 +332,9 @@ <!-- Permission needed for CTS test - DisplayTest --> <uses-permission android:name="android.permission.OVERRIDE_DISPLAY_MODE_REQUESTS" /> + <!-- Permission needed for CTS test - MatchContentFrameRateTest --> + <uses-permission android:name="android.permission.MODIFY_REFRESH_RATE_SWITCHING_TYPE" /> + <!-- Permission needed for CTS test - TimeManagerTest --> <uses-permission android:name="android.permission.MANAGE_TIME_AND_ZONE_DETECTION" /> diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index 52b41a43c63e..2da958f6b8b9 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -516,20 +516,6 @@ android:excludeFromRecents="true" android:visibleToInstantApps="true"/> - <!-- started from PipController --> - <activity - android:name="com.android.wm.shell.pip.tv.PipMenuActivity" - android:permission="com.android.systemui.permission.SELF" - android:exported="false" - android:theme="@style/PipTheme" - android:launchMode="singleTop" - android:taskAffinity="" - android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation|locale|layoutDirection" - android:resizeableActivity="true" - android:supportsPictureInPicture="true" - androidprv:alwaysFocusable="true" - android:excludeFromRecents="true" /> - <!-- started from TvNotificationPanel --> <activity android:name=".statusbar.tv.notifications.TvNotificationPanelActivity" diff --git a/packages/SystemUI/res/color/background_protect_secondary.xml b/packages/SystemUI/res-keyguard/color/notification_background_dimmed_color.xml index 97744dbe9190..3345e6e42500 100644 --- a/packages/SystemUI/res/color/background_protect_secondary.xml +++ b/packages/SystemUI/res-keyguard/color/notification_background_dimmed_color.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <!-- - ~ Copyright (C) 2017 The Android Open Source Project + ~ 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. @@ -14,7 +14,6 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License --> - <selector xmlns:android="http://schemas.android.com/apk/res/android"> - <item android:color="?attr/wallpaperTextColorSecondary" /> + <item android:alpha="0.7" android:color="?android:attr/colorBackground" /> </selector>
\ No newline at end of file diff --git a/packages/SystemUI/res-keyguard/drawable/kg_emergency_button_background.xml b/packages/SystemUI/res-keyguard/drawable/kg_emergency_button_background.xml index cc2089f69287..99c70a54a3cd 100644 --- a/packages/SystemUI/res-keyguard/drawable/kg_emergency_button_background.xml +++ b/packages/SystemUI/res-keyguard/drawable/kg_emergency_button_background.xml @@ -19,13 +19,13 @@ <item android:id="@android:id/background"> <shape android:color="@android:color/transparent"> - <stroke android:width="1dp" android:color="?attr/wallpaperTextColorSecondary"/> + <stroke android:width="1dp" android:color="?android:attr/textColorSecondary"/> <corners android:radius="24dp"/> </shape> </item> <item android:id="@android:id/mask"> <shape android:shape="rectangle"> - <solid android:color="?attr/wallpaperTextColorSecondary"/> + <solid android:color="?android:attr/textColorSecondary"/> <corners android:radius="24dp"/> </shape> </item> diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_password_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_password_view.xml index b06d6a989cb8..e1550aa0c87c 100644 --- a/packages/SystemUI/res-keyguard/layout/keyguard_password_view.xml +++ b/packages/SystemUI/res-keyguard/layout/keyguard_password_view.xml @@ -34,7 +34,7 @@ android:layout_weight="7" /> - <!-- Password entry field --> + <!-- Password entry field --> <FrameLayout android:layout_height="wrap_content" android:layout_width="280dp" @@ -51,9 +51,9 @@ android:textStyle="normal" android:inputType="textPassword" android:textSize="16sp" - android:textColor="?attr/wallpaperTextColor" android:textAppearance="?android:attr/textAppearanceMedium" android:imeOptions="flagForceAscii|actionDone" + android:textCursorDrawable="@null" android:maxLength="500" /> @@ -65,7 +65,7 @@ android:contentDescription="@string/accessibility_ime_switch_button" android:clickable="true" android:padding="8dip" - android:tint="@color/background_protected" + android:tint="?android:attr/textColorPrimary" android:layout_gravity="end|center_vertical" android:background="?android:attr/selectableItemBackground" android:visibility="gone" diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml index a75b35d117b6..87c98d2e9597 100644 --- a/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml +++ b/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml @@ -46,11 +46,10 @@ android:id="@+id/pinEntry" android:layout_width="@dimen/keyguard_security_width" android:layout_height="match_parent" - android:gravity="center" + style="@style/Widget.TextView.Password" android:layout_centerHorizontal="true" android:layout_marginRight="72dp" androidprv:scaledTextSize="@integer/scaled_password_text_size" - android:textColor="?attr/wallpaperTextColor" android:contentDescription="@string/keyguard_accessibility_pin_area" /> <View diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_sim_pin_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_sim_pin_view.xml index cd61a3775bf7..912d7bbf7ef5 100644 --- a/packages/SystemUI/res-keyguard/layout/keyguard_sim_pin_view.xml +++ b/packages/SystemUI/res-keyguard/layout/keyguard_sim_pin_view.xml @@ -64,7 +64,6 @@ android:layout_centerHorizontal="true" android:layout_marginRight="72dp" androidprv:scaledTextSize="@integer/scaled_password_text_size" - android:textColor="?attr/wallpaperTextColor" android:contentDescription="@string/keyguard_accessibility_sim_pin_area" /> <View diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_sim_puk_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_sim_puk_view.xml index bb757356f2b9..81b49648ab62 100644 --- a/packages/SystemUI/res-keyguard/layout/keyguard_sim_puk_view.xml +++ b/packages/SystemUI/res-keyguard/layout/keyguard_sim_puk_view.xml @@ -65,7 +65,6 @@ android:layout_centerHorizontal="true" android:layout_marginRight="72dp" androidprv:scaledTextSize="@integer/scaled_password_text_size" - android:textColor="?attr/wallpaperTextColor" android:contentDescription="@string/keyguard_accessibility_sim_puk_area" /> <View diff --git a/packages/SystemUI/res-keyguard/values/styles.xml b/packages/SystemUI/res-keyguard/values/styles.xml index 401f3e3e0685..bc48e8fe3eea 100644 --- a/packages/SystemUI/res-keyguard/values/styles.xml +++ b/packages/SystemUI/res-keyguard/values/styles.xml @@ -20,41 +20,46 @@ <resources> <!-- Keyguard PIN pad styles --> <style name="Keyguard.TextView" parent="@android:style/Widget.DeviceDefault.TextView"> - <item name="android:textColor">?attr/wallpaperTextColorSecondary</item> <item name="android:textSize">@dimen/kg_status_line_font_size</item> </style> <style name="Keyguard.TextView.EmergencyButton" parent="Theme.SystemUI"> - <item name="android:textColor">?attr/wallpaperTextColorSecondary</item> + <item name="android:textColor">?android:attr/textColorSecondary</item> <item name="android:textSize">14dp</item> <item name="android:background">@drawable/kg_emergency_button_background</item> <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item> <item name="android:paddingLeft">12dp</item> <item name="android:paddingRight">12dp</item> </style> - <style name="Widget.TextView.NumPadKey" parent="@android:style/Widget.TextView"> + <style name="Widget.TextView.NumPadKey" parent="@android:style/Widget.DeviceDefault.TextView"> <item name="android:singleLine">true</item> <item name="android:gravity">center_horizontal|center_vertical</item> <item name="android:background">@null</item> <item name="android:textSize">32sp</item> + <item name="android:textColor">?android:attr/textColorPrimary</item> <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item> - <item name="android:textColor">?attr/wallpaperTextColor</item> <item name="android:paddingBottom">-16dp</item> + <item name="android:colorControlHighlight">?android:attr/textColorPrimary</item> + </style> + <style name="Widget.TextView.Password" parent="@android:style/Widget.TextView"> + <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item> + <item name="android:gravity">center</item> + <item name="android:textColor">?android:attr/textColorPrimary</item> </style> - <style name="Keyguard.ImageButton.NumPadDelete" parent="@android:style/Widget.ImageButton"> + <style name="Keyguard.ImageButton.NumPadDelete" parent="@android:style/Widget.DeviceDefault.ImageButton"> <item name="android:src">@drawable/ic_backspace_black_24dp</item> <item name="android:paddingBottom">11sp</item> - <item name="android:tint">@color/pin_delete_color</item> + <item name="android:tint">?android:attr/textColorSecondary</item> <item name="android:tintMode">src_in</item> <item name="android:src">@drawable/ic_backspace_black_24dp</item> </style> - <style name="Keyguard.ImageButton.NumPadEnter" parent="@android:style/Widget.ImageButton"> + <style name="Keyguard.ImageButton.NumPadEnter" parent="@android:style/Widget.DeviceDefault.ImageButton"> <item name="android:src">@drawable/ic_keyboard_tab_36dp</item> <item name="android:paddingBottom">11sp</item> </style> - <style name="Widget.TextView.NumPadKey.Klondike" parent="Widget.TextView.NumPadKey"> + <style name="Widget.TextView.NumPadKey.Klondike"> <item name="android:textSize">12sp</item> <item name="android:fontFamily">@*android:string/config_bodyFontFamily</item> - <item name="android:textColor">?attr/wallpaperTextColorSecondary</item> + <item name="android:textColor">?android:attr/textColorSecondary</item> <item name="android:paddingBottom">0dp</item> </style> @@ -95,15 +100,9 @@ </style> <style name="PasswordTheme" parent="Theme.SystemUI"> - <item name="android:textColor">?attr/wallpaperTextColor</item> - <item name="android:colorControlNormal">?attr/wallpaperTextColor</item> - <item name="android:colorControlActivated">?attr/wallpaperTextColor</item> - </style> - - <style name="PasswordTheme.Light" parent="Theme.SystemUI.Light"> - <item name="android:textColor">?attr/wallpaperTextColor</item> - <item name="android:colorControlNormal">?attr/wallpaperTextColor</item> - <item name="android:colorControlActivated">?attr/wallpaperTextColor</item> + <item name="android:textColor">?android:attr/textColorPrimary</item> + <item name="android:colorControlNormal">?android:attr/textColorPrimary</item> + <item name="android:colorControlActivated">?android:attr/textColorPrimary</item> </style> <style name="Theme.SystemUI.KeyguardPresentation"> diff --git a/packages/SystemUI/res/color/pin_delete_color.xml b/packages/SystemUI/res/color/pin_delete_color.xml index 7d4f1321d52f..c1b4cf87e923 100644 --- a/packages/SystemUI/res/color/pin_delete_color.xml +++ b/packages/SystemUI/res/color/pin_delete_color.xml @@ -15,5 +15,5 @@ ~ limitations under the License --> <selector xmlns:android="http://schemas.android.com/apk/res/android"> - <item android:alpha="0.61" android:color="?attr/wallpaperTextColor" /> + <item android:alpha="0.61" android:color="?android:attr/textColor" /> </selector>
\ No newline at end of file diff --git a/packages/SystemUI/res/color/pin_divider_color.xml b/packages/SystemUI/res/color/pin_divider_color.xml index aff23171eee3..e05772fab8b2 100644 --- a/packages/SystemUI/res/color/pin_divider_color.xml +++ b/packages/SystemUI/res/color/pin_divider_color.xml @@ -15,5 +15,5 @@ ~ limitations under the License --> <selector xmlns:android="http://schemas.android.com/apk/res/android"> - <item android:alpha="0.45" android:color="?attr/wallpaperTextColorSecondary" /> + <item android:alpha="0.45" android:color="?android:attr/textColorSecondary" /> </selector>
\ No newline at end of file diff --git a/packages/SystemUI/res/color/qs_background_dark.xml b/packages/SystemUI/res/color/qs_background_dark.xml index 24afebde046b..c47959a04fff 100644 --- a/packages/SystemUI/res/color/qs_background_dark.xml +++ b/packages/SystemUI/res/color/qs_background_dark.xml @@ -16,5 +16,5 @@ <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:alpha="1" - android:color="?android:attr/colorBackgroundFloating"/> + android:color="?android:attr/colorBackground"/> </selector> diff --git a/packages/SystemUI/res/drawable/notification_guts_bg.xml b/packages/SystemUI/res/drawable/notification_guts_bg.xml index 2fe6c7b2d1a2..d62687883c35 100644 --- a/packages/SystemUI/res/drawable/notification_guts_bg.xml +++ b/packages/SystemUI/res/drawable/notification_guts_bg.xml @@ -16,7 +16,7 @@ --> <shape xmlns:android="http://schemas.android.com/apk/res/android"> - <solid android:color="@color/notification_material_background_color" /> + <solid android:color="?android:attr/colorBackground" /> <!--The radius is 1dp smaller than the notification one, to avoid aliasing bugs on the corners --> <corners android:radius="1dp" /> </shape> diff --git a/packages/SystemUI/res/drawable/notification_material_bg.xml b/packages/SystemUI/res/drawable/notification_material_bg.xml index ae456631c4f1..1e9be2fb9b05 100644 --- a/packages/SystemUI/res/drawable/notification_material_bg.xml +++ b/packages/SystemUI/res/drawable/notification_material_bg.xml @@ -19,7 +19,7 @@ android:color="@color/notification_ripple_untinted_color"> <item> <shape> - <solid android:color="@color/notification_material_background_color" /> + <solid android:color="?android:attr/colorBackground" /> </shape> </item> </ripple>
\ No newline at end of file diff --git a/packages/SystemUI/res/drawable/notification_material_bg_dim.xml b/packages/SystemUI/res/drawable/notification_material_bg_dim.xml index b6a8b70bb3e0..1127d3c247fd 100644 --- a/packages/SystemUI/res/drawable/notification_material_bg_dim.xml +++ b/packages/SystemUI/res/drawable/notification_material_bg_dim.xml @@ -17,7 +17,7 @@ <ripple xmlns:android="http://schemas.android.com/apk/res/android"> <item> <shape> - <solid android:color="@color/notification_material_background_dimmed_color" /> + <solid android:color="@color/notification_background_dimmed_color" /> </shape> </item> </ripple> diff --git a/packages/SystemUI/res/drawable/qs_navbar_scrim.xml b/packages/SystemUI/res/drawable/qs_navbar_scrim.xml deleted file mode 100644 index bbb2617db4a0..000000000000 --- a/packages/SystemUI/res/drawable/qs_navbar_scrim.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> - -<!-- - ~ Copyright (C) 2014 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 - --> - -<shape xmlns:android="http://schemas.android.com/apk/res/android"> - <gradient - android:type="linear" - android:angle="90" - android:startColor="#55000000" - android:endColor="#00000000" /> -</shape>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/app_ops_info.xml b/packages/SystemUI/res/layout/app_ops_info.xml index 667c857b6967..ecf572ba4a5c 100644 --- a/packages/SystemUI/res/layout/app_ops_info.xml +++ b/packages/SystemUI/res/layout/app_ops_info.xml @@ -26,7 +26,7 @@ android:orientation="vertical" android:paddingStart="@*android:dimen/notification_content_margin_start" android:paddingEnd="@*android:dimen/notification_content_margin_end" - android:background="@color/notification_material_background_color" + android:background="?android:attr/colorBackground" android:theme="@*android:style/Theme.DeviceDefault.Light"> <!-- Package Info --> diff --git a/packages/SystemUI/res/layout/feedback_info.xml b/packages/SystemUI/res/layout/feedback_info.xml index 5e847a28558e..7047c1b21961 100644 --- a/packages/SystemUI/res/layout/feedback_info.xml +++ b/packages/SystemUI/res/layout/feedback_info.xml @@ -26,7 +26,7 @@ android:orientation="vertical" android:paddingStart="@*android:dimen/notification_content_margin_start" android:paddingEnd="@*android:dimen/notification_content_margin_end" - android:background="@color/notification_material_background_color" + android:background="?android:attr/colorBackground" android:theme="@*android:style/Theme.DeviceDefault.Light"> <!-- Package Info --> diff --git a/packages/SystemUI/res/layout/notification_conversation_info.xml b/packages/SystemUI/res/layout/notification_conversation_info.xml index 10ad8291636e..fcc1aed65470 100644 --- a/packages/SystemUI/res/layout/notification_conversation_info.xml +++ b/packages/SystemUI/res/layout/notification_conversation_info.xml @@ -24,7 +24,7 @@ android:clipChildren="true" android:clipToPadding="true" android:orientation="vertical" - android:background="@color/notification_material_background_color" + android:background="?android:attr/colorBackground" android:paddingStart="12dp"> <!-- Package Info --> diff --git a/packages/SystemUI/res/layout/notification_guts.xml b/packages/SystemUI/res/layout/notification_guts.xml index 5399f57c322f..fb75dd348a41 100644 --- a/packages/SystemUI/res/layout/notification_guts.xml +++ b/packages/SystemUI/res/layout/notification_guts.xml @@ -22,5 +22,4 @@ android:focusable="true" android:id="@+id/notification_guts" android:visibility="gone" - android:gravity="top|start" - android:theme="@*android:style/Theme.DeviceDefault.Light"/> + android:gravity="top|start"/> diff --git a/packages/SystemUI/res/layout/notification_snooze.xml b/packages/SystemUI/res/layout/notification_snooze.xml index 253bc328c5b8..dc9d92001351 100644 --- a/packages/SystemUI/res/layout/notification_snooze.xml +++ b/packages/SystemUI/res/layout/notification_snooze.xml @@ -20,7 +20,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" - android:background="@color/notification_material_background_color" + android:background="?android:attr/colorBackground" android:theme="@style/Theme.SystemUI"> <RelativeLayout diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml index 1d4b98242519..75f76b431da8 100644 --- a/packages/SystemUI/res/layout/status_bar_expanded.xml +++ b/packages/SystemUI/res/layout/status_bar_expanded.xml @@ -92,13 +92,5 @@ layout="@layout/keyguard_bottom_area" android:visibility="gone" /> - <com.android.systemui.statusbar.AlphaOptimizedView - android:id="@+id/qs_navbar_scrim" - android:layout_height="96dp" - android:layout_width="match_parent" - android:layout_gravity="bottom" - android:visibility="invisible" - android:background="@drawable/qs_navbar_scrim" /> - <include layout="@layout/status_bar_expanded_plugin_frame"/> </com.android.systemui.statusbar.phone.NotificationPanelView> diff --git a/packages/SystemUI/res/layout/status_bar_notification_section_header.xml b/packages/SystemUI/res/layout/status_bar_notification_section_header.xml index b5822c889f1c..e33f186dcbb7 100644 --- a/packages/SystemUI/res/layout/status_bar_notification_section_header.xml +++ b/packages/SystemUI/res/layout/status_bar_notification_section_header.xml @@ -58,7 +58,6 @@ android:src="@drawable/status_bar_notification_section_header_clear_btn" android:contentDescription="@string/accessibility_notification_section_header_gentle_clear_all" android:scaleType="center" - android:tint="?attr/wallpaperTextColor" android:tintMode="src_in" android:visibility="gone" android:forceHasOverlappingRendering="false" diff --git a/packages/SystemUI/res/values-night/colors.xml b/packages/SystemUI/res/values-night/colors.xml index cb9e178de243..8cc747bf04d6 100644 --- a/packages/SystemUI/res/values-night/colors.xml +++ b/packages/SystemUI/res/values-night/colors.xml @@ -17,22 +17,13 @@ NOTE: You might also want to edit: core/res/res/values-night/*.xml --> <resources> - <!-- The color of the material notification background --> - <color name="notification_material_background_color">@*android:color/notification_material_background_color</color> - <!-- The color of the legacy notifications with customs backgrounds (gingerbread and lollipop.) It's fine to override this color since at that point the shade was dark. --> - <color name="notification_legacy_background_color">@*android:color/notification_material_background_color</color> - - <!-- The color of the material notification background when dimmed --> - <color name="notification_material_background_dimmed_color">#aa000000</color> + <color name="notification_legacy_background_color">@color/GM2_grey_900</color> <!-- The color of the dividing line between grouped notifications while . --> <color name="notification_divider_color">#212121</color> - <!-- The background color of the notification shade --> - <color name="notification_shade_background_color">@color/GM2_grey_900</color> - <!-- The color of the gear shown behind a notification --> <color name="notification_gear_color">@color/GM2_grey_500</color> diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml index be36316d013c..c51e0bf2c31b 100644 --- a/packages/SystemUI/res/values/colors.xml +++ b/packages/SystemUI/res/values/colors.xml @@ -80,21 +80,12 @@ <!-- The color of the legacy notification background --> <color name="notification_legacy_background_color">#ff1a1a1a</color> - <!-- The color of the material notification background --> - <color name="notification_material_background_color">@*android:color/notification_material_background_color</color> - - <!-- The color of the material notification background when dimmed --> - <color name="notification_material_background_dimmed_color">#ccffffff</color> - <!-- The color of the material notification background when dark --> <color name="notification_material_background_dark_color">#ff333333</color> <!-- The color of the dividing line between grouped notifications. --> <color name="notification_divider_color">#FF616161</color> - <!-- The background color of the notification shade --> - <color name="notification_shade_background_color">@color/GM2_grey_200</color> - <!-- The color of the ripples on the untinted notifications --> <color name="notification_ripple_untinted_color">#28000000</color> diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml index f38e653190b0..0697c5c0084c 100644 --- a/packages/SystemUI/res/values/styles.xml +++ b/packages/SystemUI/res/values/styles.xml @@ -334,7 +334,6 @@ <item name="wallpaperTextColorSecondary">@*android:color/secondary_text_material_light</item> <item name="android:colorError">@*android:color/error_color_material_light</item> <item name="android:colorControlHighlight">#40000000</item> - <item name="passwordStyle">@style/PasswordTheme.Light</item> <item name="shadowRadius">0</item> <!-- Needed for MediaRoute chooser dialog --> @@ -356,8 +355,8 @@ </style> <style name="LockPatternStyle"> - <item name="*android:regularColor">?attr/wallpaperTextColor</item> - <item name="*android:successColor">?attr/wallpaperTextColor</item> + <item name="*android:regularColor">?android:attr/textColorPrimary</item> + <item name="*android:successColor">?android:attr/textColorPrimary</item> <item name="*android:errorColor">?android:attr/colorError</item> </style> @@ -555,8 +554,8 @@ <style name="TextAppearance.NotificationSectionHeaderButton" - parent="@android:style/Widget.Material.Button.Borderless"> - <item name="android:textColor">?attr/wallpaperTextColor</item> + parent="@android:style/Widget.DeviceDefault.Button.Borderless"> + <item name="android:textColor">?android:attr/textColorPrimary</item> <item name="android:textAllCaps">false</item> <item name="android:textSize">14sp</item> <item name="android:minWidth">0dp</item> diff --git a/packages/SystemUI/src/com/android/keyguard/EmergencyButton.java b/packages/SystemUI/src/com/android/keyguard/EmergencyButton.java index 487e0d8ea38e..b7d7498e8960 100644 --- a/packages/SystemUI/src/com/android/keyguard/EmergencyButton.java +++ b/packages/SystemUI/src/com/android/keyguard/EmergencyButton.java @@ -41,6 +41,7 @@ import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.util.EmergencyAffordanceManager; import com.android.internal.widget.LockPatternUtils; +import com.android.settingslib.Utils; import com.android.systemui.Dependency; import com.android.systemui.util.EmergencyDialerConstants; @@ -148,6 +149,17 @@ public class EmergencyButton extends Button { return super.onTouchEvent(event); } + /** + * Reload colors from resources. + **/ + public void reloadColors() { + int color = Utils.getColorAttrDefaultColor(getContext(), + android.R.attr.textColorSecondary); + setTextColor(color); + setBackground(getContext() + .getDrawable(com.android.systemui.R.drawable.kg_emergency_button_background)); + } + @Override public boolean performLongClick() { return super.performLongClick(); diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java index 217cf701b265..5760565aaab1 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java @@ -20,6 +20,7 @@ import static com.android.internal.util.LatencyTracker.ACTION_CHECK_CREDENTIAL; import static com.android.internal.util.LatencyTracker.ACTION_CHECK_CREDENTIAL_UNLOCKED; import static com.android.keyguard.KeyguardAbsKeyInputView.MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT; +import android.annotation.CallSuper; import android.content.res.ColorStateList; import android.os.AsyncTask; import android.os.CountDownTimer; @@ -87,6 +88,7 @@ public abstract class KeyguardAbsKeyInputViewController<T extends KeyguardAbsKey @Override protected void onViewAttached() { + super.onViewAttached(); mView.setKeyDownListener(mKeyDownListener); mView.setEnableHaptics(mLockPatternUtils.isTactileFeedbackEnabled()); EmergencyButton button = mView.findViewById(R.id.emergency_call_button); @@ -110,6 +112,13 @@ public abstract class KeyguardAbsKeyInputViewController<T extends KeyguardAbsKey } } + @CallSuper + @Override + public void reloadColors() { + super.reloadColors(); + mMessageAreaController.reloadColors(); + } + @Override public boolean needsInput() { return false; diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java index fbda818740e8..6aa5e0df3653 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java @@ -16,6 +16,7 @@ package com.android.keyguard; +import android.annotation.CallSuper; import android.content.res.ColorStateList; import android.content.res.Resources; import android.telephony.TelephonyManager; @@ -24,6 +25,7 @@ import android.view.inputmethod.InputMethodManager; import com.android.internal.util.LatencyTracker; import com.android.internal.widget.LockPatternUtils; import com.android.keyguard.KeyguardSecurityModel.SecurityMode; +import com.android.systemui.R; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.util.ViewController; import com.android.systemui.util.concurrency.DelayableExecutor; @@ -37,6 +39,7 @@ public abstract class KeyguardInputViewController<T extends KeyguardInputView> private final SecurityMode mSecurityMode; private final KeyguardSecurityCallback mKeyguardSecurityCallback; + private final EmergencyButton mEmergencyButton; private boolean mPaused; @@ -68,6 +71,7 @@ public abstract class KeyguardInputViewController<T extends KeyguardInputView> super(view); mSecurityMode = securityMode; mKeyguardSecurityCallback = keyguardSecurityCallback; + mEmergencyButton = view == null ? null : view.findViewById(R.id.emergency_call_button); } @Override @@ -112,6 +116,16 @@ public abstract class KeyguardInputViewController<T extends KeyguardInputView> public void showMessage(CharSequence message, ColorStateList colorState) { } + /** + * Reload colors from resources. + **/ + @CallSuper + public void reloadColors() { + if (mEmergencyButton != null) { + mEmergencyButton.reloadColors(); + } + } + public void startAppearAnimation() { mView.startAppearAnimation(); } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java index 1a0a4370fca4..561ea4075291 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java @@ -29,6 +29,7 @@ import android.util.TypedValue; import android.view.View; import android.widget.TextView; +import com.android.settingslib.Utils; import com.android.systemui.R; import java.lang.ref.WeakReference; @@ -69,7 +70,7 @@ public class KeyguardMessageArea extends TextView implements SecurityMessageDisp void onThemeChanged() { TypedArray array = mContext.obtainStyledAttributes(new int[] { - R.attr.wallpaperTextColor + android.R.attr.textColor }); ColorStateList newTextColors = ColorStateList.valueOf(array.getColor(0, Color.RED)); array.recycle(); @@ -77,6 +78,11 @@ public class KeyguardMessageArea extends TextView implements SecurityMessageDisp update(); } + void reloadColor() { + mDefaultColorState = Utils.getColorAttr(getContext(), android.R.attr.textColorPrimary); + update(); + } + void onDensityOrFontScaleChanged() { TypedArray array = mContext.obtainStyledAttributes(R.style.Keyguard_TextView, new int[] { android.R.attr.textSize diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java index 1618e8e58055..6e40f025da50 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java @@ -93,6 +93,13 @@ public class KeyguardMessageAreaController extends ViewController<KeyguardMessag mView.setNextMessageColor(colorState); } + /** + * Reload colors from resources. + **/ + public void reloadColors() { + mView.reloadColor(); + } + /** Factory for creating {@link com.android.keyguard.KeyguardMessageAreaController}. */ public static class Factory { private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java index d34ea8c5e018..5e339172ca28 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java @@ -16,6 +16,7 @@ package com.android.keyguard; +import android.content.res.ColorStateList; import android.content.res.Resources; import android.os.UserHandle; import android.text.Editable; @@ -30,12 +31,14 @@ import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputMethodInfo; import android.view.inputmethod.InputMethodManager; import android.view.inputmethod.InputMethodSubtype; -import android.widget.TextView; +import android.widget.EditText; +import android.widget.ImageView; import android.widget.TextView.OnEditorActionListener; import com.android.internal.util.LatencyTracker; import com.android.internal.widget.LockPatternUtils; import com.android.keyguard.KeyguardSecurityModel.SecurityMode; +import com.android.settingslib.Utils; import com.android.systemui.R; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.util.concurrency.DelayableExecutor; @@ -51,8 +54,8 @@ public class KeyguardPasswordViewController private final InputMethodManager mInputMethodManager; private final DelayableExecutor mMainExecutor; private final boolean mShowImeAtScreenOn; - private TextView mPasswordEntry; - private View mSwitchImeButton; + private EditText mPasswordEntry; + private ImageView mSwitchImeButton; private final OnEditorActionListener mOnEditorActionListener = (v, actionId, event) -> { // Check if this was the result of hitting the enter key @@ -88,6 +91,18 @@ public class KeyguardPasswordViewController } }; + @Override + public void reloadColors() { + super.reloadColors(); + int textColor = Utils.getColorAttr(mView.getContext(), + android.R.attr.textColorPrimary).getDefaultColor(); + mPasswordEntry.setTextColor(textColor); + mPasswordEntry.setHighlightColor(textColor); + mPasswordEntry.setBackgroundTintList(ColorStateList.valueOf(textColor)); + mPasswordEntry.setForegroundTintList(ColorStateList.valueOf(textColor)); + mSwitchImeButton.setImageTintList(ColorStateList.valueOf(textColor)); + } + protected KeyguardPasswordViewController(KeyguardPasswordView view, KeyguardUpdateMonitor keyguardUpdateMonitor, SecurityMode securityMode, diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java index 730c17787908..2aaf748e2415 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java @@ -33,6 +33,7 @@ import com.android.internal.widget.LockPatternView.Cell; import com.android.internal.widget.LockscreenCredential; import com.android.keyguard.EmergencyButton.EmergencyButtonCallback; import com.android.keyguard.KeyguardSecurityModel.SecurityMode; +import com.android.settingslib.Utils; import com.android.systemui.R; import java.util.List; @@ -197,6 +198,7 @@ public class KeyguardPatternViewController @Override protected void onViewAttached() { + super.onViewAttached(); mLockPatternView.setOnPatternListener(new UnlockPatternListener()); mLockPatternView.setSaveEnabled(false); mLockPatternView.setInStealthMode(!mLockPatternUtils.isVisiblePatternEnabled( @@ -252,6 +254,16 @@ public class KeyguardPatternViewController } @Override + public void reloadColors() { + super.reloadColors(); + mMessageAreaController.reloadColors(); + int textColor = Utils.getColorAttr(mLockPatternView.getContext(), + android.R.attr.textColorPrimary).getDefaultColor(); + int errorColor = Utils.getColorError(mLockPatternView.getContext()).getDefaultColor(); + mLockPatternView.setColors(textColor, textColor, errorColor); + } + + @Override public void onPause() { super.onPause(); diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java index 7fa43116a7b1..4ddfccb21c73 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java @@ -24,12 +24,16 @@ import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_TIMEOUT; import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_USER_REQUEST; import android.content.Context; +import android.content.res.ColorStateList; import android.graphics.Rect; import android.util.AttributeSet; +import android.view.ContextThemeWrapper; import android.view.KeyEvent; import android.view.View; +import android.widget.ImageButton; import com.android.internal.widget.LockscreenCredential; +import com.android.settingslib.Utils; import com.android.systemui.R; /** @@ -39,8 +43,9 @@ public abstract class KeyguardPinBasedInputView extends KeyguardAbsKeyInputView protected PasswordTextView mPasswordEntry; private View mOkButton; - private View mDeleteButton; - private View[] mButtons = new View[10]; + private ImageButton mDeleteButton; + private NumPadKey[] mButtons = new NumPadKey[10]; + private View mDivider; public KeyguardPinBasedInputView(Context context) { this(context, null); @@ -147,6 +152,7 @@ public abstract class KeyguardPinBasedInputView extends KeyguardAbsKeyInputView mDeleteButton = findViewById(R.id.delete_button); mDeleteButton.setVisibility(View.VISIBLE); + mDivider = findViewById(R.id.divider); mButtons[0] = findViewById(R.id.key0); mButtons[1] = findViewById(R.id.key1); @@ -161,6 +167,26 @@ public abstract class KeyguardPinBasedInputView extends KeyguardAbsKeyInputView mPasswordEntry.requestFocus(); super.onFinishInflate(); + reloadColors(); + } + + /** + * Reload colors from resources. + **/ + public void reloadColors() { + for (NumPadKey key : mButtons) { + key.reloadColors(); + } + mPasswordEntry.reloadColors(); + int deleteColor = Utils.getColorAttr(getContext(), android.R.attr.textColorSecondary) + .getDefaultColor(); + mDeleteButton.setImageTintList(ColorStateList.valueOf(deleteColor)); + mDivider.setBackground(getContext().getDrawable(R.drawable.pin_divider)); + + ContextThemeWrapper themedContext = new ContextThemeWrapper(mContext, + R.style.Widget_TextView_NumPadKey); + mDeleteButton.setBackground(themedContext.getDrawable(R.drawable.ripple_drawable_pin)); + mOkButton.setBackground(themedContext.getDrawable(R.drawable.ripple_drawable_pin)); } @Override diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java index 6769436be8ef..fb0d6beca513 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java @@ -53,6 +53,12 @@ public class KeyguardPinViewController } @Override + public void reloadColors() { + super.reloadColors(); + mView.reloadColors(); + } + + @Override void resetState() { super.resetState(); mMessageAreaController.setMessage(""); diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java index 9a511502b475..1a8d420fb394 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java @@ -47,6 +47,7 @@ import com.android.keyguard.KeyguardSecurityModel.SecurityMode; import com.android.keyguard.dagger.KeyguardBouncerScope; import com.android.settingslib.utils.ThreadUtils; import com.android.systemui.shared.system.SysUiStatsLog; +import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.util.ViewController; @@ -69,6 +70,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard private final KeyguardStateController mKeyguardStateController; private final KeyguardSecurityViewFlipperController mSecurityViewFlipperController; private final SecurityCallback mSecurityCallback; + private final ConfigurationController mConfigurationController; private SecurityMode mCurrentSecurityMode = SecurityMode.Invalid; @@ -144,6 +146,18 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard } } }; + private ConfigurationController.ConfigurationListener mConfigurationListener = + new ConfigurationController.ConfigurationListener() { + @Override + public void onOverlayChanged() { + mSecurityViewFlipperController.reloadColors(); + } + + @Override + public void onUiModeChanged() { + mSecurityViewFlipperController.reloadColors(); + } + }; private KeyguardSecurityContainerController(KeyguardSecurityContainer view, AdminSecondaryLockScreenController.Factory adminSecondaryLockScreenControllerFactory, @@ -154,7 +168,8 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard UiEventLogger uiEventLogger, KeyguardStateController keyguardStateController, SecurityCallback securityCallback, - KeyguardSecurityViewFlipperController securityViewFlipperController) { + KeyguardSecurityViewFlipperController securityViewFlipperController, + ConfigurationController configurationController) { super(view); mLockPatternUtils = lockPatternUtils; mUpdateMonitor = keyguardUpdateMonitor; @@ -166,6 +181,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard mSecurityViewFlipperController = securityViewFlipperController; mAdminSecondaryLockScreenController = adminSecondaryLockScreenControllerFactory.create( mKeyguardSecurityCallback); + mConfigurationController = configurationController; } @Override @@ -176,10 +192,12 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard @Override protected void onViewAttached() { mView.setSwipeListener(mSwipeListener); + mConfigurationController.addCallback(mConfigurationListener); } @Override protected void onViewDetached() { + mConfigurationController.removeCallback(mConfigurationListener); } /** */ @@ -459,6 +477,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard private final UiEventLogger mUiEventLogger; private final KeyguardStateController mKeyguardStateController; private final KeyguardSecurityViewFlipperController mSecurityViewFlipperController; + private final ConfigurationController mConfigurationController; @Inject Factory(KeyguardSecurityContainer view, @@ -470,7 +489,8 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard MetricsLogger metricsLogger, UiEventLogger uiEventLogger, KeyguardStateController keyguardStateController, - KeyguardSecurityViewFlipperController securityViewFlipperController) { + KeyguardSecurityViewFlipperController securityViewFlipperController, + ConfigurationController configurationController) { mView = view; mAdminSecondaryLockScreenControllerFactory = adminSecondaryLockScreenControllerFactory; mLockPatternUtils = lockPatternUtils; @@ -480,6 +500,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard mUiEventLogger = uiEventLogger; mKeyguardStateController = keyguardStateController; mSecurityViewFlipperController = securityViewFlipperController; + mConfigurationController = configurationController; } public KeyguardSecurityContainerController create( @@ -487,7 +508,8 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard return new KeyguardSecurityContainerController(mView, mAdminSecondaryLockScreenControllerFactory, mLockPatternUtils, mKeyguardUpdateMonitor, mKeyguardSecurityModel, mMetricsLogger, mUiEventLogger, - mKeyguardStateController, securityCallback, mSecurityViewFlipperController); + mKeyguardStateController, securityCallback, mSecurityViewFlipperController, + mConfigurationController); } } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipperController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipperController.java index 49530355a6fb..f1b504e9f941 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipperController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipperController.java @@ -71,6 +71,15 @@ public class KeyguardSecurityViewFlipperController } } + /** + * Reload colors of ui elements upon theme change. + */ + public void reloadColors() { + for (KeyguardInputViewController<KeyguardInputView> child : mChildren) { + child.reloadColors(); + } + } + @VisibleForTesting KeyguardInputViewController<KeyguardInputView> getSecurityView(SecurityMode securityMode, KeyguardSecurityCallback keyguardSecurityCallback) { diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java index 2cdd7f117594..5b4a7ff3e16e 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java @@ -309,7 +309,7 @@ public class KeyguardSimPinViewController Resources rez = mView.getResources(); String msg; TypedArray array = mView.getContext().obtainStyledAttributes( - new int[] { R.attr.wallpaperTextColor }); + new int[] { android.R.attr.textColor }); int color = array.getColor(0, Color.WHITE); array.recycle(); if (count < 2) { diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java index adb4c13b74d5..eafb33f8195d 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java @@ -189,7 +189,7 @@ public class KeyguardSimPukViewController Resources rez = mView.getResources(); String msg; TypedArray array = mView.getContext().obtainStyledAttributes( - new int[] { R.attr.wallpaperTextColor }); + new int[] { android.R.attr.textColor }); int color = array.getColor(0, Color.WHITE); array.recycle(); if (count < 2) { diff --git a/packages/SystemUI/src/com/android/keyguard/NumPadKey.java b/packages/SystemUI/src/com/android/keyguard/NumPadKey.java index 2205fdd4267d..a5182055e14d 100644 --- a/packages/SystemUI/src/com/android/keyguard/NumPadKey.java +++ b/packages/SystemUI/src/com/android/keyguard/NumPadKey.java @@ -18,9 +18,11 @@ package com.android.keyguard; import android.content.Context; import android.content.res.TypedArray; +import android.graphics.drawable.Drawable; import android.os.PowerManager; import android.os.SystemClock; import android.util.AttributeSet; +import android.view.ContextThemeWrapper; import android.view.HapticFeedbackConstants; import android.view.LayoutInflater; import android.view.MotionEvent; @@ -30,6 +32,7 @@ import android.view.accessibility.AccessibilityManager; import android.widget.TextView; import com.android.internal.widget.LockPatternUtils; +import com.android.settingslib.Utils; import com.android.systemui.R; public class NumPadKey extends ViewGroup { @@ -121,12 +124,29 @@ public class NumPadKey extends ViewGroup { a = context.obtainStyledAttributes(attrs, android.R.styleable.View); if (!a.hasValueOrEmpty(android.R.styleable.View_background)) { - setBackground(mContext.getDrawable(R.drawable.ripple_drawable_pin)); + Drawable rippleDrawable = new ContextThemeWrapper(mContext, + R.style.Widget_TextView_NumPadKey).getDrawable(R.drawable.ripple_drawable_pin); + setBackground(rippleDrawable); } a.recycle(); setContentDescription(mDigitText.getText().toString()); } + /** + * Reload colors from resources. + **/ + public void reloadColors() { + int textColor = Utils.getColorAttr(getContext(), android.R.attr.textColorPrimary) + .getDefaultColor(); + int klondikeColor = Utils.getColorAttr(getContext(), android.R.attr.textColorSecondary) + .getDefaultColor(); + mDigitText.setTextColor(textColor); + mKlondikeText.setTextColor(klondikeColor); + Drawable rippleDrawable = new ContextThemeWrapper(mContext, + R.style.Widget_TextView_NumPadKey).getDrawable(R.drawable.ripple_drawable_pin); + setBackground(rippleDrawable); + } + @Override public boolean onTouchEvent(MotionEvent event) { if (event.getActionMasked() == MotionEvent.ACTION_DOWN) { diff --git a/packages/SystemUI/src/com/android/keyguard/PasswordTextView.java b/packages/SystemUI/src/com/android/keyguard/PasswordTextView.java index c92174a0d8af..5ffc2836b9e3 100644 --- a/packages/SystemUI/src/com/android/keyguard/PasswordTextView.java +++ b/packages/SystemUI/src/com/android/keyguard/PasswordTextView.java @@ -42,6 +42,7 @@ import android.view.animation.AnimationUtils; import android.view.animation.Interpolator; import android.widget.EditText; +import com.android.settingslib.Utils; import com.android.systemui.R; import java.util.ArrayList; @@ -131,8 +132,8 @@ public class PasswordTextView extends View { mCharPadding = a.getDimensionPixelSize(R.styleable.PasswordTextView_charPadding, getContext().getResources().getDimensionPixelSize( R.dimen.password_char_padding)); - int textColor = a.getColor(R.styleable.PasswordTextView_android_textColor, Color.WHITE); - mDrawPaint.setColor(textColor); + mDrawPaint.setColor(a.getColor(R.styleable.PasswordTextView_android_textColor, + Color.WHITE)); } finally { a.recycle(); } @@ -184,6 +185,15 @@ public class PasswordTextView extends View { } } + /** + * Reload colors from resources. + **/ + public void reloadColors() { + int textColor = Utils.getColorAttr(getContext(), android.R.attr.textColorPrimary) + .getDefaultColor(); + mDrawPaint.setColor(textColor); + } + @Override public boolean hasOverlappingRendering() { return false; diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java b/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java index 70a57cc8bd2a..e24a513437ea 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 The Android Open Source Project + * 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. @@ -14,44 +14,39 @@ * limitations under the License. */ -package com.android.systemui.classifier.brightline; +package com.android.systemui.classifier; import static com.android.systemui.classifier.FalsingManagerProxy.FALSING_SUCCESS; +import static com.android.systemui.classifier.FalsingModule.BRIGHT_LINE_GESTURE_CLASSIFERS; -import android.app.ActivityManager; -import android.content.res.Resources; import android.net.Uri; import android.os.Build; import android.util.IndentingPrintWriter; import android.util.Log; import android.view.MotionEvent; -import android.view.ViewConfiguration; import androidx.annotation.NonNull; import com.android.internal.logging.MetricsLogger; -import com.android.systemui.R; -import com.android.systemui.classifier.Classifier; -import com.android.systemui.classifier.FalsingDataProvider; import com.android.systemui.classifier.FalsingDataProvider.SessionListener; -import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.dagger.qualifiers.TestHarness; import com.android.systemui.dock.DockManager; import com.android.systemui.plugins.FalsingManager; -import com.android.systemui.statusbar.phone.NotificationTapHelper; -import com.android.systemui.util.DeviceConfigProxy; import com.android.systemui.util.sensors.ThresholdSensor; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayDeque; -import java.util.ArrayList; +import java.util.Collection; import java.util.List; import java.util.Locale; import java.util.Queue; +import java.util.Set; import java.util.StringJoiner; import java.util.stream.Collectors; import javax.inject.Inject; +import javax.inject.Named; /** * FalsingManager designed to make clear why a touch was rejected. @@ -68,6 +63,7 @@ public class BrightLineFalsingManager implements FalsingManager { private final DockManager mDockManager; private final SingleTapClassifier mSingleTapClassifier; private final DoubleTapClassifier mDoubleTapClassifier; + private final boolean mTestHarness; private final MetricsLogger mMetricsLogger; private int mIsFalseTouchCalls; private static final Queue<String> RECENT_INFO_LOG = @@ -75,7 +71,7 @@ public class BrightLineFalsingManager implements FalsingManager { private static final Queue<DebugSwipeRecord> RECENT_SWIPES = new ArrayDeque<>(RECENT_SWIPE_LOG_SIZE + 1); - private final List<FalsingClassifier> mClassifiers; + private final Collection<FalsingClassifier> mClassifiers; private final SessionListener mSessionListener = new SessionListener() { @Override @@ -93,29 +89,17 @@ public class BrightLineFalsingManager implements FalsingManager { @Inject public BrightLineFalsingManager(FalsingDataProvider falsingDataProvider, - DeviceConfigProxy deviceConfigProxy, @Main Resources resources, - ViewConfiguration viewConfiguration, DockManager dockManager) { + DockManager dockManager, MetricsLogger metricsLogger, + @Named(BRIGHT_LINE_GESTURE_CLASSIFERS) Set<FalsingClassifier> classifiers, + SingleTapClassifier singleTapClassifier, DoubleTapClassifier doubleTapClassifier, + @TestHarness boolean testHarness) { mDataProvider = falsingDataProvider; mDockManager = dockManager; - - mMetricsLogger = new MetricsLogger(); - mClassifiers = new ArrayList<>(); - DistanceClassifier distanceClassifier = - new DistanceClassifier(mDataProvider, deviceConfigProxy); - ProximityClassifier proximityClassifier = - new ProximityClassifier(distanceClassifier, mDataProvider, deviceConfigProxy); - mClassifiers.add(new PointerCountClassifier(mDataProvider)); - mClassifiers.add(new TypeClassifier(mDataProvider)); - mClassifiers.add(new DiagonalClassifier(mDataProvider, deviceConfigProxy)); - mClassifiers.add(distanceClassifier); - mClassifiers.add(proximityClassifier); - mClassifiers.add(new ZigZagClassifier(mDataProvider, deviceConfigProxy)); - - mSingleTapClassifier = new SingleTapClassifier( - mDataProvider, viewConfiguration.getScaledTouchSlop()); - mDoubleTapClassifier = new DoubleTapClassifier(mDataProvider, mSingleTapClassifier, - resources.getDimension(R.dimen.double_tap_slop), - NotificationTapHelper.DOUBLE_TAP_TIMEOUT_MS); + mMetricsLogger = metricsLogger; + mClassifiers = classifiers; + mSingleTapClassifier = singleTapClassifier; + mDoubleTapClassifier = doubleTapClassifier; + mTestHarness = testHarness; mDataProvider.addSessionListener(mSessionListener); } @@ -132,7 +116,7 @@ public class BrightLineFalsingManager implements FalsingManager { return mPreviousResult; } - mPreviousResult = !ActivityManager.isRunningInUserTestHarness() + mPreviousResult = !mTestHarness && !mDataProvider.isJustUnlockedWithFace() && !mDockManager.isDocked() && mClassifiers.stream().anyMatch(falsingClassifier -> { boolean result = falsingClassifier.isFalseTouch(); diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/DiagonalClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/DiagonalClassifier.java index a73ccf575249..92dd8b74e959 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/DiagonalClassifier.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/DiagonalClassifier.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 The Android Open Source Project + * 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.classifier.brightline; +package com.android.systemui.classifier; import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_DIAGONAL_HORIZONTAL_ANGLE_RANGE; import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_DIAGONAL_VERTICAL_ANGLE_RANGE; @@ -23,11 +23,12 @@ import static com.android.systemui.classifier.Classifier.RIGHT_AFFORDANCE; import android.provider.DeviceConfig; -import com.android.systemui.classifier.FalsingDataProvider; import com.android.systemui.util.DeviceConfigProxy; import java.util.Locale; +import javax.inject.Inject; + /** * False on swipes that are too close to 45 degrees. * @@ -47,6 +48,7 @@ class DiagonalClassifier extends FalsingClassifier { private final float mHorizontalAngleRange; private final float mVerticalAngleRange; + @Inject DiagonalClassifier(FalsingDataProvider dataProvider, DeviceConfigProxy deviceConfigProxy) { super(dataProvider); diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/DistanceClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/DistanceClassifier.java index 524d524f38b6..50d55f6f6028 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/DistanceClassifier.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/DistanceClassifier.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 The Android Open Source Project + * 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.classifier.brightline; +package com.android.systemui.classifier; import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_DISTANCE_HORIZONTAL_FLING_THRESHOLD_IN; import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_DISTANCE_HORIZONTAL_SWIPE_THRESHOLD_IN; @@ -27,12 +27,13 @@ import android.provider.DeviceConfig; import android.view.MotionEvent; import android.view.VelocityTracker; -import com.android.systemui.classifier.FalsingDataProvider; import com.android.systemui.util.DeviceConfigProxy; import java.util.List; import java.util.Locale; +import javax.inject.Inject; + /** * Ensure that the swipe + momentum covers a minimum distance. */ @@ -54,6 +55,7 @@ class DistanceClassifier extends FalsingClassifier { private boolean mDistanceDirty; private DistanceVectors mCachedDistance; + @Inject DistanceClassifier(FalsingDataProvider dataProvider, DeviceConfigProxy deviceConfigProxy) { super(dataProvider); diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/DoubleTapClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/DoubleTapClassifier.java index a27ea6172414..1c8f4208edba 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/DoubleTapClassifier.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/DoubleTapClassifier.java @@ -14,15 +14,19 @@ * limitations under the License. */ -package com.android.systemui.classifier.brightline; +package com.android.systemui.classifier; -import android.view.MotionEvent; +import static com.android.systemui.classifier.FalsingModule.DOUBLE_TAP_TIMEOUT_MS; +import static com.android.systemui.classifier.FalsingModule.DOUBLE_TAP_TOUCH_SLOP; -import com.android.systemui.classifier.FalsingDataProvider; +import android.view.MotionEvent; import java.util.List; import java.util.Queue; +import javax.inject.Inject; +import javax.inject.Named; + /** * Returns a false touch if the most two recent gestures are not taps or are too far apart. */ @@ -34,8 +38,10 @@ public class DoubleTapClassifier extends FalsingClassifier { private StringBuilder mReason = new StringBuilder(); + @Inject DoubleTapClassifier(FalsingDataProvider dataProvider, SingleTapClassifier singleTapClassifier, - float doubleTapSlop, long doubleTapTimeMs) { + @Named(DOUBLE_TAP_TOUCH_SLOP) float doubleTapSlop, + @Named(DOUBLE_TAP_TIMEOUT_MS) long doubleTapTimeMs) { super(dataProvider); mSingleTapClassifier = singleTapClassifier; mDoubleTapSlop = doubleTapSlop; diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingClassifier.java index 568dc432729f..82575c3e639e 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingClassifier.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingClassifier.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 The Android Open Source Project + * 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. @@ -14,12 +14,10 @@ * limitations under the License. */ -package com.android.systemui.classifier.brightline; +package com.android.systemui.classifier; import android.view.MotionEvent; -import com.android.systemui.classifier.Classifier; -import com.android.systemui.classifier.FalsingDataProvider; import com.android.systemui.util.sensors.ProximitySensor; import java.util.List; diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingDataProvider.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingDataProvider.java index b29871c1c3d0..009b311f2363 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingDataProvider.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingDataProvider.java @@ -21,9 +21,6 @@ import android.view.MotionEvent; import android.view.MotionEvent.PointerCoords; import android.view.MotionEvent.PointerProperties; -import com.android.systemui.classifier.brightline.BrightLineFalsingManager; -import com.android.systemui.classifier.brightline.FalsingClassifier; -import com.android.systemui.classifier.brightline.TimeLimitedMotionEventBuffer; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.util.time.SystemClock; diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java index 74629411c13d..d4f58c324d39 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java @@ -24,7 +24,6 @@ import android.view.MotionEvent; import androidx.annotation.NonNull; import com.android.systemui.Dumpable; -import com.android.systemui.classifier.brightline.BrightLineFalsingManager; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dump.DumpManager; diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingModule.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingModule.java index 937bcbaa6222..7b7f17e1568b 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingModule.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingModule.java @@ -16,16 +16,69 @@ package com.android.systemui.classifier; +import android.content.res.Resources; +import android.view.ViewConfiguration; + +import com.android.systemui.R; import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.statusbar.phone.NotificationTapHelper; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +import javax.inject.Named; import dagger.Binds; import dagger.Module; +import dagger.Provides; +import dagger.multibindings.ElementsIntoSet; /** Dagger Module for Falsing. */ @Module public interface FalsingModule { + String BRIGHT_LINE_GESTURE_CLASSIFERS = "bright_line_gesture_classifiers"; + String SINGLE_TAP_TOUCH_SLOP = "falsing_single_tap_touch_slop"; + String DOUBLE_TAP_TOUCH_SLOP = "falsing_double_tap_touch_slop"; + String DOUBLE_TAP_TIMEOUT_MS = "falsing_double_tap_timeout_ms"; + /** */ @Binds @SysUISingleton FalsingCollector bindsFalsingCollector(FalsingCollectorImpl impl); + + /** */ + @Provides + @ElementsIntoSet + @Named(BRIGHT_LINE_GESTURE_CLASSIFERS) + static Set<FalsingClassifier> providesBrightLineGestureClassifiers( + DistanceClassifier distanceClassifier, ProximityClassifier proximityClassifier, + PointerCountClassifier pointerCountClassifier, TypeClassifier typeClassifier, + DiagonalClassifier diagonalClassifier, ZigZagClassifier zigZagClassifier) { + return new HashSet<>(Arrays.asList( + pointerCountClassifier, typeClassifier, diagonalClassifier, distanceClassifier, + proximityClassifier, zigZagClassifier)); + } + + /** */ + @Provides + @Named(DOUBLE_TAP_TIMEOUT_MS) + static long providesDoubleTapTimeoutMs() { + return NotificationTapHelper.DOUBLE_TAP_TIMEOUT_MS; + } + + /** */ + @Provides + @Named(DOUBLE_TAP_TOUCH_SLOP) + static float providesDoubleTapTouchSlop(@Main Resources resources) { + return resources.getDimension(R.dimen.double_tap_slop); + } + + /** */ + @Provides + @Named(SINGLE_TAP_TOUCH_SLOP) + static float providesSingleTapTouchSlop(ViewConfiguration viewConfiguration) { + return viewConfiguration.getScaledTouchSlop(); + } } diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/PointerCountClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/PointerCountClassifier.java index dd5d8a8f557b..0565165e1e8d 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/PointerCountClassifier.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/PointerCountClassifier.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 The Android Open Source Project + * 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. @@ -14,17 +14,17 @@ * limitations under the License. */ -package com.android.systemui.classifier.brightline; +package com.android.systemui.classifier; import static com.android.systemui.classifier.Classifier.NOTIFICATION_DRAG_DOWN; import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS; import android.view.MotionEvent; -import com.android.systemui.classifier.FalsingDataProvider; - import java.util.Locale; +import javax.inject.Inject; + /** * False touch if more than one finger touches the screen. * @@ -37,6 +37,7 @@ class PointerCountClassifier extends FalsingClassifier { private static final int MAX_ALLOWED_POINTERS_SWIPE_DOWN = 2; private int mMaxPointerCount; + @Inject PointerCountClassifier(FalsingDataProvider dataProvider) { super(dataProvider); } diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/ProximityClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/ProximityClassifier.java index 3551c241e71e..6e73fc06de4c 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/ProximityClassifier.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/ProximityClassifier.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 The Android Open Source Project + * 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.classifier.brightline; +package com.android.systemui.classifier; import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_PROXIMITY_PERCENT_COVERED_THRESHOLD; import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS; @@ -22,12 +22,13 @@ import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS; import android.provider.DeviceConfig; import android.view.MotionEvent; -import com.android.systemui.classifier.FalsingDataProvider; import com.android.systemui.util.DeviceConfigProxy; import com.android.systemui.util.sensors.ProximitySensor; import java.util.Locale; +import javax.inject.Inject; + /** * False touch if proximity sensor is covered for more than a certain percentage of the gesture. @@ -47,10 +48,11 @@ class ProximityClassifier extends FalsingClassifier { private long mNearDurationNs; private float mPercentNear; + @Inject ProximityClassifier(DistanceClassifier distanceClassifier, FalsingDataProvider dataProvider, DeviceConfigProxy deviceConfigProxy) { super(dataProvider); - this.mDistanceClassifier = distanceClassifier; + mDistanceClassifier = distanceClassifier; mPercentCoveredThreshold = deviceConfigProxy.getFloat( DeviceConfig.NAMESPACE_SYSTEMUI, diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/SingleTapClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/SingleTapClassifier.java index 8c7648149e44..6b7a1413bc74 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/SingleTapClassifier.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/SingleTapClassifier.java @@ -14,14 +14,17 @@ * limitations under the License. */ -package com.android.systemui.classifier.brightline; +package com.android.systemui.classifier; -import android.view.MotionEvent; +import static com.android.systemui.classifier.FalsingModule.SINGLE_TAP_TOUCH_SLOP; -import com.android.systemui.classifier.FalsingDataProvider; +import android.view.MotionEvent; import java.util.List; +import javax.inject.Inject; +import javax.inject.Named; + /** * Falsing classifier that accepts or rejects a single gesture as a tap. */ @@ -29,7 +32,9 @@ public class SingleTapClassifier extends FalsingClassifier { private final float mTouchSlop; private String mReason; - SingleTapClassifier(FalsingDataProvider dataProvider, float touchSlop) { + @Inject + SingleTapClassifier(FalsingDataProvider dataProvider, + @Named(SINGLE_TAP_TOUCH_SLOP) float touchSlop) { super(dataProvider); mTouchSlop = touchSlop; } diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/TimeLimitedMotionEventBuffer.java b/packages/SystemUI/src/com/android/systemui/classifier/TimeLimitedMotionEventBuffer.java index 7430a1eda920..7969b4e83ac0 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/TimeLimitedMotionEventBuffer.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/TimeLimitedMotionEventBuffer.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 The Android Open Source Project + * 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.classifier.brightline; +package com.android.systemui.classifier; import android.view.MotionEvent; diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/TypeClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/TypeClassifier.java index f62871f383b3..711a0fc0f478 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/TypeClassifier.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/TypeClassifier.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 The Android Open Source Project + * 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.classifier.brightline; +package com.android.systemui.classifier; import static com.android.systemui.classifier.Classifier.BOUNCER_UNLOCK; @@ -26,12 +26,13 @@ import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS; import static com.android.systemui.classifier.Classifier.RIGHT_AFFORDANCE; import static com.android.systemui.classifier.Classifier.UNLOCK; -import com.android.systemui.classifier.FalsingDataProvider; +import javax.inject.Inject; /** * Ensure that the swipe direction generally matches that of the interaction type. */ public class TypeClassifier extends FalsingClassifier { + @Inject TypeClassifier(FalsingDataProvider dataProvider) { super(dataProvider); } diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/ZigZagClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/ZigZagClassifier.java index 9ca77d364bc4..383dda498b49 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/ZigZagClassifier.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/ZigZagClassifier.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 The Android Open Source Project + * 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.classifier.brightline; +package com.android.systemui.classifier; import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_ZIGZAG_X_PRIMARY_DEVIANCE; import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_ZIGZAG_X_SECONDARY_DEVIANCE; @@ -25,13 +25,14 @@ import android.graphics.Point; import android.provider.DeviceConfig; import android.view.MotionEvent; -import com.android.systemui.classifier.FalsingDataProvider; import com.android.systemui.util.DeviceConfigProxy; import java.util.ArrayList; import java.util.List; import java.util.Locale; +import javax.inject.Inject; + /** * Penalizes gestures that change direction in either the x or y too much. */ @@ -56,6 +57,7 @@ class ZigZagClassifier extends FalsingClassifier { private float mLastMaxXDeviance; private float mLastMaxYDeviance; + @Inject ZigZagClassifier(FalsingDataProvider dataProvider, DeviceConfigProxy deviceConfigProxy) { super(dataProvider); diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java index 7127f26a7ed2..275bfaa1023c 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java @@ -30,7 +30,6 @@ import android.os.HandlerThread; import android.os.Looper; import android.os.ServiceManager; import android.os.UserHandle; -import android.util.DisplayMetrics; import android.view.Choreographer; import android.view.IWindowManager; import android.view.LayoutInflater; @@ -141,16 +140,6 @@ public class DependencyProvider { return networkController.getDataSaverController(); } - /** */ - @Provides - @SysUISingleton - public DisplayMetrics provideDisplayMetrics(Context context, WindowManager windowManager) { - DisplayMetrics displayMetrics = new DisplayMetrics(); - context.getDisplay().getMetrics(displayMetrics); - return displayMetrics; - } - - /** */ @Provides @SysUISingleton public INotificationManager provideINotificationManager() { diff --git a/packages/SystemUI/src/com/android/systemui/dagger/GlobalModule.java b/packages/SystemUI/src/com/android/systemui/dagger/GlobalModule.java index 53383d65e379..a89c7acea984 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/GlobalModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/GlobalModule.java @@ -16,13 +16,14 @@ package com.android.systemui.dagger; +import android.app.ActivityManager; import android.content.Context; import android.util.DisplayMetrics; import com.android.internal.logging.UiEventLogger; import com.android.internal.logging.UiEventLoggerImpl; +import com.android.systemui.dagger.qualifiers.TestHarness; import com.android.systemui.util.concurrency.GlobalConcurrencyModule; -import com.android.wm.shell.animation.FlingAnimationUtils; import javax.inject.Singleton; @@ -49,15 +50,12 @@ import dagger.Provides; GlobalConcurrencyModule.class}) public class GlobalModule { - // TODO(b/162923491): This should not be a singleton at all, the display metrics can change and - // callers should be creating a new builder on demand - @Singleton + /** */ @Provides - static FlingAnimationUtils.Builder provideFlingAnimationUtilsBuilder( - Context context) { + public DisplayMetrics provideDisplayMetrics(Context context) { DisplayMetrics displayMetrics = new DisplayMetrics(); context.getDisplay().getMetrics(displayMetrics); - return new FlingAnimationUtils.Builder(displayMetrics); + return displayMetrics; } /** Provides an instance of {@link com.android.internal.logging.UiEventLogger} */ @@ -66,4 +64,10 @@ public class GlobalModule { static UiEventLogger provideUiEventLogger() { return new UiEventLoggerImpl(); } + + @Provides + @TestHarness + static boolean provideIsTestHarness() { + return ActivityManager.isRunningInUserTestHarness(); + } } diff --git a/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/TestHarness.java b/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/TestHarness.java new file mode 100644 index 000000000000..f68ab188c93c --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/TestHarness.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.dagger.qualifiers; + +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; + +import javax.inject.Qualifier; + + +/** + * An annotation for injecting whether or not we are running in a test environment. + */ +@Qualifier +@Documented +@Retention(RUNTIME) +public @interface TestHarness { +} diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/LogConfig.java b/packages/SystemUI/src/com/android/systemui/screenshot/LogConfig.java new file mode 100644 index 000000000000..6050c2b90e34 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/screenshot/LogConfig.java @@ -0,0 +1,64 @@ +/* + * 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.screenshot; + +@SuppressWarnings("PointlessBooleanExpression") +class LogConfig { + + /** Log ALL the things... */ + private static final boolean DEBUG_ALL = false; + + /** Default log logTag for screenshot code */ + private static final String TAG_SS = "Screenshot"; + + /** Use class name as Log tag instead of the default */ + private static final boolean TAG_WITH_CLASS_NAME = false; + + /** Action creation and user selection: Share, Save, Edit, Delete, Smart action, etc */ + static final boolean DEBUG_ACTIONS = DEBUG_ALL || false; + + /** Debug info about animations such as start, complete and cancel */ + static final boolean DEBUG_ANIM = DEBUG_ALL || false; + + /** Whenever Uri is supplied to consumer, or onComplete runnable is run() */ + static final boolean DEBUG_CALLBACK = DEBUG_ALL || false; + + /** Logs information about dismissing the screenshot tool */ + static final boolean DEBUG_DISMISS = DEBUG_ALL || false; + + /** Touch or key event driven action or side effects */ + static final boolean DEBUG_INPUT = DEBUG_ALL || false; + + /** Scroll capture usage */ + static final boolean DEBUG_SCROLL = DEBUG_ALL || false; + + /** Service lifecycle events and callbacks */ + static final boolean DEBUG_SERVICE = DEBUG_ALL || false; + + /** Storage related actions, Bitmap.compress, ContentManager, etc */ + static final boolean DEBUG_STORAGE = DEBUG_ALL || false; + + /** High level logical UI actions: timeout, onConfigChanged, insets, show actions, reset */ + static final boolean DEBUG_UI = DEBUG_ALL || false; + + /** Interactions with Window and WindowManager */ + static final boolean DEBUG_WINDOW = DEBUG_ALL || false; + + static String logTag(Class<?> cls) { + return TAG_WITH_CLASS_NAME ? cls.getSimpleName() : TAG_SS; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java index b2ebf3f700b9..f4ce77acb8ec 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java @@ -16,6 +16,11 @@ package com.android.systemui.screenshot; +import static com.android.systemui.screenshot.LogConfig.DEBUG_ACTIONS; +import static com.android.systemui.screenshot.LogConfig.DEBUG_CALLBACK; +import static com.android.systemui.screenshot.LogConfig.DEBUG_STORAGE; +import static com.android.systemui.screenshot.LogConfig.logTag; + import android.app.ActivityTaskManager; import android.app.Notification; import android.app.PendingIntent; @@ -45,7 +50,7 @@ import android.provider.MediaStore; import android.provider.MediaStore.MediaColumns; import android.text.TextUtils; import android.text.format.DateUtils; -import android.util.Slog; +import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.config.sysui.SystemUiDeviceConfigFlags; @@ -73,8 +78,8 @@ import java.util.concurrent.CompletableFuture; /** * An AsyncTask that saves an image to the media store in the background. */ -class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> { - private static final String TAG = "SaveImageInBackgroundTask"; +class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> { + private static final String TAG = logTag(SaveImageInBackgroundTask.class); private static final String SCREENSHOT_FILE_NAME_TEMPLATE = "Screenshot_%s.png"; private static final String SCREENSHOT_ID_TEMPLATE = "Screenshot_%s"; @@ -121,6 +126,9 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> { @Override protected Void doInBackground(Void... paramsUnused) { if (isCancelled()) { + if (DEBUG_STORAGE) { + Log.d(TAG, "cancelled! returning null"); + } return null; } Thread.currentThread().setPriority(Thread.MAX_PRIORITY); @@ -151,9 +159,19 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> { try { // First, write the actual data for our screenshot try (OutputStream out = resolver.openOutputStream(uri)) { + if (DEBUG_STORAGE) { + Log.d(TAG, "Compressing PNG:" + + " w=" + image.getWidth() + " h=" + image.getHeight()); + } if (!image.compress(Bitmap.CompressFormat.PNG, 100, out)) { + if (DEBUG_STORAGE) { + Log.d(TAG, "Bitmap.compress returned false"); + } throw new IOException("Failed to compress"); } + if (DEBUG_STORAGE) { + Log.d(TAG, "Done compressing PNG"); + } } // Next, write metadata to help index the screenshot @@ -181,7 +199,9 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> { exif.setAttribute(ExifInterface.TAG_OFFSET_TIME_ORIGINAL, DateTimeFormatter.ofPattern("XXX").format(time)); } - + if (DEBUG_STORAGE) { + Log.d(TAG, "Writing EXIF metadata"); + } exif.saveAttributes(); } @@ -190,6 +210,9 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> { values.put(MediaColumns.IS_PENDING, 0); values.putNull(MediaColumns.DATE_EXPIRES); resolver.update(uri, values, null, null); + if (DEBUG_STORAGE) { + Log.d(TAG, "Completed writing to ContentManager"); + } } catch (Exception e) { resolver.delete(uri, null); throw e; @@ -215,15 +238,24 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> { mImageData.deleteAction = createDeleteAction(mContext, mContext.getResources(), uri); mParams.mActionsReadyListener.onActionsReady(mImageData); + if (DEBUG_CALLBACK) { + Log.d(TAG, "finished background processing, Calling (Consumer<Uri>) " + + "finisher.accept(\"" + mImageData.uri + "\""); + } mParams.finisher.accept(mImageData.uri); mParams.image = null; } catch (Exception e) { // IOException/UnsupportedOperationException may be thrown if external storage is // not mounted - Slog.e(TAG, "unable to save screenshot", e); + if (DEBUG_STORAGE) { + Log.d(TAG, "Failed to store screenshot", e); + } mParams.clearImage(); mImageData.reset(); mParams.mActionsReadyListener.onActionsReady(mImageData); + if (DEBUG_CALLBACK) { + Log.d(TAG, "Calling (Consumer<Uri>) finisher.accept(null)"); + } mParams.finisher.accept(null); } @@ -245,6 +277,9 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> { // params from the ctor in any case. mImageData.reset(); mParams.mActionsReadyListener.onActionsReady(mImageData); + if (DEBUG_CALLBACK) { + Log.d(TAG, "onCancelled, calling (Consumer<Uri>) finisher.accept(null)"); + } mParams.finisher.accept(null); mParams.clearImage(); } @@ -380,7 +415,9 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> { try { return ActivityTaskManager.getService().getLastResumedActivityUserId(); } catch (RemoteException e) { - Slog.w(TAG, "getUserHandleOfForegroundApplication: ", e); + if (DEBUG_ACTIONS) { + Log.d(TAG, "Failed to get UserHandle of foreground app: ", e); + } return context.getUserId(); } } @@ -421,6 +458,4 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> { .putExtra(ScreenshotController.EXTRA_ID, screenshotId) .putExtra(ScreenshotController.EXTRA_SMART_ACTIONS_ENABLED, smartActionsEnabled); } - - } diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java index 1e916739ca9b..6a4e93be0fe5 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java @@ -16,22 +16,28 @@ package com.android.systemui.screenshot; -import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; import static android.content.res.Configuration.ORIENTATION_PORTRAIT; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; import static android.view.WindowManager.LayoutParams.TYPE_SCREENSHOT; +import static com.android.systemui.screenshot.LogConfig.DEBUG_ANIM; +import static com.android.systemui.screenshot.LogConfig.DEBUG_CALLBACK; +import static com.android.systemui.screenshot.LogConfig.DEBUG_DISMISS; +import static com.android.systemui.screenshot.LogConfig.DEBUG_INPUT; +import static com.android.systemui.screenshot.LogConfig.DEBUG_UI; +import static com.android.systemui.screenshot.LogConfig.DEBUG_WINDOW; +import static com.android.systemui.screenshot.LogConfig.logTag; + import static java.util.Objects.requireNonNull; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.annotation.Nullable; -import android.annotation.SuppressLint; import android.app.Notification; import android.content.ComponentName; import android.content.Context; -import android.content.res.Configuration; +import android.content.pm.ActivityInfo; import android.graphics.Bitmap; import android.graphics.Insets; import android.graphics.PixelFormat; @@ -64,6 +70,7 @@ import android.widget.Toast; import com.android.internal.config.sysui.SystemUiDeviceConfigFlags; import com.android.internal.logging.UiEventLogger; import com.android.internal.policy.PhoneWindow; +import com.android.settingslib.applications.InterestingConfigChanges; import com.android.systemui.R; import com.android.systemui.util.DeviceConfigProxy; @@ -76,6 +83,7 @@ import javax.inject.Inject; * Controls the state and flow for screenshots. */ public class ScreenshotController { + private static final String TAG = logTag(ScreenshotController.class); /** * POD used in the AsyncTask which saves an image in the background. */ @@ -111,12 +119,10 @@ public class ScreenshotController { } } - abstract static class ActionsReadyListener { - abstract void onActionsReady(ScreenshotController.SavedImageData imageData); + interface ActionsReadyListener { + void onActionsReady(ScreenshotController.SavedImageData imageData); } - private static final String TAG = "ScreenshotController"; - // These strings are used for communicating the action invoked to // ScreenshotNotificationSmartActionsProvider. static final String EXTRA_ACTION_TYPE = "android:screenshot_action_type"; @@ -145,7 +151,6 @@ public class ScreenshotController { private final WindowManager mWindowManager; private final WindowManager.LayoutParams mWindowLayoutParams; - private final Display mDisplay; private final DisplayMetrics mDisplayMetrics; private final AccessibilityManager mAccessibilityManager; private final MediaActionSound mCameraSound; @@ -162,15 +167,15 @@ public class ScreenshotController { private Animator mScreenshotAnimation; private Runnable mOnCompleteRunnable; - private boolean mInDarkMode; - private boolean mDirectionLTR; - private boolean mOrientationPortrait; private final Handler mScreenshotHandler = new Handler(Looper.getMainLooper()) { @Override public void handleMessage(Message msg) { switch (msg.what) { case MESSAGE_CORNER_TIMEOUT: + if (DEBUG_UI) { + Log.d(TAG, "Corner timeout hit"); + } mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_INTERACTION_TIMEOUT); ScreenshotController.this.dismissScreenshot(false); break; @@ -180,6 +185,15 @@ public class ScreenshotController { } }; + /** Tracks config changes that require re-creating UI */ + private final InterestingConfigChanges mConfigChanges = new InterestingConfigChanges( + ActivityInfo.CONFIG_ORIENTATION + | ActivityInfo.CONFIG_LAYOUT_DIRECTION + | ActivityInfo.CONFIG_LOCALE + | ActivityInfo.CONFIG_UI_MODE + | ActivityInfo.CONFIG_SCREEN_LAYOUT + | ActivityInfo.CONFIG_ASSETS_PATHS); + @Inject ScreenshotController( Context context, @@ -194,23 +208,20 @@ public class ScreenshotController { mUiEventLogger = uiEventLogger; final DisplayManager dm = requireNonNull(context.getSystemService(DisplayManager.class)); - mDisplay = dm.getDisplay(DEFAULT_DISPLAY); - mContext = context.createWindowContext(TYPE_SCREENSHOT, null); + final Display display = dm.getDisplay(DEFAULT_DISPLAY); + final Context displayContext = context.createDisplayContext(display); + mContext = displayContext.createWindowContext(TYPE_SCREENSHOT, null); mWindowManager = mContext.getSystemService(WindowManager.class); mAccessibilityManager = AccessibilityManager.getInstance(mContext); mConfigProxy = configProxy; - Configuration config = mContext.getResources().getConfiguration(); - mInDarkMode = config.isNightModeActive(); - mDirectionLTR = config.getLayoutDirection() == View.LAYOUT_DIRECTION_LTR; - mOrientationPortrait = config.orientation == ORIENTATION_PORTRAIT; mWindowToken = new Binder("ScreenshotController"); mScrollCaptureClient.setHostWindowToken(mWindowToken); // Setup the window that we are going to use - mWindowLayoutParams = new WindowManager.LayoutParams(MATCH_PARENT, MATCH_PARENT, 0, 0, - TYPE_SCREENSHOT, + mWindowLayoutParams = new WindowManager.LayoutParams( + MATCH_PARENT, MATCH_PARENT, /* xpos */ 0, /* ypos */ 0, TYPE_SCREENSHOT, WindowManager.LayoutParams.FLAG_FULLSCREEN | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL @@ -235,7 +246,7 @@ public class ScreenshotController { reloadAssets(); mDisplayMetrics = new DisplayMetrics(); - mDisplay.getRealMetrics(mDisplayMetrics); + display.getRealMetrics(mDisplayMetrics); // Setup the Camera shutter sound mCameraSound = new MediaActionSound(); @@ -245,7 +256,6 @@ public class ScreenshotController { void takeScreenshotFullscreen(Consumer<Uri> finisher, Runnable onComplete) { mOnCompleteRunnable = onComplete; - mDisplay.getRealMetrics(mDisplayMetrics); takeScreenshotInternal( finisher, new Rect(0, 0, mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels)); @@ -266,19 +276,18 @@ public class ScreenshotController { return; } - if (aspectRatiosMatch(screenshot, visibleInsets, screenshotScreenBounds)) { - saveScreenshot(screenshot, finisher, screenshotScreenBounds, visibleInsets, false); - } else { - saveScreenshot(screenshot, finisher, - new Rect(0, 0, screenshot.getWidth(), screenshot.getHeight()), Insets.NONE, - true); + boolean showFlash = false; + if (!aspectRatiosMatch(screenshot, visibleInsets, screenshotScreenBounds)) { + showFlash = true; + visibleInsets = Insets.NONE; + screenshotScreenBounds.set(0, 0, screenshot.getWidth(), screenshot.getHeight()); } + saveScreenshot(screenshot, finisher, screenshotScreenBounds, visibleInsets, showFlash); } /** * Displays a screenshot selector */ - @SuppressLint("ClickableViewAccessibility") void takeScreenshotPartial(final Consumer<Uri> finisher, Runnable onComplete) { dismissScreenshot(true); mOnCompleteRunnable = onComplete; @@ -293,13 +302,17 @@ public class ScreenshotController { * Clears current screenshot */ void dismissScreenshot(boolean immediate) { + if (DEBUG_DISMISS) { + Log.d(TAG, "dismissScreenshot(immediate=" + immediate + ")"); + } // If we're already animating out, don't restart the animation // (but do obey an immediate dismissal) if (!immediate && mScreenshotView.isDismissing()) { - Log.v(TAG, "Already dismissing, ignoring duplicate command"); + if (DEBUG_DISMISS) { + Log.v(TAG, "Already dismissing, ignoring duplicate command"); + } return; } - Log.v(TAG, "Clearing screenshot"); mScreenshotHandler.removeMessages(MESSAGE_CORNER_TIMEOUT); if (immediate) { resetScreenshotView(); @@ -308,73 +321,25 @@ public class ScreenshotController { } } - private void onConfigChanged(Configuration newConfig) { - boolean needsUpdate = false; - // dark mode - if (newConfig.isNightModeActive()) { - // Night mode is active, we're using dark theme - if (!mInDarkMode) { - mInDarkMode = true; - needsUpdate = true; - } - } else { - // Night mode is not active, we're using the light theme - if (mInDarkMode) { - mInDarkMode = false; - needsUpdate = true; - } - } - - // RTL configuration - switch (newConfig.getLayoutDirection()) { - case View.LAYOUT_DIRECTION_LTR: - if (!mDirectionLTR) { - mDirectionLTR = true; - needsUpdate = true; - } - break; - case View.LAYOUT_DIRECTION_RTL: - if (mDirectionLTR) { - mDirectionLTR = false; - needsUpdate = true; - } - break; - } - - // portrait/landscape orientation - switch (newConfig.orientation) { - case ORIENTATION_PORTRAIT: - if (!mOrientationPortrait) { - mOrientationPortrait = true; - needsUpdate = true; - } - break; - case ORIENTATION_LANDSCAPE: - if (mOrientationPortrait) { - mOrientationPortrait = false; - needsUpdate = true; - } - break; - } - - if (needsUpdate) { - reloadAssets(); - } - } - /** - * Update assets (called when the dark theme status changes). We only need to update the dismiss - * button and the actions container background, since the buttons are re-inflated on demand. + * Update resources on configuration change. Reinflate for theme/color changes. */ private void reloadAssets() { + if (DEBUG_UI) { + Log.d(TAG, "reloadAssets()"); + } boolean wasAttached = mDecorView.isAttachedToWindow(); if (wasAttached) { + if (DEBUG_WINDOW) { + Log.d(TAG, "Removing screenshot window"); + } mWindowManager.removeView(mDecorView); } // respect the display cutout in landscape (since we'd otherwise overlap) but not portrait + int orientation = mContext.getResources().getConfiguration().orientation; mWindowLayoutParams.setFitInsetsTypes( - mOrientationPortrait ? 0 : WindowInsets.Type.displayCutout()); + orientation == ORIENTATION_PORTRAIT ? 0 : WindowInsets.Type.displayCutout()); // ignore system bar insets for the purpose of window layout mDecorView.setOnApplyWindowInsetsListener((v, insets) -> v.onApplyWindowInsets( @@ -390,6 +355,9 @@ public class ScreenshotController { // TODO(159460485): Remove this when focus is handled properly in the system mScreenshotView.setOnTouchListener((v, event) -> { if (event.getActionMasked() == MotionEvent.ACTION_OUTSIDE) { + if (DEBUG_INPUT) { + Log.d(TAG, "onTouch: ACTION_OUTSIDE"); + } // Once the user touches outside, stop listening for input setWindowFocusable(false); } @@ -398,6 +366,9 @@ public class ScreenshotController { mScreenshotView.setOnKeyListener((v, keyCode, event) -> { if (keyCode == KeyEvent.KEYCODE_BACK) { + if (DEBUG_INPUT) { + Log.d(TAG, "onKeyEvent: KeyEvent.KEYCODE_BACK"); + } dismissScreenshot(false); return true; } @@ -427,10 +398,16 @@ public class ScreenshotController { Bitmap screenshot = screenshotBuffer == null ? null : screenshotBuffer.asBitmap(); if (screenshot == null) { - Log.e(TAG, "Screenshot bitmap was null"); + Log.e(TAG, "takeScreenshotInternal: Screenshot bitmap was null"); mNotificationsController.notifyScreenshotError( R.string.screenshot_failed_to_capture_text); + if (DEBUG_CALLBACK) { + Log.d(TAG, "Supplying null to Consumer<Uri>"); + } finisher.accept(null); + if (DEBUG_CALLBACK) { + Log.d(TAG, "Calling mOnCompleteRunnable.run()"); + } mOnCompleteRunnable.run(); return; } @@ -453,12 +430,17 @@ public class ScreenshotController { if (!mScreenshotView.isDismissing()) { mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_REENTERED); } + if (DEBUG_WINDOW) { + Log.d(TAG, "saveScreenshot: screenshotView is already attached, resetting. " + + "(dismissing=" + mScreenshotView.isDismissing() + ")"); + } mScreenshotView.reset(); } mScreenBitmap = screenshot; if (!isUserSetupComplete()) { + Log.w(TAG, "User setup not complete, displaying toast only"); // User setup isn't complete, so we don't want to show any UI beyond a toast, as editing // and sharing shouldn't be exposed to the user. saveScreenshotAndToast(finisher); @@ -469,7 +451,12 @@ public class ScreenshotController { mScreenBitmap.setHasAlpha(false); mScreenBitmap.prepareToDraw(); - onConfigChanged(mContext.getResources().getConfiguration()); + if (mConfigChanges.applyNewConfig(mContext.getResources())) { + if (DEBUG_UI) { + Log.d(TAG, "saveScreenshot: reloading assets"); + } + reloadAssets(); + } // The window is focusable by default setWindowFocusable(true); @@ -502,25 +489,21 @@ public class ScreenshotController { mCameraSound.play(MediaActionSound.SHUTTER_CLICK); }); - saveScreenshotInWorkerThread(finisher, - new ScreenshotController.ActionsReadyListener() { - @Override - void onActionsReady(ScreenshotController.SavedImageData imageData) { - finisher.accept(imageData.uri); - if (imageData.uri == null) { - mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_NOT_SAVED); - mNotificationsController.notifyScreenshotError( - R.string.screenshot_failed_to_save_text); - } else { - mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_SAVED); - - mScreenshotHandler.post(() -> { - Toast.makeText(mContext, R.string.screenshot_saved_title, - Toast.LENGTH_SHORT).show(); - }); - } - } - }); + saveScreenshotInWorkerThread(finisher, imageData -> { + if (DEBUG_CALLBACK) { + Log.d(TAG, "returning URI to finisher (Consumer<URI>): " + imageData.uri); + } + finisher.accept(imageData.uri); + if (imageData.uri == null) { + mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_NOT_SAVED); + mNotificationsController.notifyScreenshotError( + R.string.screenshot_failed_to_save_text); + } else { + mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_SAVED); + mScreenshotHandler.post(() -> Toast.makeText(mContext, + R.string.screenshot_saved_title, Toast.LENGTH_SHORT).show()); + } + }); } /** @@ -531,37 +514,46 @@ public class ScreenshotController { mScreenshotHandler.removeMessages(MESSAGE_CORNER_TIMEOUT); mScreenshotHandler.post(() -> { if (!mScreenshotView.isAttachedToWindow()) { + if (DEBUG_WINDOW) { + Log.d(TAG, "Adding screenshot window"); + } mWindowManager.addView(mWindow.getDecorView(), mWindowLayoutParams); } mScreenshotView.prepareForAnimation(mScreenBitmap, screenInsets); mScreenshotHandler.post(() -> { + if (DEBUG_WINDOW) { + Log.d(TAG, "adding OnComputeInternalInsetsListener"); + } mScreenshotView.getViewTreeObserver().addOnComputeInternalInsetsListener( mScreenshotView); mScreenshotAnimation = mScreenshotView.createScreenshotDropInAnimation(screenRect, showFlash); - saveScreenshotInWorkerThread(finisher, - new ScreenshotController.ActionsReadyListener() { - @Override - void onActionsReady( - ScreenshotController.SavedImageData imageData) { - showUiOnActionsReady(imageData); - } - }); + saveScreenshotInWorkerThread(finisher, this::showUiOnActionsReady); // Play the shutter sound to notify that we've taken a screenshot mCameraSound.play(MediaActionSound.SHUTTER_CLICK); + if (DEBUG_ANIM) { + Log.d(TAG, "starting post-screenshot animation"); + } mScreenshotAnimation.start(); }); }); } + /** Reset screenshot view and then call onCompleteRunnable */ private void resetScreenshotView() { + if (DEBUG_UI) { + Log.d(TAG, "resetScreenshotView"); + } if (mScreenshotView.isAttachedToWindow()) { + if (DEBUG_WINDOW) { + Log.d(TAG, "Removing screenshot window"); + } mWindowManager.removeView(mDecorView); } mScreenshotView.reset(); @@ -571,8 +563,7 @@ public class ScreenshotController { /** * Creates a new worker thread and saves the screenshot to the media store. */ - private void saveScreenshotInWorkerThread( - Consumer<Uri> finisher, + private void saveScreenshotInWorkerThread(Consumer<Uri> finisher, @Nullable ScreenshotController.ActionsReadyListener actionsReadyListener) { ScreenshotController.SaveImageInBackgroundData data = new ScreenshotController.SaveImageInBackgroundData(); @@ -582,13 +573,7 @@ public class ScreenshotController { if (mSaveInBgTask != null) { // just log success/failure for the pre-existing screenshot - mSaveInBgTask.setActionsReadyListener( - new ScreenshotController.ActionsReadyListener() { - @Override - void onActionsReady(ScreenshotController.SavedImageData imageData) { - logSuccessOnActionsReady(imageData); - } - }); + mSaveInBgTask.setActionsReadyListener(this::logSuccessOnActionsReady); } mSaveInBgTask = new SaveImageInBackgroundTask(mContext, mScreenshotSmartActions, data); @@ -607,6 +592,9 @@ public class ScreenshotController { SCREENSHOT_CORNER_DEFAULT_TIMEOUT_MILLIS, AccessibilityManager.FLAG_CONTENT_CONTROLS); + if (DEBUG_UI) { + Log.d(TAG, "Showing UI actions, dismiss timeout: " + timeoutMs + " ms"); + } mScreenshotHandler.sendMessageDelayed( mScreenshotHandler.obtainMessage(MESSAGE_CORNER_TIMEOUT), timeoutMs); @@ -652,6 +640,9 @@ public class ScreenshotController { * shown. */ private void setWindowFocusable(boolean focusable) { + if (DEBUG_WINDOW) { + Log.d(TAG, "setWindowFocusable: " + focusable); + } if (focusable) { mWindowLayoutParams.flags &= ~WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; } else { @@ -670,9 +661,10 @@ public class ScreenshotController { if (insettedHeight == 0 || insettedWidth == 0 || bitmap.getWidth() == 0 || bitmap.getHeight() == 0) { - Log.e(TAG, String.format( - "Provided bitmap and insets create degenerate region: %dx%d %s", - bitmap.getWidth(), bitmap.getHeight(), bitmapInsets)); + if (DEBUG_UI) { + Log.e(TAG, "Provided bitmap and insets create degenerate region: " + + bitmap.getWidth() + "x" + bitmap.getHeight() + " " + bitmapInsets); + } return false; } @@ -680,11 +672,10 @@ public class ScreenshotController { float boundsAspect = ((float) screenBounds.width()) / screenBounds.height(); boolean matchWithinTolerance = Math.abs(insettedBitmapAspect - boundsAspect) < 0.1f; - if (!matchWithinTolerance) { - Log.d(TAG, String.format("aspectRatiosMatch: don't match bitmap: %f, bounds: %f", - insettedBitmapAspect, boundsAspect)); + if (DEBUG_UI) { + Log.d(TAG, "aspectRatiosMatch: don't match bitmap: " + insettedBitmapAspect + + ", bounds: " + boundsAspect); } - return matchWithinTolerance; } } diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsProvider.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsProvider.java index 63f323ed2768..29f67f348f7b 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsProvider.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsProvider.java @@ -16,6 +16,9 @@ package com.android.systemui.screenshot; +import static com.android.systemui.screenshot.LogConfig.DEBUG_ACTIONS; +import static com.android.systemui.screenshot.LogConfig.logTag; + import android.app.Notification; import android.content.ComponentName; import android.graphics.Bitmap; @@ -32,6 +35,9 @@ import java.util.concurrent.CompletableFuture; * in order to provide smart actions in the screenshot notification. */ public class ScreenshotNotificationSmartActionsProvider { + + private static final String TAG = logTag(ScreenshotNotificationSmartActionsProvider.class); + /* Key provided in the notification action to get the type of smart action. */ public static final String ACTION_TYPE = "action_type"; public static final String DEFAULT_ACTION_TYPE = "Smart Action"; @@ -51,29 +57,21 @@ public class ScreenshotNotificationSmartActionsProvider { ERROR, TIMEOUT } - - private static final String TAG = "ScreenshotActions"; - /** * Default implementation that returns an empty list. * This method is overridden in vendor-specific Sys UI implementation. * - * @param screenshotId A generated random unique id for the screenshot. - * @param screenshotFileName name of the file where the screenshot will be written. - * @param bitmap The bitmap of the screenshot. The bitmap config must be {@link - * HARDWARE}. - * @param componentName Contains package and activity class names where the screenshot was - * taken. This is used as an additional signal to generate and rank - * more relevant actions. - * @param userHandle The user handle of the app where the screenshot was taken. + * @param screenshotId a unique id for the screenshot + * @param screenshotUri uri where the screenshot has been stored + * @param bitmap the screenshot, config must be {@link Bitmap.Config#HARDWARE} + * @param componentName name of the foreground component when the screenshot was taken + * @param userHandle user handle of the foreground task owner */ - public CompletableFuture<List<Notification.Action>> getActions( - String screenshotId, - Uri screenshotUri, - Bitmap bitmap, - ComponentName componentName, - UserHandle userHandle) { - Log.d(TAG, "Returning empty smart action list."); + public CompletableFuture<List<Notification.Action>> getActions(String screenshotId, + Uri screenshotUri, Bitmap bitmap, ComponentName componentName, UserHandle userHandle) { + if (DEBUG_ACTIONS) { + Log.d(TAG, "Returning empty smart action list."); + } return CompletableFuture.completedFuture(Collections.emptyList()); } @@ -88,7 +86,9 @@ public class ScreenshotNotificationSmartActionsProvider { */ public void notifyOp(String screenshotId, ScreenshotOp op, ScreenshotOpStatus status, long durationMs) { - Log.d(TAG, "Return without notify."); + if (DEBUG_ACTIONS) { + Log.d(TAG, "SmartActions: notifyOp() - return without notify"); + } } /** @@ -100,6 +100,8 @@ public class ScreenshotNotificationSmartActionsProvider { * @param isSmartAction whether action invoked was a smart action. */ public void notifyAction(String screenshotId, String action, boolean isSmartAction) { - Log.d(TAG, "Return without notify."); + if (DEBUG_ACTIONS) { + Log.d(TAG, "SmartActions: notifyAction: return without notify"); + } } } diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSmartActions.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSmartActions.java index 468602a5369e..1184dc7fe1a4 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSmartActions.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSmartActions.java @@ -18,6 +18,9 @@ package com.android.systemui.screenshot; import static android.os.AsyncTask.THREAD_POOL_EXECUTOR; +import static com.android.systemui.screenshot.LogConfig.DEBUG_ACTIONS; +import static com.android.systemui.screenshot.LogConfig.logTag; + import android.app.ActivityManager; import android.app.Notification; import android.content.ComponentName; @@ -27,7 +30,7 @@ import android.net.Uri; import android.os.Handler; import android.os.SystemClock; import android.os.UserHandle; -import android.util.Slog; +import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.SystemUIFactory; @@ -47,7 +50,7 @@ import javax.inject.Inject; */ @SysUISingleton public class ScreenshotSmartActions { - private static final String TAG = "ScreenshotSmartActions"; + private static final String TAG = logTag(ScreenshotSmartActions.class); @Inject public ScreenshotSmartActions() {} @@ -57,18 +60,24 @@ public class ScreenshotSmartActions { String screenshotId, Uri screenshotUri, Bitmap image, ScreenshotNotificationSmartActionsProvider smartActionsProvider, boolean smartActionsEnabled, UserHandle userHandle) { + if (DEBUG_ACTIONS) { + Log.d(TAG, String.format("getSmartActionsFuture id=%s, uri=%s, provider=%s, " + + "smartActionsEnabled=%b, userHandle=%s", screenshotId, screenshotUri, + smartActionsProvider.getClass(), smartActionsEnabled, userHandle)); + } if (!smartActionsEnabled) { - Slog.i(TAG, "Screenshot Intelligence not enabled, returning empty list."); + if (DEBUG_ACTIONS) { + Log.d(TAG, "Screenshot Intelligence not enabled, returning empty list."); + } return CompletableFuture.completedFuture(Collections.emptyList()); } if (image.getConfig() != Bitmap.Config.HARDWARE) { - Slog.w(TAG, String.format( - "Bitmap expected: Hardware, Bitmap found: %s. Returning empty list.", - image.getConfig())); + if (DEBUG_ACTIONS) { + Log.d(TAG, String.format("Bitmap expected: Hardware, Bitmap found: %s. " + + "Returning empty list.", image.getConfig())); + } return CompletableFuture.completedFuture(Collections.emptyList()); } - - Slog.d(TAG, "Screenshot from user profile: " + userHandle.getIdentifier()); CompletableFuture<List<Notification.Action>> smartActionsFuture; long startTimeMs = SystemClock.uptimeMillis(); try { @@ -83,7 +92,9 @@ public class ScreenshotSmartActions { } catch (Throwable e) { long waitTimeMs = SystemClock.uptimeMillis() - startTimeMs; smartActionsFuture = CompletableFuture.completedFuture(Collections.emptyList()); - Slog.e(TAG, "Failed to get future for screenshot notification smart actions.", e); + if (DEBUG_ACTIONS) { + Log.e(TAG, "Failed to get future for screenshot notification smart actions.", e); + } notifyScreenshotOp(screenshotId, smartActionsProvider, ScreenshotNotificationSmartActionsProvider.ScreenshotOp.REQUEST_SMART_ACTIONS, ScreenshotNotificationSmartActionsProvider.ScreenshotOpStatus.ERROR, @@ -97,12 +108,18 @@ public class ScreenshotSmartActions { CompletableFuture<List<Notification.Action>> smartActionsFuture, int timeoutMs, ScreenshotNotificationSmartActionsProvider smartActionsProvider) { long startTimeMs = SystemClock.uptimeMillis(); + if (DEBUG_ACTIONS) { + Log.d(TAG, String.format("getSmartActions id=%s, timeoutMs=%d, provider=%s", + screenshotId, timeoutMs, smartActionsProvider.getClass())); + } try { List<Notification.Action> actions = smartActionsFuture.get(timeoutMs, TimeUnit.MILLISECONDS); long waitTimeMs = SystemClock.uptimeMillis() - startTimeMs; - Slog.d(TAG, String.format("Got %d smart actions. Wait time: %d ms", - actions.size(), waitTimeMs)); + if (DEBUG_ACTIONS) { + Log.d(TAG, String.format("Got %d smart actions. Wait time: %d ms", + actions.size(), waitTimeMs)); + } notifyScreenshotOp(screenshotId, smartActionsProvider, ScreenshotNotificationSmartActionsProvider.ScreenshotOp.WAIT_FOR_SMART_ACTIONS, ScreenshotNotificationSmartActionsProvider.ScreenshotOpStatus.SUCCESS, @@ -110,8 +127,10 @@ public class ScreenshotSmartActions { return actions; } catch (Throwable e) { long waitTimeMs = SystemClock.uptimeMillis() - startTimeMs; - Slog.e(TAG, String.format("Error getting smart actions. Wait time: %d ms", waitTimeMs), - e); + if (DEBUG_ACTIONS) { + Log.e(TAG, String.format("Error getting smart actions. Wait time: %d ms", + waitTimeMs), e); + } ScreenshotNotificationSmartActionsProvider.ScreenshotOpStatus status = (e instanceof TimeoutException) ? ScreenshotNotificationSmartActionsProvider.ScreenshotOpStatus.TIMEOUT @@ -127,10 +146,14 @@ public class ScreenshotSmartActions { ScreenshotNotificationSmartActionsProvider smartActionsProvider, ScreenshotNotificationSmartActionsProvider.ScreenshotOp op, ScreenshotNotificationSmartActionsProvider.ScreenshotOpStatus status, long durationMs) { + if (DEBUG_ACTIONS) { + Log.d(TAG, String.format("%s notifyOp: %s id=%s, status=%s, durationMs=%d", + smartActionsProvider.getClass(), op, screenshotId, status, durationMs)); + } try { smartActionsProvider.notifyOp(screenshotId, op, status, durationMs); } catch (Throwable e) { - Slog.e(TAG, "Error in notifyScreenshotOp: ", e); + Log.e(TAG, "Error in notifyScreenshotOp: ", e); } } @@ -140,9 +163,13 @@ public class ScreenshotSmartActions { ScreenshotNotificationSmartActionsProvider provider = SystemUIFactory.getInstance().createScreenshotNotificationSmartActionsProvider( context, THREAD_POOL_EXECUTOR, new Handler()); + if (DEBUG_ACTIONS) { + Log.e(TAG, String.format("%s notifyAction: %s id=%s, isSmartAction=%b", + provider.getClass(), action, screenshotId, isSmartAction)); + } provider.notifyAction(screenshotId, action, isSmartAction); } catch (Throwable e) { - Slog.e(TAG, "Error in notifyScreenshotAction: ", e); + Log.e(TAG, "Error in notifyScreenshotAction: ", e); } } } diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java index 3814bd2999e9..cce60f9c7c9d 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java @@ -18,6 +18,14 @@ package com.android.systemui.screenshot; import static android.content.res.Configuration.ORIENTATION_PORTRAIT; +import static com.android.systemui.screenshot.LogConfig.DEBUG_ANIM; +import static com.android.systemui.screenshot.LogConfig.DEBUG_DISMISS; +import static com.android.systemui.screenshot.LogConfig.DEBUG_INPUT; +import static com.android.systemui.screenshot.LogConfig.DEBUG_SCROLL; +import static com.android.systemui.screenshot.LogConfig.DEBUG_UI; +import static com.android.systemui.screenshot.LogConfig.DEBUG_WINDOW; +import static com.android.systemui.screenshot.LogConfig.logTag; + import static java.util.Objects.requireNonNull; import android.animation.Animator; @@ -75,7 +83,7 @@ import java.util.function.Consumer; public class ScreenshotView extends FrameLayout implements ViewTreeObserver.OnComputeInternalInsetsListener { - private static final String TAG = "ScreenshotView"; + private static final String TAG = logTag(ScreenshotView.class); private static final long SCREENSHOT_FLASH_IN_DURATION_MS = 133; private static final long SCREENSHOT_FLASH_OUT_DURATION_MS = 217; @@ -166,12 +174,18 @@ public class ScreenshotView extends FrameLayout implements * @param onClick the action to take when the chip is clicked. */ public void showScrollChip(Runnable onClick) { + if (DEBUG_SCROLL) { + Log.d(TAG, "Showing Scroll option"); + } mScrollChip.setVisibility(VISIBLE); - mScrollChip.setOnClickListener((v) -> - onClick.run() - // TODO Logging, store event consumer to a field - //onElementTapped.accept(ScreenshotEvent.SCREENSHOT_SCROLL_TAPPED); - ); + mScrollChip.setOnClickListener((v) -> { + if (DEBUG_INPUT) { + Log.d(TAG, "scroll chip tapped"); + } + onClick.run(); + // TODO Logging, store event consumer to a field + //onElementTapped.accept(ScreenshotEvent.SCREENSHOT_SCROLL_TAPPED); + }); } @Override // ViewTreeObserver.OnComputeInternalInsetsListener @@ -179,15 +193,13 @@ public class ScreenshotView extends FrameLayout implements inoutInfo.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION); Region touchRegion = new Region(); - Rect screenshotRect = new Rect(); - mScreenshotPreview.getBoundsOnScreen(screenshotRect); - touchRegion.op(screenshotRect, Region.Op.UNION); - Rect actionsRect = new Rect(); - mActionsContainer.getBoundsOnScreen(actionsRect); - touchRegion.op(actionsRect, Region.Op.UNION); - Rect dismissRect = new Rect(); - mDismissButton.getBoundsOnScreen(dismissRect); - touchRegion.op(dismissRect, Region.Op.UNION); + final Rect tmpRect = new Rect(); + mScreenshotPreview.getBoundsOnScreen(tmpRect); + touchRegion.op(tmpRect, Region.Op.UNION); + mActionsContainer.getBoundsOnScreen(tmpRect); + touchRegion.op(tmpRect, Region.Op.UNION); + mDismissButton.getBoundsOnScreen(tmpRect); + touchRegion.op(tmpRect, Region.Op.UNION); if (QuickStepContract.isGesturalMode(mNavMode)) { // Receive touches in gesture insets such that they don't cause TOUCH_OUTSIDE @@ -362,7 +374,6 @@ public class ScreenshotView extends FrameLayout implements toCorner.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationStart(Animator animation) { - super.onAnimationStart(animation); mScreenshotPreview.setVisibility(View.VISIBLE); } }); @@ -380,8 +391,13 @@ public class ScreenshotView extends FrameLayout implements dropInAnimation.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { - super.onAnimationEnd(animation); + if (DEBUG_ANIM) { + Log.d(TAG, "drop-in animation completed"); + } mDismissButton.setOnClickListener(view -> { + if (DEBUG_INPUT) { + Log.d(TAG, "dismiss button clicked"); + } mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_EXPLICIT_DISMISSAL); animateDismissal(); }); @@ -497,7 +513,7 @@ public class ScreenshotView extends FrameLayout implements try { imageData.editAction.actionIntent.send(); } catch (PendingIntent.CanceledException e) { - Log.e(TAG, "Intent cancelled", e); + Log.e(TAG, "PendingIntent was cancelled", e); } mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_PREVIEW_TAPPED); animateDismissal(); @@ -543,6 +559,9 @@ public class ScreenshotView extends FrameLayout implements } private void animateDismissal(Animator dismissAnimation) { + if (DEBUG_WINDOW) { + Log.d(TAG, "removing OnComputeInternalInsetsListener"); + } getViewTreeObserver().removeOnComputeInternalInsetsListener(this); mDismissAnimation = dismissAnimation; mDismissAnimation.addListener(new AnimatorListenerAdapter() { @@ -551,6 +570,9 @@ public class ScreenshotView extends FrameLayout implements @Override public void onAnimationCancel(Animator animation) { super.onAnimationCancel(animation); + if (DEBUG_ANIM) { + Log.d(TAG, "Cancelled dismiss animation"); + } mCancelled = true; } @@ -558,17 +580,33 @@ public class ScreenshotView extends FrameLayout implements public void onAnimationEnd(Animator animation) { super.onAnimationEnd(animation); if (!mCancelled) { + if (DEBUG_ANIM) { + Log.d(TAG, "after dismiss animation, calling onDismissRunnable.run()"); + } mOnDismissRunnable.run(); } } }); + if (DEBUG_ANIM) { + Log.d(TAG, "Starting dismiss animation"); + } mDismissAnimation.start(); } void reset() { + if (DEBUG_UI) { + Log.d(TAG, "reset screenshot view"); + } + if (mDismissAnimation != null && mDismissAnimation.isRunning()) { + if (DEBUG_ANIM) { + Log.d(TAG, "cancelling dismiss animation"); + } mDismissAnimation.cancel(); } + if (DEBUG_WINDOW) { + Log.d(TAG, "removing OnComputeInternalInsetsListener"); + } // Make sure we clean up the view tree observer getViewTreeObserver().removeOnComputeInternalInsetsListener(this); // Clear any references to the bitmap @@ -637,10 +675,9 @@ public class ScreenshotView extends FrameLayout implements BitmapDrawable bitmapDrawable = new BitmapDrawable(res, bitmap); if (insettedHeight == 0 || insettedWidth == 0 || bitmap.getWidth() == 0 || bitmap.getHeight() == 0) { - Log.e(TAG, String.format( - "Can't create insetted drawable, using 0 insets " - + "bitmap and insets create degenerate region: %dx%d %s", - bitmap.getWidth(), bitmap.getHeight(), insets)); + Log.e(TAG, "Can't create inset drawable, using 0 insets bitmap and insets create " + + "degenerate region: " + bitmap.getWidth() + "x" + bitmap.getHeight() + " " + + bitmapDrawable); return bitmapDrawable; } @@ -689,12 +726,18 @@ public class ScreenshotView extends FrameLayout implements } else if (event.getActionMasked() == MotionEvent.ACTION_UP) { if (isPastDismissThreshold() && (mDismissAnimation == null || !mDismissAnimation.isRunning())) { + if (DEBUG_INPUT) { + Log.d(TAG, "dismiss triggered via swipe gesture"); + } mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_SWIPE_DISMISSED); animateDismissal(createSwipeDismissAnimation()); return true; } else if (MathUtils.dist(mStartX, mStartY, event.getRawX(), event.getRawY()) > dpToPx(CLICK_MOVEMENT_THRESHOLD_DP)) { // if we've moved a non-negligible distance (but not past the threshold), + if (DEBUG_DISMISS) { + Log.d(TAG, "swipe gesture abandoned"); + } // start the return animation if ((mDismissAnimation == null || !mDismissAnimation.isRunning())) { createSwipeReturnAnimation().start(); diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureClient.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureClient.java index ea835fa94fe8..e159992bc9a5 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureClient.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureClient.java @@ -16,6 +16,8 @@ package com.android.systemui.screenshot; +import static com.android.systemui.screenshot.LogConfig.DEBUG_SCROLL; + import static java.util.Objects.requireNonNull; import android.annotation.UiContext; @@ -49,10 +51,7 @@ public class ScrollCaptureClient { @VisibleForTesting static final int MATCH_ANY_TASK = ActivityTaskManager.INVALID_TASK_ID; - private static final String TAG = "ScrollCaptureClient"; - - /** Whether to log method names and arguments for most calls */ - private static final boolean DEBUG_TRACE = false; + private static final String TAG = LogConfig.logTag(ScrollCaptureClient.class); /** * A connection to a remote window. Starts a capture session. @@ -155,7 +154,7 @@ public class ScrollCaptureClient { */ public void request(int displayId, int taskId, Consumer<Connection> consumer) { try { - if (DEBUG_TRACE) { + if (DEBUG_SCROLL) { Log.d(TAG, "requestScrollCapture(displayId=" + displayId + ", " + mHostWindowToken + ", taskId=" + taskId + ", consumer=" + consumer + ")"); } @@ -189,7 +188,7 @@ public class ScrollCaptureClient { @Override public void onConnected(IScrollCaptureConnection connection, Rect scrollBounds, Point positionInWindow) throws RemoteException { - if (DEBUG_TRACE) { + if (DEBUG_SCROLL) { Log.d(TAG, "onConnected(connection=" + connection + ", scrollBounds=" + scrollBounds + ", positionInWindow=" + positionInWindow + ")"); } @@ -202,7 +201,7 @@ public class ScrollCaptureClient { @Override public void onUnavailable() throws RemoteException { - if (DEBUG_TRACE) { + if (DEBUG_SCROLL) { Log.d(TAG, "onUnavailable"); } // The targeted app does not support scroll capture @@ -211,7 +210,7 @@ public class ScrollCaptureClient { @Override public void onCaptureStarted() { - if (DEBUG_TRACE) { + if (DEBUG_SCROLL) { Log.d(TAG, "onCaptureStarted()"); } mSessionConsumer.accept(this); @@ -224,7 +223,7 @@ public class ScrollCaptureClient { if (frameNumber != ScrollCaptureViewSupport.NO_FRAME_PRODUCED) { image = mReader.acquireNextImage(); } - if (DEBUG_TRACE) { + if (DEBUG_SCROLL) { Log.d(TAG, "onCaptureBufferSent(frameNumber=" + frameNumber + ", contentArea=" + contentArea + ") image=" + image); } @@ -237,7 +236,7 @@ public class ScrollCaptureClient { @Override public void onConnectionClosed() { - if (DEBUG_TRACE) { + if (DEBUG_SCROLL) { Log.d(TAG, "onConnectionClosed()"); } disconnect(); @@ -261,7 +260,7 @@ public class ScrollCaptureClient { // -> Error handling: BiConsumer<Session, Throwable> ? @Override public void start(int maxBufferCount, Consumer<Session> sessionConsumer) { - if (DEBUG_TRACE) { + if (DEBUG_SCROLL) { Log.d(TAG, "start(maxBufferCount=" + maxBufferCount + ", sessionConsumer=" + sessionConsumer + ")"); } @@ -288,7 +287,7 @@ public class ScrollCaptureClient { @Override public void end(Runnable listener) { - if (DEBUG_TRACE) { + if (DEBUG_SCROLL) { Log.d(TAG, "end(listener=" + listener + ")"); } if (mStarted) { @@ -319,7 +318,7 @@ public class ScrollCaptureClient { @Override public void requestTile(Rect contentRect, Consumer<CaptureResult> consumer) { - if (DEBUG_TRACE) { + if (DEBUG_SCROLL) { Log.d(TAG, "requestTile(contentRect=" + contentRect + "consumer=" + consumer + ")"); } mRequestRect = new Rect(contentRect); @@ -337,7 +336,7 @@ public class ScrollCaptureClient { */ @Override public void binderDied() { - if (DEBUG_TRACE) { + if (DEBUG_SCROLL) { Log.d(TAG, "binderDied()"); } disconnect(); diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/SmartActionsReceiver.java b/packages/SystemUI/src/com/android/systemui/screenshot/SmartActionsReceiver.java index f32529fdaf04..3ad922b57c7c 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/SmartActionsReceiver.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/SmartActionsReceiver.java @@ -16,6 +16,7 @@ package com.android.systemui.screenshot; +import static com.android.systemui.screenshot.LogConfig.DEBUG_ACTIONS; import static com.android.systemui.screenshot.ScreenshotController.EXTRA_ACTION_INTENT; import static com.android.systemui.screenshot.ScreenshotController.EXTRA_ACTION_TYPE; import static com.android.systemui.screenshot.ScreenshotController.EXTRA_ID; @@ -26,7 +27,6 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.util.Log; -import android.util.Slog; import javax.inject.Inject; @@ -48,7 +48,9 @@ public class SmartActionsReceiver extends BroadcastReceiver { public void onReceive(Context context, Intent intent) { PendingIntent pendingIntent = intent.getParcelableExtra(EXTRA_ACTION_INTENT); String actionType = intent.getStringExtra(EXTRA_ACTION_TYPE); - Slog.d(TAG, "Executing smart action [" + actionType + "]:" + pendingIntent.getIntent()); + if (DEBUG_ACTIONS) { + Log.d(TAG, "Executing smart action [" + actionType + "]:" + pendingIntent.getIntent()); + } ActivityOptions opts = ActivityOptions.makeBasic(); try { diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java index 2c82bcb24ad0..c2b20d37f3f3 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java @@ -20,6 +20,10 @@ import static android.content.Intent.ACTION_CLOSE_SYSTEM_DIALOGS; import static com.android.internal.util.ScreenshotHelper.SCREENSHOT_MSG_PROCESS_COMPLETE; import static com.android.internal.util.ScreenshotHelper.SCREENSHOT_MSG_URI; +import static com.android.systemui.screenshot.LogConfig.DEBUG_CALLBACK; +import static com.android.systemui.screenshot.LogConfig.DEBUG_DISMISS; +import static com.android.systemui.screenshot.LogConfig.DEBUG_SERVICE; +import static com.android.systemui.screenshot.LogConfig.logTag; import android.app.Service; import android.content.BroadcastReceiver; @@ -41,6 +45,8 @@ import android.os.UserManager; import android.util.Log; import android.view.WindowManager; +import androidx.annotation.NonNull; + import com.android.internal.logging.UiEventLogger; import com.android.internal.util.ScreenshotHelper; import com.android.systemui.R; @@ -51,89 +57,34 @@ import java.util.function.Consumer; import javax.inject.Inject; public class TakeScreenshotService extends Service { - private static final String TAG = "TakeScreenshotService"; + private static final String TAG = logTag(TakeScreenshotService.class); private final ScreenshotController mScreenshot; private final UserManager mUserManager; private final UiEventLogger mUiEventLogger; private final ScreenshotNotificationsController mNotificationsController; + private final Handler mHandler; - private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { - + private final BroadcastReceiver mCloseSystemDialogs = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if (ACTION_CLOSE_SYSTEM_DIALOGS.equals(intent.getAction()) && mScreenshot != null) { - mScreenshot.dismissScreenshot(false); - } - } - }; - - private Handler mHandler = new Handler(Looper.myLooper()) { - @Override - public void handleMessage(Message msg) { - final Messenger callback = msg.replyTo; - Consumer<Uri> uriConsumer = uri -> { - Message reply = Message.obtain(null, SCREENSHOT_MSG_URI, uri); - try { - callback.send(reply); - } catch (RemoteException e) { - } - }; - Runnable onComplete = () -> { - Message reply = Message.obtain(null, SCREENSHOT_MSG_PROCESS_COMPLETE); - try { - callback.send(reply); - } catch (RemoteException e) { + if (DEBUG_DISMISS) { + Log.d(TAG, "Received ACTION_CLOSE_SYSTEM_DIALOGS"); } - }; - - // If the storage for this user is locked, we have no place to store - // the screenshot, so skip taking it instead of showing a misleading - // animation and error notification. - if (!mUserManager.isUserUnlocked()) { - Log.w(TAG, "Skipping screenshot because storage is locked!"); - mNotificationsController.notifyScreenshotError( - R.string.screenshot_failed_to_save_user_locked_text); - post(() -> uriConsumer.accept(null)); - post(onComplete); - return; - } - - ScreenshotHelper.ScreenshotRequest screenshotRequest = - (ScreenshotHelper.ScreenshotRequest) msg.obj; - - mUiEventLogger.log(ScreenshotEvent.getScreenshotSource(screenshotRequest.getSource())); - - switch (msg.what) { - case WindowManager.TAKE_SCREENSHOT_FULLSCREEN: - mScreenshot.takeScreenshotFullscreen(uriConsumer, onComplete); - break; - case WindowManager.TAKE_SCREENSHOT_SELECTED_REGION: - mScreenshot.takeScreenshotPartial(uriConsumer, onComplete); - break; - case WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE: - Bitmap screenshot = BitmapUtil.bundleToHardwareBitmap( - screenshotRequest.getBitmapBundle()); - Rect screenBounds = screenshotRequest.getBoundsInScreen(); - Insets insets = screenshotRequest.getInsets(); - int taskId = screenshotRequest.getTaskId(); - int userId = screenshotRequest.getUserId(); - ComponentName topComponent = screenshotRequest.getTopComponent(); - mScreenshot.handleImageAsScreenshot(screenshot, screenBounds, insets, - taskId, userId, topComponent, uriConsumer, onComplete); - break; - default: - Log.d(TAG, "Invalid screenshot option: " + msg.what); + mScreenshot.dismissScreenshot(false); } } }; @Inject - public TakeScreenshotService( - ScreenshotController screenshotController, - UserManager userManager, + public TakeScreenshotService(ScreenshotController screenshotController, UserManager userManager, UiEventLogger uiEventLogger, ScreenshotNotificationsController notificationsController) { + if (DEBUG_SERVICE) { + Log.d(TAG, "new " + this); + } + mHandler = new Handler(Looper.getMainLooper(), this::handleMessage); mScreenshot = screenshotController; mUserManager = userManager; mUiEventLogger = uiEventLogger; @@ -141,21 +92,124 @@ public class TakeScreenshotService extends Service { } @Override - public IBinder onBind(Intent intent) { - // register broadcast receiver - IntentFilter filter = new IntentFilter(ACTION_CLOSE_SYSTEM_DIALOGS); - registerReceiver(mBroadcastReceiver, filter); - - return new Messenger(mHandler).getBinder(); + public void onCreate() { + if (DEBUG_SERVICE) { + Log.d(TAG, "onCreate()"); + } + } + @Override + public IBinder onBind(@NonNull Intent intent) { + registerReceiver(mCloseSystemDialogs, new IntentFilter(ACTION_CLOSE_SYSTEM_DIALOGS)); + final Messenger m = new Messenger(mHandler); + if (DEBUG_SERVICE) { + Log.d(TAG, "onBind: returning connection: " + m); + } + return m.getBinder(); } @Override public boolean onUnbind(Intent intent) { + if (DEBUG_SERVICE) { + Log.d(TAG, "onUnbind"); + } if (mScreenshot != null) { mScreenshot.dismissScreenshot(true); } - unregisterReceiver(mBroadcastReceiver); + unregisterReceiver(mCloseSystemDialogs); + return false; + } + + @Override + public void onDestroy() { + super.onDestroy(); + if (DEBUG_SERVICE) { + Log.d(TAG, "onDestroy"); + } + } + + /** Respond to incoming Message via Binder (Messenger) */ + private boolean handleMessage(Message msg) { + final Messenger replyTo = msg.replyTo; + final Runnable onComplete = () -> sendComplete(replyTo); + final Consumer<Uri> uriConsumer = (uri) -> reportUri(replyTo, uri); + + // If the storage for this user is locked, we have no place to store + // the screenshot, so skip taking it instead of showing a misleading + // animation and error notification. + if (!mUserManager.isUserUnlocked()) { + Log.w(TAG, "Skipping screenshot because storage is locked!"); + mNotificationsController.notifyScreenshotError( + R.string.screenshot_failed_to_save_user_locked_text); + if (DEBUG_CALLBACK) { + Log.d(TAG, "handleMessage: calling uriConsumer.accept(null)"); + } + uriConsumer.accept(null); + if (DEBUG_CALLBACK) { + Log.d(TAG, "handleMessage: calling onComplete.run()"); + } + onComplete.run(); + return true; + } + + ScreenshotHelper.ScreenshotRequest screenshotRequest = + (ScreenshotHelper.ScreenshotRequest) msg.obj; + + mUiEventLogger.log(ScreenshotEvent.getScreenshotSource(screenshotRequest.getSource())); + + switch (msg.what) { + case WindowManager.TAKE_SCREENSHOT_FULLSCREEN: + if (DEBUG_SERVICE) { + Log.d(TAG, "handleMessage: TAKE_SCREENSHOT_FULLSCREEN"); + } + mScreenshot.takeScreenshotFullscreen(uriConsumer, onComplete); + break; + case WindowManager.TAKE_SCREENSHOT_SELECTED_REGION: + if (DEBUG_SERVICE) { + Log.d(TAG, "handleMessage: TAKE_SCREENSHOT_SELECTED_REGION"); + } + mScreenshot.takeScreenshotPartial(uriConsumer, onComplete); + break; + case WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE: + if (DEBUG_SERVICE) { + Log.d(TAG, "handleMessage: TAKE_SCREENSHOT_PROVIDED_IMAGE"); + } + Bitmap screenshot = BitmapUtil.bundleToHardwareBitmap( + screenshotRequest.getBitmapBundle()); + Rect screenBounds = screenshotRequest.getBoundsInScreen(); + Insets insets = screenshotRequest.getInsets(); + int taskId = screenshotRequest.getTaskId(); + int userId = screenshotRequest.getUserId(); + ComponentName topComponent = screenshotRequest.getTopComponent(); + mScreenshot.handleImageAsScreenshot(screenshot, screenBounds, insets, + taskId, userId, topComponent, uriConsumer, onComplete); + break; + default: + Log.w(TAG, "Invalid screenshot option: " + msg.what); + return false; + } return true; + }; + + private void sendComplete(Messenger target) { + try { + if (DEBUG_CALLBACK) { + Log.d(TAG, "sendComplete: " + target); + } + target.send(Message.obtain(null, SCREENSHOT_MSG_PROCESS_COMPLETE)); + } catch (RemoteException e) { + Log.d(TAG, "ignored remote exception", e); + } + } + + private void reportUri(Messenger target, Uri uri) { + try { + if (DEBUG_CALLBACK) { + Log.d(TAG, "reportUri: " + target + " -> " + uri); + } + target.send(Message.obtain(null, SCREENSHOT_MSG_URI, uri)); + } catch (RemoteException e) { + Log.d(TAG, "ignored remote exception", e); + } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java index a252a7a12274..3765e5a26e8e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java @@ -69,7 +69,6 @@ import com.android.systemui.util.wakelock.WakeLock; import java.io.FileDescriptor; import java.io.PrintWriter; import java.text.NumberFormat; -import java.util.IllegalFormatConversionException; import javax.inject.Inject; @@ -575,24 +574,12 @@ public class KeyguardIndicationController implements StateListener, String percentage = NumberFormat.getPercentInstance() .format(mBatteryLevel / 100f); if (hasChargingTime) { - // We now have battery percentage in these strings and it's expected that all - // locales will also have it in the future. For now, we still have to support the old - // format until all languages get the new translations. String chargingTimeFormatted = Formatter.formatShortElapsedTimeRoundingUpToMinutes( mContext, mChargingTimeRemaining); - try { - return mContext.getResources().getString(chargingId, chargingTimeFormatted, - percentage); - } catch (IllegalFormatConversionException e) { - return mContext.getResources().getString(chargingId, chargingTimeFormatted); - } + return mContext.getResources().getString(chargingId, chargingTimeFormatted, + percentage); } else { - // Same as above - try { - return mContext.getResources().getString(chargingId, percentage); - } catch (IllegalFormatConversionException e) { - return mContext.getResources().getString(chargingId); - } + return mContext.getResources().getString(chargingId, percentage); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/MediaNotificationProcessor.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/MediaNotificationProcessor.java index db5458664023..2586e9403e01 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/MediaNotificationProcessor.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/MediaNotificationProcessor.java @@ -29,7 +29,7 @@ import androidx.annotation.VisibleForTesting; import androidx.palette.graphics.Palette; import com.android.internal.util.ContrastColorUtil; -import com.android.systemui.R; +import com.android.settingslib.Utils; import java.util.List; @@ -143,7 +143,8 @@ public class MediaNotificationProcessor { int foregroundColor = selectForegroundColor(backgroundColor, palette); builder.setColorPalette(backgroundColor, foregroundColor); } else { - backgroundColor = mContext.getColor(R.color.notification_material_background_color); + backgroundColor = Utils.getColorAttr(mContext, android.R.attr.colorBackground) + .getDefaultColor(); } Bitmap colorized = mColorizer.colorize(drawable, backgroundColor, mContext.getResources().getConfiguration().getLayoutDirection() == diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java index 10273cbbebad..86ebc6b2c4ce 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java @@ -34,6 +34,7 @@ import android.view.animation.Interpolator; import android.view.animation.PathInterpolator; import com.android.internal.jank.InteractionJankMonitor; +import com.android.settingslib.Utils; import com.android.systemui.Gefingerpoken; import com.android.systemui.Interpolators; import com.android.systemui.R; @@ -169,13 +170,14 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView } private void updateColors() { - mNormalColor = mContext.getColor(R.color.notification_material_background_color); + mNormalColor = Utils.getColorAttr(mContext, android.R.attr.colorBackground) + .getDefaultColor(); mTintedRippleColor = mContext.getColor( R.color.notification_ripple_tinted_color); mNormalRippleColor = mContext.getColor( R.color.notification_ripple_untinted_color); mDimmedAlpha = Color.alpha(mContext.getColor( - R.color.notification_material_background_dimmed_color)); + R.color.notification_background_dimmed_color)); } private void initDimens() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGuts.java index 7071b73c2ebf..8ac5b933e4f9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGuts.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGuts.java @@ -19,7 +19,6 @@ package com.android.systemui.statusbar.notification.row; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.content.Context; -import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.drawable.Drawable; import android.os.Bundle; @@ -35,7 +34,6 @@ import android.widget.FrameLayout; import androidx.annotation.Nullable; import com.android.internal.annotations.VisibleForTesting; -import com.android.systemui.Dependency; import com.android.systemui.Interpolators; import com.android.systemui.R; import com.android.systemui.statusbar.notification.stack.StackStateAnimator; @@ -168,9 +166,6 @@ public class NotificationGuts extends FrameLayout { } } }; - final TypedArray ta = context.obtainStyledAttributes(attrs, - com.android.internal.R.styleable.Theme, 0, 0); - ta.recycle(); } public NotificationGuts(Context context) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java index 6920e3f4a7c6..416c5af93400 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java @@ -38,6 +38,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.graphics.ColorUtils; import com.android.internal.util.ContrastColorUtil; import com.android.internal.widget.CachingIconView; +import com.android.settingslib.Utils; import com.android.systemui.statusbar.CrossFadeHelper; import com.android.systemui.statusbar.TransformableView; import com.android.systemui.statusbar.notification.TransformState; @@ -324,8 +325,8 @@ public abstract class NotificationViewWrapper implements TransformableView { if (customBackgroundColor != 0) { return customBackgroundColor; } - return mView.getContext().getColor( - com.android.internal.R.color.notification_material_background_color); + return Utils.getColorAttr(mView.getContext(), android.R.attr.colorBackground) + .getDefaultColor(); } public void setLegacy(boolean legacy) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java index ba5f95e9c4d8..5cc17a08504f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java @@ -50,7 +50,6 @@ import android.util.DisplayMetrics; import android.util.Log; import android.util.MathUtils; import android.util.Pair; -import android.view.ContextThemeWrapper; import android.view.DisplayCutout; import android.view.InputDevice; import android.view.LayoutInflater; @@ -385,7 +384,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable */ private float mBackgroundXFactor = 1f; - private boolean mUsingLightTheme; private boolean mQsExpanded; private boolean mForwardScrollable; private boolean mBackwardScrollable; @@ -4122,15 +4120,9 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable * @param lightTheme True if light theme should be used. */ @ShadeViewRefactor(RefactorComponent.DECORATOR) - void updateDecorViews(boolean lightTheme) { - if (lightTheme == mUsingLightTheme) { - return; - } - mUsingLightTheme = lightTheme; - Context context = new ContextThemeWrapper(mContext, - lightTheme ? R.style.Theme_SystemUI_Light : R.style.Theme_SystemUI); + void updateDecorViews() { final @ColorInt int textColor = - Utils.getColorAttrDefaultColor(context, R.attr.wallpaperTextColor); + Utils.getColorAttrDefaultColor(mContext, android.R.attr.textColorPrimary); mSectionsManager.setHeaderForegroundColor(textColor); mFooterView.setTextColor(textColor); mEmptyShadeView.setTextColor(textColor); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java index abbbbb8352f5..8a0330912502 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java @@ -162,7 +162,6 @@ public class NotificationStackScrollLayoutController { private final KeyguardMediaController mKeyguardMediaController; private final SysuiStatusBarStateController mStatusBarStateController; private final KeyguardBypassController mKeyguardBypassController; - private final SysuiColorExtractor mColorExtractor; private final NotificationLockscreenUserManager mLockscreenUserManager; // TODO: StatusBar should be encapsulated behind a Controller private final StatusBar mStatusBar; @@ -224,12 +223,14 @@ public class NotificationStackScrollLayoutController { public void onOverlayChanged() { updateShowEmptyShadeView(); mView.updateCornerRadius(); + mView.updateBgColor(); mView.reinflateViews(); } @Override public void onUiModeChanged() { mView.updateBgColor(); + mView.updateDecorViews(); } @Override @@ -577,7 +578,6 @@ public class NotificationStackScrollLayoutController { mKeyguardMediaController = keyguardMediaController; mKeyguardBypassController = keyguardBypassController; mZenModeController = zenModeController; - mColorExtractor = colorExtractor; mLockscreenUserManager = lockscreenUserManager; mMetricsLogger = metricsLogger; mFalsingCollector = falsingCollector; @@ -689,12 +689,6 @@ public class NotificationStackScrollLayoutController { Settings.Secure.NOTIFICATION_DISMISS_RTL, Settings.Secure.NOTIFICATION_HISTORY_ENABLED); - mOnColorsChangedListener = (colorExtractor, which) -> { - final boolean useDarkText = mColorExtractor.getNeutralColors().supportsDarkText(); - mView.updateDecorViews(useDarkText); - }; - mColorExtractor.addOnColorsChangedListener(mOnColorsChangedListener); - mKeyguardMediaController.setVisibilityChangedListener(visible -> { mView.setKeyguardMediaControllorVisible(visible); if (visible) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java index 54fb863b5de7..0df3347b8f89 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java @@ -49,7 +49,7 @@ public class LockIcon extends KeyguardAffordanceView { static final int STATE_SCANNING_FACE = 2; static final int STATE_BIOMETRICS_ERROR = 3; private float mDozeAmount; - private int mIconColor; + private int mIconColor = Color.TRANSPARENT; private int mOldState; private int mState; private boolean mDozing; @@ -149,7 +149,10 @@ public class LockIcon extends KeyguardAffordanceView { updateDarkTint(); } - void onThemeChange(int iconColor) { + void updateColor(int iconColor) { + if (mIconColor == iconColor) { + return; + } mDrawableCache.clear(); mIconColor = iconColor; updateDarkTint(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java index 289ff71dcb46..0e7e2fd8173c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java @@ -84,6 +84,7 @@ public class LockscreenLockIconController { private boolean mDocked; private boolean mWakeAndUnlockRunning; private boolean mShowingLaunchAffordance; + private boolean mBouncerShowing; private boolean mBouncerShowingScrimmed; private boolean mFingerprintUnlock; private int mStatusBarState = StatusBarState.SHADE; @@ -142,16 +143,13 @@ public class LockscreenLockIconController { private int mDensity; @Override - public void onThemeChanged() { - if (mLockIcon == null) { - return; - } + public void onUiModeChanged() { + updateColor(); + } - TypedArray typedArray = mLockIcon.getContext().getTheme().obtainStyledAttributes( - null, new int[]{ R.attr.wallpaperTextColor }, 0, 0); - int iconColor = typedArray.getColor(0, Color.WHITE); - typedArray.recycle(); - mLockIcon.onThemeChange(iconColor); + @Override + public void onOverlayChanged() { + updateColor(); } @Override @@ -350,6 +348,7 @@ public class LockscreenLockIconController { */ public void attach(LockIcon lockIcon) { mLockIcon = lockIcon; + updateColor(); mLockIcon.setOnClickListener(this::handleClick); mLockIcon.setOnLongClickListener(this::handleLongClick); @@ -408,11 +407,22 @@ public class LockscreenLockIconController { } /** Sets whether the bouncer is showing. */ - public void setBouncerShowingScrimmed(boolean bouncerShowing) { - mBouncerShowingScrimmed = bouncerShowing; - if (mKeyguardBypassController.getBypassEnabled()) { - update(); + public void setBouncerShowingScrimmed(boolean showing, boolean scrimmed) { + mBouncerShowing = showing; + mBouncerShowingScrimmed = scrimmed; + update(); + } + + private void updateColor() { + if (mLockIcon == null) { + return; } + + TypedArray typedArray = mLockIcon.getContext().getTheme().obtainStyledAttributes( + null, new int[]{ android.R.attr.textColorPrimary }, 0, 0); + int iconColor = typedArray.getColor(0, Color.WHITE); + typedArray.recycle(); + mLockIcon.updateColor(iconColor); } /** @@ -510,7 +520,10 @@ public class LockscreenLockIconController { return changed; } boolean onAodOrDocked = mStatusBarStateController.isDozing() || mDocked; - boolean invisible = onAodOrDocked || mWakeAndUnlockRunning || mShowingLaunchAffordance; + boolean onKeyguardWithoutBouncer = mStatusBarState == StatusBarState.KEYGUARD + && !mBouncerShowing; + boolean invisible = onAodOrDocked || mWakeAndUnlockRunning || mShowingLaunchAffordance + || onKeyguardWithoutBouncer; boolean fingerprintOrBypass = mFingerprintUnlock || mKeyguardBypassController.getBypassEnabled(); if (fingerprintOrBypass && !mBouncerShowingScrimmed) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java index c67e8fd4f89c..84eacdc41841 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java @@ -86,6 +86,7 @@ import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.doze.DozeLog; import com.android.systemui.fragments.FragmentHostManager; import com.android.systemui.fragments.FragmentHostManager.FragmentListener; +import com.android.systemui.media.MediaDataManager; import com.android.systemui.media.MediaHierarchyManager; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.qs.QS; @@ -140,6 +141,7 @@ import java.util.function.Consumer; import java.util.function.Function; import javax.inject.Inject; +import javax.inject.Provider; @StatusBarComponent.StatusBarScope public class NotificationPanelViewController extends PanelViewController { @@ -184,7 +186,7 @@ public class NotificationPanelViewController extends PanelViewController { private final MetricsLogger mMetricsLogger; private final ActivityManager mActivityManager; private final ConfigurationController mConfigurationController; - private final FlingAnimationUtils.Builder mFlingAnimationUtilsBuilder; + private final Provider<FlingAnimationUtils.Builder> mFlingAnimationUtilsBuilder; private final NotificationStackScrollLayoutController mNotificationStackScrollLayoutController; private final NotificationIconAreaController mNotificationIconAreaController; @@ -304,7 +306,6 @@ public class NotificationPanelViewController extends PanelViewController { private FrameLayout mQsFrame; private KeyguardStatusViewController mKeyguardStatusViewController; private DisabledUdfpsController mDisabledUdfpsController; - private View mQsNavbarScrim; private NotificationsQuickSettingsContainer mNotificationContainerParent; private boolean mAnimateNextPositionUpdate; @@ -455,6 +456,7 @@ public class NotificationPanelViewController extends PanelViewController { private final CommandQueue mCommandQueue; private final NotificationLockscreenUserManager mLockscreenUserManager; private final ShadeController mShadeController; + private final MediaDataManager mMediaDataManager; private int mDisplayId; /** @@ -535,7 +537,7 @@ public class NotificationPanelViewController extends PanelViewController { KeyguardUpdateMonitor keyguardUpdateMonitor, MetricsLogger metricsLogger, ActivityManager activityManager, ConfigurationController configurationController, - FlingAnimationUtils.Builder flingAnimationUtilsBuilder, + Provider<FlingAnimationUtils.Builder> flingAnimationUtilsBuilder, StatusBarTouchableRegionManager statusBarTouchableRegionManager, ConversationNotificationManager conversationNotificationManager, MediaHierarchyManager mediaHierarchyManager, @@ -546,10 +548,11 @@ public class NotificationPanelViewController extends PanelViewController { NotificationGroupManagerLegacy groupManager, NotificationIconAreaController notificationIconAreaController, AuthController authController, - QSDetailDisplayer qsDetailDisplayer) { + QSDetailDisplayer qsDetailDisplayer, + MediaDataManager mediaDataManager) { super(view, falsingManager, dozeLog, keyguardStateController, (SysuiStatusBarStateController) statusBarStateController, vibratorHelper, - latencyTracker, flingAnimationUtilsBuilder, statusBarTouchableRegionManager); + latencyTracker, flingAnimationUtilsBuilder.get(), statusBarTouchableRegionManager); mView = view; mMetricsLogger = metricsLogger; mActivityManager = activityManager; @@ -576,6 +579,7 @@ public class NotificationPanelViewController extends PanelViewController { mPulseExpansionHandler = pulseExpansionHandler; mDozeParameters = dozeParameters; mBiometricUnlockController = biometricUnlockController; + mMediaDataManager = mediaDataManager; pulseExpansionHandler.setPulseExpandAbortListener(() -> { if (mQs != null) { mQs.animateHeaderSlidingOut(); @@ -650,7 +654,6 @@ public class NotificationPanelViewController extends PanelViewController { mOnEmptySpaceClickListener); addTrackingHeadsUpListener(mNotificationStackScrollLayoutController::setTrackingHeadsUp); mKeyguardBottomArea = mView.findViewById(R.id.keyguard_bottom_area); - mQsNavbarScrim = mView.findViewById(R.id.qs_navbar_scrim); mLastOrientation = mResources.getConfiguration().orientation; initBottomArea(); @@ -688,7 +691,7 @@ public class NotificationPanelViewController extends PanelViewController { @Override protected void loadDimens() { super.loadDimens(); - mFlingAnimationUtils = mFlingAnimationUtilsBuilder.reset() + mFlingAnimationUtils = mFlingAnimationUtilsBuilder.get() .setMaxLengthSeconds(0.4f).build(); mStatusBarMinHeight = mResources.getDimensionPixelSize( com.android.internal.R.dimen.status_bar_height); @@ -892,7 +895,8 @@ public class NotificationPanelViewController extends PanelViewController { int clockPreferredY = mKeyguardStatusViewController.getClockPreferredY(totalHeight); boolean bypassEnabled = mKeyguardBypassController.getBypassEnabled(); final boolean hasVisibleNotifications = !bypassEnabled - && mNotificationStackScrollLayoutController.getVisibleNotificationCount() != 0; + && (mNotificationStackScrollLayoutController.getVisibleNotificationCount() != 0 + || mMediaDataManager.hasActiveMedia()); mKeyguardStatusViewController.setHasVisibleNotifications(hasVisibleNotifications); mClockPositionAlgorithm.setup(mStatusBarMinHeight, totalHeight - bottomPadding, mNotificationStackScrollLayoutController.getIntrinsicContentHeight(), @@ -1730,9 +1734,6 @@ public class NotificationPanelViewController extends PanelViewController { mBarState != KEYGUARD && (!mQsExpanded || mQsExpansionFromOverscroll)); - mQsNavbarScrim.setVisibility( - mBarState == StatusBarState.SHADE && mQsExpanded && !mStackScrollerOverscrolling - && mQsScrimEnabled ? View.VISIBLE : View.INVISIBLE); if (mKeyguardUserSwitcher != null && mQsExpanded && !mStackScrollerOverscrolling) { mKeyguardUserSwitcher.hideIfNotSimple(true /* animate */); } @@ -1757,10 +1758,6 @@ public class NotificationPanelViewController extends PanelViewController { updateKeyguardBottomAreaAlpha(); updateBigClockAlpha(); } - if (mBarState == StatusBarState.SHADE && mQsExpanded && !mStackScrollerOverscrolling - && mQsScrimEnabled) { - mQsNavbarScrim.setAlpha(getQsExpansionFraction()); - } if (mAccessibilityManager.isEnabled()) { mView.setAccessibilityPaneTitle(determineAccessibilityPaneTitle()); @@ -2041,8 +2038,8 @@ public class NotificationPanelViewController extends PanelViewController { maxHeight = calculatePanelHeightShade(); } maxHeight = Math.max(min, maxHeight); - if (maxHeight == 0) { - Log.wtf(TAG, "maxPanelHeight is 0. getOverExpansionAmount(): " + if (maxHeight == 0 || isNaN(maxHeight)) { + Log.wtf(TAG, "maxPanelHeight is invalid. getOverExpansionAmount(): " + getOverExpansionAmount() + ", calculatePanelHeightQsExpanded: " + calculatePanelHeightQsExpanded() + ", calculatePanelHeightShade: " + calculatePanelHeightShade() + ", mStatusBarMinHeight = " diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java index 4af27877c201..9e7efc12f4f7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java @@ -37,22 +37,21 @@ import android.view.animation.Interpolator; import androidx.annotation.Nullable; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.colorextraction.ColorExtractor; import com.android.internal.colorextraction.ColorExtractor.GradientColors; -import com.android.internal.colorextraction.ColorExtractor.OnColorsChangedListener; import com.android.internal.graphics.ColorUtils; import com.android.internal.util.function.TriConsumer; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardUpdateMonitorCallback; +import com.android.settingslib.Utils; import com.android.systemui.DejankUtils; import com.android.systemui.Dumpable; import com.android.systemui.R; -import com.android.systemui.colorextraction.SysuiColorExtractor; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dock.DockManager; import com.android.systemui.statusbar.BlurUtils; import com.android.systemui.statusbar.ScrimView; import com.android.systemui.statusbar.notification.stack.ViewState; +import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.util.AlarmTimeout; import com.android.systemui.util.wakelock.DelayedWakeLock; @@ -71,8 +70,7 @@ import javax.inject.Inject; * security method gets shown). */ @SysUISingleton -public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnColorsChangedListener, - Dumpable { +public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dumpable { static final String TAG = "ScrimController"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); @@ -125,12 +123,12 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo * The default scrim under the shade and dialogs. * This should not be lower than 0.54, otherwise we won't pass GAR. */ - public static final float BUSY_SCRIM_ALPHA = 0.85f; + public static final float BUSY_SCRIM_ALPHA = 1f; /** * Same as above, but when blur is supported. */ - public static final float BLUR_SCRIM_ALPHA = 0.54f; + public static final float BLUR_SCRIM_ALPHA = 0.80f; static final int TAG_KEY_ANIM = R.id.scrim; private static final int TAG_START_ALPHA = R.id.scrim_alpha_start; @@ -153,8 +151,8 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo private final AlarmTimeout mTimeTicker; private final KeyguardVisibilityCallback mKeyguardVisibilityCallback; private final Handler mHandler; + private final BlurUtils mBlurUtils; - private final SysuiColorExtractor mColorExtractor; private GradientColors mColors; private boolean mNeedsDrawableColorUpdate; @@ -204,12 +202,13 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo public ScrimController(LightBarController lightBarController, DozeParameters dozeParameters, AlarmManager alarmManager, KeyguardStateController keyguardStateController, DelayedWakeLock.Builder delayedWakeLockBuilder, Handler handler, - KeyguardUpdateMonitor keyguardUpdateMonitor, SysuiColorExtractor sysuiColorExtractor, - DockManager dockManager, BlurUtils blurUtils) { + KeyguardUpdateMonitor keyguardUpdateMonitor, DockManager dockManager, + BlurUtils blurUtils, ConfigurationController configurationController) { mScrimStateListener = lightBarController::setScrimState; mDefaultScrimAlpha = blurUtils.supportsBlursOnWindows() ? BLUR_SCRIM_ALPHA : BUSY_SCRIM_ALPHA; + mBlurUtils = blurUtils; mKeyguardStateController = keyguardStateController; mDarkenWhileDragging = !mKeyguardStateController.canDismissLockScreen(); @@ -230,11 +229,24 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo keyguardStateController.getKeyguardFadingAwayDuration()); } }); + configurationController.addCallback(new ConfigurationController.ConfigurationListener() { + @Override + public void onThemeChanged() { + ScrimController.this.onThemeChanged(); + } - mColorExtractor = sysuiColorExtractor; - mColorExtractor.addOnColorsChangedListener(this); - mColors = mColorExtractor.getNeutralColors(); - mNeedsDrawableColorUpdate = true; + @Override + public void onOverlayChanged() { + ScrimController.this.onThemeChanged(); + } + + @Override + public void onUiModeChanged() { + ScrimController.this.onThemeChanged(); + } + }); + + mColors = new GradientColors(); } /** @@ -245,6 +257,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo mScrimBehind = scrimBehind; mScrimInFront = scrimInFront; mScrimForBubble = scrimForBubble; + updateThemeColors(); if (mScrimBehindChangeRunnable != null) { mScrimBehind.setChangeRunnable(mScrimBehindChangeRunnable); @@ -619,11 +632,6 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo mScrimInFront.setColors(mColors, animateScrimInFront); mScrimBehind.setColors(mColors, animateScrimBehind); - // Calculate minimum scrim opacity for white or black text. - int textColor = mColors.supportsDarkText() ? Color.BLACK : Color.WHITE; - int mainColor = mColors.getMainColor(); - float minOpacity = ColorUtils.calculateMinimumBackgroundAlpha(textColor, mainColor, - 4.5f /* minimumContrast */) / 255f; dispatchScrimState(mScrimBehind.getViewAlpha()); } @@ -968,10 +976,19 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo // Don't care in the base class. } - @Override - public void onColorsChanged(ColorExtractor colorExtractor, int which) { - mColors = mColorExtractor.getNeutralColors(); + private void updateThemeColors() { + int background = Utils.getColorAttr(mScrimBehind.getContext(), + android.R.attr.colorBackgroundFloating).getDefaultColor(); + int accent = Utils.getColorAccent(mScrimBehind.getContext()).getDefaultColor(); + mColors.setMainColor(background); + mColors.setSecondaryColor(accent); + mColors.setSupportsDarkText( + ColorUtils.calculateContrast(mColors.getMainColor(), Color.WHITE) > 4.5); mNeedsDrawableColorUpdate = true; + } + + private void onThemeChanged() { + updateThemeColors(); scheduleUpdate(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java index c8c5a6331a3b..9e872ab65591 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -3782,7 +3782,8 @@ public class StatusBar extends SystemUI implements DemoMode, mBouncerShowing = bouncerShowing; mKeyguardBypassController.setBouncerShowing(bouncerShowing); mPulseExpansionHandler.setBouncerShowing(bouncerShowing); - mLockscreenLockIconController.setBouncerShowingScrimmed(isBouncerShowingScrimmed()); + mLockscreenLockIconController.setBouncerShowingScrimmed(bouncerShowing, + isBouncerShowingScrimmed()); if (mStatusBarView != null) mStatusBarView.setBouncerShowing(bouncerShowing); updateHideIconsForBouncer(true /* animate */); mCommandQueue.recomputeDisableFlags(mDisplayId, true /* animate */); diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java index 0aa2a739eb82..50cef781d9d0 100644 --- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java +++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java @@ -47,7 +47,7 @@ import java.util.stream.Collectors; */ @SysUISingleton public class ThemeOverlayApplier implements Dumpable { - private static final String TAG = "ThemeOverlayManager"; + private static final String TAG = "ThemeOverlayApplier"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); @VisibleForTesting diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java index c4e2b5d47ba8..006ecb937922 100644 --- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java +++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java @@ -307,13 +307,13 @@ public class ThemeOverlayController extends SystemUI implements Dumpable { if (!hasSystemPalette && mSystemOverlayColor != Color.TRANSPARENT) { categoryToPackage.put(OVERLAY_CATEGORY_SYSTEM_PALETTE, ThemeOverlayApplier.MONET_SYSTEM_PALETTE_PACKAGE - + Integer.toHexString(mSystemOverlayColor).toUpperCase()); + + getColorString(mSystemOverlayColor)); } // Same for the accent color if (!hasAccentColor && mAccentOverlayColor != Color.TRANSPARENT) { categoryToPackage.put(OVERLAY_CATEGORY_ACCENT_COLOR, ThemeOverlayApplier.MONET_ACCENT_COLOR_PACKAGE - + Integer.toHexString(mAccentOverlayColor).toUpperCase()); + + getColorString(mAccentOverlayColor)); } Set<UserHandle> userHandles = Sets.newHashSet(UserHandle.of(currentUser)); @@ -325,6 +325,14 @@ public class ThemeOverlayController extends SystemUI implements Dumpable { mThemeManager.applyCurrentUserOverlays(categoryToPackage, userHandles); } + private String getColorString(int color) { + String colorString = Integer.toHexString(color).toUpperCase(); + while (colorString.length() < 6) { + colorString = "0" + colorString; + } + return colorString; + } + @Override public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) { pw.println("mLockColors=" + mLockColors); diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java index 86ba5f1f74a9..8dea5b5a19a3 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java @@ -22,6 +22,7 @@ import com.android.systemui.dagger.WMSingleton; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.WindowManagerShellWrapper; import com.android.wm.shell.common.DisplayController; +import com.android.wm.shell.common.SystemWindows; import com.android.wm.shell.common.TaskStackListenerImpl; import com.android.wm.shell.pip.Pip; import com.android.wm.shell.pip.PipBoundsAlgorithm; @@ -34,6 +35,7 @@ import com.android.wm.shell.pip.tv.PipController; import com.android.wm.shell.pip.tv.PipControlsView; import com.android.wm.shell.pip.tv.PipControlsViewController; import com.android.wm.shell.pip.tv.PipNotification; +import com.android.wm.shell.pip.tv.TvPipMenuController; import com.android.wm.shell.splitscreen.SplitScreen; import java.util.Optional; @@ -44,7 +46,7 @@ import dagger.Provides; /** * Dagger module for TV Pip. */ -@Module +@Module(includes = {WMShellBaseModule.class}) public abstract class TvPipModule { @WMSingleton @Provides @@ -53,6 +55,7 @@ public abstract class TvPipModule { PipBoundsState pipBoundsState, PipBoundsAlgorithm pipBoundsAlgorithm, PipTaskOrganizer pipTaskOrganizer, + TvPipMenuController tvPipMenuController, PipMediaController pipMediaController, PipNotification pipNotification, TaskStackListenerImpl taskStackListener, @@ -63,6 +66,7 @@ public abstract class TvPipModule { pipBoundsState, pipBoundsAlgorithm, pipTaskOrganizer, + tvPipMenuController, pipMediaController, pipNotification, taskStackListener, @@ -104,14 +108,22 @@ public abstract class TvPipModule { @WMSingleton @Provides + static TvPipMenuController providesPipTvMenuController(Context context, + PipBoundsState pipBoundsState, SystemWindows systemWindows) { + return new TvPipMenuController(context, pipBoundsState, systemWindows); + } + + @WMSingleton + @Provides static PipTaskOrganizer providePipTaskOrganizer(Context context, + TvPipMenuController tvMenuController, PipBoundsState pipBoundsState, PipBoundsAlgorithm pipBoundsAlgorithm, PipSurfaceTransactionHelper pipSurfaceTransactionHelper, Optional<SplitScreen> splitScreenOptional, DisplayController displayController, PipUiEventLogger pipUiEventLogger, ShellTaskOrganizer shellTaskOrganizer) { return new PipTaskOrganizer(context, pipBoundsState, pipBoundsAlgorithm, - null /* menuActivityController */, pipSurfaceTransactionHelper, splitScreenOptional, + tvMenuController, pipSurfaceTransactionHelper, splitScreenOptional, displayController, pipUiEventLogger, shellTaskOrganizer); } } diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/TvWMShellModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/TvWMShellModule.java index f88bedd88d9f..06f1522a7256 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/TvWMShellModule.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/TvWMShellModule.java @@ -41,7 +41,7 @@ import dagger.Provides; * Provides dependencies from {@link com.android.wm.shell} which could be customized among different * branches of SystemUI. */ -@Module(includes = {WMShellBaseModule.class, TvPipModule.class}) +@Module(includes = {TvPipModule.class}) public class TvWMShellModule { @WMSingleton @Provides diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java index 7a1c05890873..5f48c4294524 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java @@ -41,9 +41,9 @@ import com.android.wm.shell.pip.PipMediaController; import com.android.wm.shell.pip.PipSurfaceTransactionHelper; import com.android.wm.shell.pip.PipTaskOrganizer; import com.android.wm.shell.pip.PipUiEventLogger; +import com.android.wm.shell.pip.phone.PhonePipMenuController; import com.android.wm.shell.pip.phone.PipAppOpsListener; import com.android.wm.shell.pip.phone.PipController; -import com.android.wm.shell.pip.phone.PipMenuActivityController; import com.android.wm.shell.pip.phone.PipTouchHandler; import com.android.wm.shell.splitscreen.SplitScreen; import com.android.wm.shell.splitscreen.SplitScreenController; @@ -93,12 +93,12 @@ public class WMShellModule { static Optional<Pip> providePip(Context context, DisplayController displayController, PipAppOpsListener pipAppOpsListener, PipBoundsAlgorithm pipBoundsAlgorithm, PipBoundsState pipBoundsState, PipMediaController pipMediaController, - PipMenuActivityController pipMenuActivityController, PipTaskOrganizer pipTaskOrganizer, + PhonePipMenuController phonePipMenuController, PipTaskOrganizer pipTaskOrganizer, PipTouchHandler pipTouchHandler, WindowManagerShellWrapper windowManagerShellWrapper, TaskStackListenerImpl taskStackListener, ShellExecutor mainExecutor) { return Optional.ofNullable(PipController.create(context, displayController, pipAppOpsListener, pipBoundsAlgorithm, pipBoundsState, pipMediaController, - pipMenuActivityController, pipTaskOrganizer, pipTouchHandler, + phonePipMenuController, pipTaskOrganizer, pipTouchHandler, windowManagerShellWrapper, taskStackListener, mainExecutor)); } @@ -117,20 +117,20 @@ public class WMShellModule { @WMSingleton @Provides - static PipMenuActivityController providesPipMenuActivityController(Context context, + static PhonePipMenuController providesPipPhoneMenuController(Context context, PipMediaController pipMediaController, SystemWindows systemWindows) { - return new PipMenuActivityController(context, pipMediaController, systemWindows); + return new PhonePipMenuController(context, pipMediaController, systemWindows); } @WMSingleton @Provides static PipTouchHandler providePipTouchHandler(Context context, - PipMenuActivityController menuActivityController, PipBoundsAlgorithm pipBoundsAlgorithm, + PhonePipMenuController menuPhoneController, PipBoundsAlgorithm pipBoundsAlgorithm, PipBoundsState pipBoundsState, PipTaskOrganizer pipTaskOrganizer, FloatingContentCoordinator floatingContentCoordinator, PipUiEventLogger pipUiEventLogger) { - return new PipTouchHandler(context, menuActivityController, pipBoundsAlgorithm, + return new PipTouchHandler(context, menuPhoneController, pipBoundsAlgorithm, pipBoundsState, pipTaskOrganizer, floatingContentCoordinator, pipUiEventLogger); } @@ -139,12 +139,12 @@ public class WMShellModule { static PipTaskOrganizer providePipTaskOrganizer(Context context, PipBoundsState pipBoundsState, PipBoundsAlgorithm pipBoundsAlgorithm, - PipMenuActivityController menuActivityController, + PhonePipMenuController menuPhoneController, PipSurfaceTransactionHelper pipSurfaceTransactionHelper, Optional<SplitScreen> splitScreenOptional, DisplayController displayController, PipUiEventLogger pipUiEventLogger, ShellTaskOrganizer shellTaskOrganizer) { return new PipTaskOrganizer(context, pipBoundsState, pipBoundsAlgorithm, - menuActivityController, pipSurfaceTransactionHelper, splitScreenOptional, + menuPhoneController, pipSurfaceTransactionHelper, splitScreenOptional, displayController, pipUiEventLogger, shellTaskOrganizer); } } diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java index eef38d316775..b03dc94fde33 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java @@ -34,6 +34,7 @@ import com.android.internal.logging.UiEventLogger; import com.android.internal.widget.LockPatternUtils; import com.android.keyguard.KeyguardSecurityModel.SecurityMode; import com.android.systemui.SysuiTestCase; +import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.KeyguardStateController; import org.junit.Before; @@ -80,6 +81,8 @@ public class KeyguardSecurityContainerControllerTest extends SysuiTestCase { private KeyguardSecurityViewFlipper mSecurityViewFlipper; @Mock private KeyguardSecurityViewFlipperController mKeyguardSecurityViewFlipperController; + @Mock + private ConfigurationController mConfigurationController; private KeyguardSecurityContainerController mKeyguardSecurityContainerController; @@ -92,8 +95,8 @@ public class KeyguardSecurityContainerControllerTest extends SysuiTestCase { mKeyguardSecurityContainerController = new KeyguardSecurityContainerController.Factory( mView, mAdminSecondaryLockScreenControllerFactory, mLockPatternUtils, mKeyguardUpdateMonitor, mKeyguardSecurityModel, mMetricsLogger, mUiEventLogger, - mKeyguardStateController, mKeyguardSecurityViewFlipperController) - .create(mSecurityCallback); + mKeyguardStateController, mKeyguardSecurityViewFlipperController, + mConfigurationController).create(mSecurityCallback); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineClassifierTest.java new file mode 100644 index 000000000000..19f0a15c8936 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineClassifierTest.java @@ -0,0 +1,164 @@ +/* + * 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.classifier; + +import static com.android.systemui.util.mockito.KotlinMockitoHelpersKt.any; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.testing.AndroidTestingRunner; +import android.view.MotionEvent; + +import androidx.test.filters.SmallTest; + +import com.android.internal.logging.MetricsLogger; +import com.android.internal.logging.testing.FakeMetricsLogger; +import com.android.systemui.SysuiTestCase; +import com.android.systemui.dock.DockManagerFake; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +@SmallTest +@RunWith(AndroidTestingRunner.class) +public class BrightLineClassifierTest extends SysuiTestCase { + private BrightLineFalsingManager mBrightLineFalsingManager; + @Mock + private FalsingDataProvider mFalsingDataProvider; + private final DockManagerFake mDockManager = new DockManagerFake(); + private final MetricsLogger mMetricsLogger = new FakeMetricsLogger(); + private final Set<FalsingClassifier> mClassifiers = new HashSet<>(); + @Mock + private SingleTapClassifier mSingleTapClassfier; + @Mock + private DoubleTapClassifier mDoubleTapClassifier; + @Mock + private FalsingClassifier mClassifierA; + @Mock + private FalsingClassifier mClassifierB; + private final List<MotionEvent> mMotionEventList = new ArrayList<>(); + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + mClassifiers.add(mClassifierA); + mClassifiers.add(mClassifierB); + when(mFalsingDataProvider.isDirty()).thenReturn(true); + when(mFalsingDataProvider.getRecentMotionEvents()).thenReturn(mMotionEventList); + mBrightLineFalsingManager = new BrightLineFalsingManager(mFalsingDataProvider, mDockManager, + mMetricsLogger, mClassifiers, mSingleTapClassfier, mDoubleTapClassifier, false); + } + + @Test + public void testRegisterSessionListener() { + verify(mFalsingDataProvider).addSessionListener( + any(FalsingDataProvider.SessionListener.class)); + + mBrightLineFalsingManager.cleanup(); + verify(mFalsingDataProvider).removeSessionListener( + any(FalsingDataProvider.SessionListener.class)); + } + + @Test + public void testIsFalseTouch_NoClassifiers() { + mClassifiers.clear(); + + assertThat(mBrightLineFalsingManager.isFalseTouch(0)).isFalse(); + } + + @Test + public void testIsFalseTouch_ClassffiersPass() { + assertThat(mBrightLineFalsingManager.isFalseTouch(0)).isFalse(); + } + + @Test + public void testIsFalseTouch_ClassifierARejects() { + when(mClassifierA.isFalseTouch()).thenReturn(true); + assertThat(mBrightLineFalsingManager.isFalseTouch(0)).isTrue(); + } + + @Test + public void testIsFalseTouch_ClassifierBRejects() { + when(mClassifierB.isFalseTouch()).thenReturn(true); + assertThat(mBrightLineFalsingManager.isFalseTouch(0)).isTrue(); + } + + @Test + public void testIsFalseTouch_FaceAuth() { + // Even when the classifiers report a false, we should allow. + when(mClassifierA.isFalseTouch()).thenReturn(true); + when(mFalsingDataProvider.isJustUnlockedWithFace()).thenReturn(true); + + assertThat(mBrightLineFalsingManager.isFalseTouch(0)).isFalse(); + } + + @Test + public void testIsFalseTouch_Docked() { + // Even when the classifiers report a false, we should allow. + when(mClassifierA.isFalseTouch()).thenReturn(true); + mDockManager.setIsDocked(true); + + assertThat(mBrightLineFalsingManager.isFalseTouch(0)).isFalse(); + } + + @Test + public void testIsFalseTap_BasicCheck() { + when(mSingleTapClassfier.isTap(mMotionEventList)).thenReturn(false); + + assertThat(mBrightLineFalsingManager.isFalseTap(false)).isTrue(); + + when(mSingleTapClassfier.isTap(mMotionEventList)).thenReturn(true); + + assertThat(mBrightLineFalsingManager.isFalseTap(false)).isFalse(); + } + + @Test + public void testIsFalseTap_RobustCheck_NoFaceAuth() { + when(mSingleTapClassfier.isTap(mMotionEventList)).thenReturn(true); + mFalsingDataProvider.setJustUnlockedWithFace(false); + assertThat(mBrightLineFalsingManager.isFalseTap(true)).isTrue(); + } + + @Test + public void testIsFalseTap_RobustCheck_FaceAuth() { + when(mSingleTapClassfier.isTap(mMotionEventList)).thenReturn(true); + when(mFalsingDataProvider.isJustUnlockedWithFace()).thenReturn(true); + assertThat(mBrightLineFalsingManager.isFalseTap(true)).isFalse(); + } + + @Test + public void testIsFalseDoubleTap() { + when(mDoubleTapClassifier.isFalseTouch()).thenReturn(false); + + assertThat(mBrightLineFalsingManager.isFalseDoubleTap()).isFalse(); + + when(mDoubleTapClassifier.isFalseTouch()).thenReturn(true); + + assertThat(mBrightLineFalsingManager.isFalseDoubleTap()).isTrue(); + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/DiagonalClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/DiagonalClassifierTest.java index 714d6581ff00..7659db8cc9ef 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/DiagonalClassifierTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/DiagonalClassifierTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 The Android Open Source Project + * 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.classifier.brightline; +package com.android.systemui.classifier; import static com.android.systemui.classifier.Classifier.LEFT_AFFORDANCE; import static com.android.systemui.classifier.Classifier.RIGHT_AFFORDANCE; @@ -27,8 +27,6 @@ import android.testing.AndroidTestingRunner; import androidx.test.filters.SmallTest; -import com.android.systemui.classifier.ClassifierTest; -import com.android.systemui.classifier.FalsingDataProvider; import com.android.systemui.util.DeviceConfigProxyFake; import org.junit.After; diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/DistanceClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/DistanceClassifierTest.java index d66c7a9d43a5..013fa369e876 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/DistanceClassifierTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/DistanceClassifierTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 The Android Open Source Project + * 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.classifier.brightline; +package com.android.systemui.classifier; import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.assertThat; @@ -23,8 +23,6 @@ import android.testing.AndroidTestingRunner; import androidx.test.filters.SmallTest; -import com.android.systemui.classifier.ClassifierTest; -import com.android.systemui.classifier.FalsingDataProvider; import com.android.systemui.util.DeviceConfigProxyFake; import org.junit.After; diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/DoubleTapClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/DoubleTapClassifierTest.java index 288ab0ad6596..4c4108a0cb90 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/DoubleTapClassifierTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/DoubleTapClassifierTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 The Android Open Source Project + * 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.classifier.brightline; +package com.android.systemui.classifier; import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.assertThat; @@ -27,9 +27,6 @@ import android.view.MotionEvent; import androidx.test.filters.SmallTest; -import com.android.systemui.classifier.ClassifierTest; -import com.android.systemui.classifier.FalsingDataProvider; - import org.junit.After; import org.junit.Before; import org.junit.Test; diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/PointerCountClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/PointerCountClassifierTest.java index b512f0d6ef32..ee289b5b922d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/PointerCountClassifierTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/PointerCountClassifierTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 The Android Open Source Project + * 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.classifier.brightline; +package com.android.systemui.classifier; import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS; @@ -26,8 +26,6 @@ import android.view.MotionEvent; import androidx.test.filters.SmallTest; -import com.android.systemui.classifier.ClassifierTest; - import org.junit.After; import org.junit.Before; import org.junit.Test; diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ProximityClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/ProximityClassifierTest.java index c2e290f166a3..38b025f675ee 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ProximityClassifierTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/ProximityClassifierTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 The Android Open Source Project + * 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.classifier.brightline; +package com.android.systemui.classifier; import static com.android.systemui.classifier.Classifier.GENERIC; import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS; @@ -28,8 +28,6 @@ import android.view.MotionEvent; import androidx.test.filters.SmallTest; -import com.android.systemui.classifier.ClassifierTest; -import com.android.systemui.classifier.FalsingDataProvider; import com.android.systemui.util.DeviceConfigProxyFake; import com.android.systemui.util.sensors.ProximitySensor; diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/SingleTapClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/SingleTapClassifierTest.java index d67f2b833deb..941e12e475f9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/SingleTapClassifierTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/SingleTapClassifierTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 The Android Open Source Project + * 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.classifier.brightline; +package com.android.systemui.classifier; import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.assertThat; @@ -25,9 +25,6 @@ import android.view.MotionEvent; import androidx.test.filters.SmallTest; -import com.android.systemui.classifier.ClassifierTest; -import com.android.systemui.classifier.FalsingDataProvider; - import org.junit.After; import org.junit.Before; import org.junit.Test; diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/TimeLimitedMotionEventBufferTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/TimeLimitedMotionEventBufferTest.java index 1dfffb271f02..6e312594a2e4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/TimeLimitedMotionEventBufferTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/TimeLimitedMotionEventBufferTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 The Android Open Source Project + * 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.classifier.brightline; +package com.android.systemui.classifier; import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.assertThat; diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/TypeClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/TypeClassifierTest.java index 5f3b84c2f7ae..6b9bb4fedd16 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/TypeClassifierTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/TypeClassifierTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 The Android Open Source Project + * 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.classifier.brightline; +package com.android.systemui.classifier; import static com.android.systemui.classifier.Classifier.BOUNCER_UNLOCK; import static com.android.systemui.classifier.Classifier.LEFT_AFFORDANCE; @@ -33,9 +33,6 @@ import android.testing.AndroidTestingRunner; import androidx.test.filters.SmallTest; -import com.android.systemui.classifier.ClassifierTest; -import com.android.systemui.classifier.FalsingDataProvider; - import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ZigZagClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/ZigZagClassifierTest.java index e49262f5099f..339dd9e9e6d2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ZigZagClassifierTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/ZigZagClassifierTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 The Android Open Source Project + * 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.classifier.brightline; +package com.android.systemui.classifier; import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.assertThat; @@ -23,7 +23,6 @@ import android.testing.AndroidTestingRunner; import androidx.test.filters.SmallTest; -import com.android.systemui.classifier.ClassifierTest; import com.android.systemui.util.DeviceConfigProxyFake; import org.junit.After; diff --git a/packages/SystemUI/tests/src/com/android/systemui/dock/DockManagerFake.java b/packages/SystemUI/tests/src/com/android/systemui/dock/DockManagerFake.java index a6355880c660..37540621557f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/dock/DockManagerFake.java +++ b/packages/SystemUI/tests/src/com/android/systemui/dock/DockManagerFake.java @@ -22,6 +22,7 @@ package com.android.systemui.dock; public class DockManagerFake implements DockManager { DockEventListener mCallback; AlignmentStateListener mAlignmentListener; + private boolean mDocked; @Override public void addListener(DockEventListener callback) { @@ -45,7 +46,11 @@ public class DockManagerFake implements DockManager { @Override public boolean isDocked() { - return false; + return mDocked; + } + + public void setIsDocked(boolean docked) { + mDocked = docked; } @Override diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java index 3d582e74ea4e..d4a94a19af4f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java @@ -63,6 +63,7 @@ import com.android.systemui.biometrics.AuthController; import com.android.systemui.classifier.FalsingCollectorFake; import com.android.systemui.classifier.FalsingManagerFake; import com.android.systemui.doze.DozeLog; +import com.android.systemui.media.MediaDataManager; import com.android.systemui.media.MediaHierarchyManager; import com.android.systemui.qs.QSDetailDisplayer; import com.android.systemui.statusbar.CommandQueue; @@ -197,6 +198,8 @@ public class NotificationPanelViewTest extends SysuiTestCase { private NotificationStackScrollLayoutController mNotificationStackScrollLayoutController; @Mock private AuthController mAuthController; + @Mock + private MediaDataManager mMediaDataManager; private NotificationPanelViewController mNotificationPanelViewController; private View.AccessibilityDelegate mAccessibiltyDelegate; @@ -267,7 +270,7 @@ public class NotificationPanelViewTest extends SysuiTestCase { mDozeParameters, mCommandQueue, mVibratorHelper, mLatencyTracker, mPowerManager, mAccessibilityManager, 0, mUpdateMonitor, mMetricsLogger, mActivityManager, mConfigurationController, - flingAnimationUtilsBuilder, mStatusBarTouchableRegionManager, + () -> flingAnimationUtilsBuilder, mStatusBarTouchableRegionManager, mConversationNotificationManager, mMediaHiearchyManager, mBiometricUnlockController, mStatusBarKeyguardViewManager, mNotificationStackScrollLayoutController, @@ -275,7 +278,8 @@ public class NotificationPanelViewTest extends SysuiTestCase { mGroupManager, mNotificationAreaController, mAuthController, - new QSDetailDisplayer()); + new QSDetailDisplayer(), + mMediaDataManager); mNotificationPanelViewController.initDependencies( mStatusBar, mNotificationShelfController); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java index 23f263746fb3..eaf31ed17bb2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java @@ -48,10 +48,10 @@ import com.android.internal.colorextraction.ColorExtractor.GradientColors; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.systemui.DejankUtils; import com.android.systemui.SysuiTestCase; -import com.android.systemui.colorextraction.SysuiColorExtractor; import com.android.systemui.dock.DockManager; import com.android.systemui.statusbar.BlurUtils; import com.android.systemui.statusbar.ScrimView; +import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.util.wakelock.DelayedWakeLock; import com.android.systemui.utils.os.FakeHandler; @@ -99,11 +99,11 @@ public class ScrimControllerTest extends SysuiTestCase { @Mock KeyguardUpdateMonitor mKeyguardUpdateMonitor; @Mock - private SysuiColorExtractor mSysuiColorExtractor; - @Mock private DockManager mDockManager; @Mock private BlurUtils mBlurUtils; + @Mock + private ConfigurationController mConfigurationController; private static class AnimatorListener implements Animator.AnimatorListener { @@ -196,6 +196,7 @@ public class ScrimControllerTest extends SysuiTestCase { when(mDozeParamenters.getAlwaysOn()).thenAnswer(invocation -> mAlwaysOnEnabled); when(mDozeParamenters.getDisplayNeedsBlanking()).thenReturn(true); + when(mBlurUtils.supportsBlursOnWindows()).thenReturn(true); doAnswer((Answer<Void>) invocation -> { mScrimState = invocation.getArgument(0); @@ -211,14 +212,12 @@ public class ScrimControllerTest extends SysuiTestCase { .thenReturn(mDelayedWakeLockBuilder); when(mDelayedWakeLockBuilder.build()).thenReturn(mWakeLock); - when(mSysuiColorExtractor.getNeutralColors()).thenReturn(new GradientColors()); - when(mDockManager.isDocked()).thenReturn(false); mScrimController = new ScrimController(mLightBarController, mDozeParamenters, mAlarmManager, mKeyguardStateController, mDelayedWakeLockBuilder, - new FakeHandler(mLooper.getLooper()), mKeyguardUpdateMonitor, mSysuiColorExtractor, - mDockManager, mBlurUtils); + new FakeHandler(mLooper.getLooper()), mKeyguardUpdateMonitor, + mDockManager, mBlurUtils, mConfigurationController); mScrimController.setScrimVisibleListener(visible -> mScrimVisibility = visible); mScrimController.attachViews(mScrimBehind, mScrimInFront, mScrimForBubble); mScrimController.setAnimatorListener(mAnimatorListener); @@ -520,10 +519,10 @@ public class ScrimControllerTest extends SysuiTestCase { Assert.assertEquals(ScrimController.TRANSPARENT, mScrimInFront.getViewAlpha(), 0.0f); // Back scrim should be visible - Assert.assertEquals(ScrimController.BUSY_SCRIM_ALPHA, + Assert.assertEquals(ScrimController.BLUR_SCRIM_ALPHA, mScrimBehind.getViewAlpha(), 0.0f); // Bubble scrim should be visible - Assert.assertEquals(ScrimController.BUSY_SCRIM_ALPHA, + Assert.assertEquals(ScrimController.BLUR_SCRIM_ALPHA, mScrimBehind.getViewAlpha(), 0.0f); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java index aee884022d95..c826cbcb5389 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java @@ -107,7 +107,7 @@ public class ThemeOverlayControllerTest extends SysuiTestCase { eq(UserHandle.USER_ALL)); verify(mDumpManager).registerDumpable(any(), any()); - List<Integer> colorList = List.of(Color.RED, Color.BLUE); + List<Integer> colorList = List.of(Color.RED, Color.BLUE, 0x0CCCCC, 0x000111); when(mThemeOverlayApplier.getAvailableAccentColors()).thenReturn(colorList); when(mThemeOverlayApplier.getAvailableSystemColors()).thenReturn(colorList); } @@ -148,6 +148,23 @@ public class ThemeOverlayControllerTest extends SysuiTestCase { } @Test + public void onWallpaperColorsChanged_addsLeadingZerosToColors() { + // Should ask for a new theme when wallpaper colors change + WallpaperColors mainColors = new WallpaperColors(Color.valueOf(0x0CCCCC), + Color.valueOf(0x000111), null); + mColorsListener.getValue().onColorsChanged(mainColors, WallpaperManager.FLAG_SYSTEM); + ArgumentCaptor<Map<String, String>> themeOverlays = ArgumentCaptor.forClass(Map.class); + + verify(mThemeOverlayApplier).applyCurrentUserOverlays(themeOverlays.capture(), any()); + + // Assert that we received the colors that we were expecting + assertThat(themeOverlays.getValue().get(OVERLAY_CATEGORY_SYSTEM_PALETTE)) + .isEqualTo(MONET_SYSTEM_PALETTE_PACKAGE + "0CCCCC"); + assertThat(themeOverlays.getValue().get(OVERLAY_CATEGORY_ACCENT_COLOR)) + .isEqualTo(MONET_ACCENT_COLOR_PACKAGE + "000111"); + } + + @Test public void onWallpaperColorsChanged_preservesWallpaperPickerTheme() { // Should ask for a new theme when wallpaper colors change WallpaperColors mainColors = new WallpaperColors(Color.valueOf(Color.RED), diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationPromptController.java b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationPromptController.java index 721220729a33..d2c1bc10abb0 100644 --- a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationPromptController.java +++ b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationPromptController.java @@ -106,13 +106,16 @@ public class WindowMagnificationPromptController { final Notification.Builder notificationBuilder = new Notification.Builder(mContext, SystemNotificationChannels.ACCESSIBILITY_MAGNIFICATION); + final String message = mContext.getString(R.string.window_magnification_prompt_content); + notificationBuilder.setSmallIcon(R.drawable.ic_settings_24dp) .setContentTitle(mContext.getString(R.string.window_magnification_prompt_title)) - .setContentText(mContext.getString(R.string.window_magnification_prompt_content)) + .setContentText(message) .setLargeIcon(Icon.createWithResource(mContext, R.drawable.ic_accessibility_magnification)) .setTicker(mContext.getString(R.string.window_magnification_prompt_title)) .setOnlyAlertOnce(true) + .setStyle(new Notification.BigTextStyle().bigText(message)) .setDeleteIntent(createPendingIntent(ACTION_DISMISS)) .setContentIntent(createPendingIntent(ACTION_TURN_ON_IN_SETTINGS)) .setActions(buildTurnOnAction(), buildDismissAction()); diff --git a/services/core/Android.bp b/services/core/Android.bp index 614863d6b8f5..17e3456e565b 100644 --- a/services/core/Android.bp +++ b/services/core/Android.bp @@ -145,6 +145,7 @@ java_library_static { "SurfaceFlingerProperties", "com.android.sysprop.watchdog", ], + javac_shard_size: 50, } java_genrule { diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index e8ee18c6f4d4..f0677a23765c 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -41,6 +41,7 @@ import static android.net.INetworkMonitor.NETWORK_VALIDATION_RESULT_VALID; import static android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL; import static android.net.NetworkCapabilities.NET_CAPABILITY_FOREGROUND; import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET; +import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING; @@ -6354,18 +6355,71 @@ public class ConnectivityService extends IConnectivityManager.Stub nai.declaredMetered = !nc.hasCapability(NET_CAPABILITY_NOT_METERED); } - /** Propagates to |nc| the capabilities declared by the underlying networks of |nai|. */ - private void mixInUnderlyingCapabilities(NetworkAgentInfo nai, NetworkCapabilities nc) { - Network[] underlyingNetworks = nai.declaredUnderlyingNetworks; - Network defaultNetwork = getNetwork(getDefaultNetwork()); + /** Modifies |caps| based on the capabilities of the specified underlying networks. */ + @VisibleForTesting + void applyUnderlyingCapabilities(@Nullable Network[] underlyingNetworks, + @NonNull NetworkCapabilities caps, boolean declaredMetered) { + final Network defaultNetwork = getNetwork(getDefaultNetwork()); if (underlyingNetworks == null && defaultNetwork != null) { // null underlying networks means to track the default. underlyingNetworks = new Network[] { defaultNetwork }; } + int[] transportTypes = new int[] { NetworkCapabilities.TRANSPORT_VPN }; + int downKbps = NetworkCapabilities.LINK_BANDWIDTH_UNSPECIFIED; + int upKbps = NetworkCapabilities.LINK_BANDWIDTH_UNSPECIFIED; + boolean metered = declaredMetered; // metered if any underlying is metered, or agentMetered + boolean roaming = false; // roaming if any underlying is roaming + boolean congested = false; // congested if any underlying is congested + boolean suspended = true; // suspended if all underlying are suspended + + boolean hadUnderlyingNetworks = false; + if (null != underlyingNetworks) { + for (Network underlyingNetwork : underlyingNetworks) { + final NetworkAgentInfo underlying = + getNetworkAgentInfoForNetwork(underlyingNetwork); + if (underlying == null) continue; + + final NetworkCapabilities underlyingCaps = underlying.networkCapabilities; + hadUnderlyingNetworks = true; + for (int underlyingType : underlyingCaps.getTransportTypes()) { + transportTypes = ArrayUtils.appendInt(transportTypes, underlyingType); + } - // TODO(b/124469351): Get capabilities directly from ConnectivityService instead. - final ConnectivityManager cm = mContext.getSystemService(ConnectivityManager.class); - Vpn.applyUnderlyingCapabilities(cm, underlyingNetworks, nc, nai.declaredMetered); + // Merge capabilities of this underlying network. For bandwidth, assume the + // worst case. + downKbps = NetworkCapabilities.minBandwidth(downKbps, + underlyingCaps.getLinkDownstreamBandwidthKbps()); + upKbps = NetworkCapabilities.minBandwidth(upKbps, + underlyingCaps.getLinkUpstreamBandwidthKbps()); + // If this underlying network is metered, the VPN is metered (it may cost money + // to send packets on this network). + metered |= !underlyingCaps.hasCapability(NET_CAPABILITY_NOT_METERED); + // If this underlying network is roaming, the VPN is roaming (the billing structure + // is different than the usual, local one). + roaming |= !underlyingCaps.hasCapability(NET_CAPABILITY_NOT_ROAMING); + // If this underlying network is congested, the VPN is congested (the current + // condition of the network affects the performance of this network). + congested |= !underlyingCaps.hasCapability(NET_CAPABILITY_NOT_CONGESTED); + // If this network is not suspended, the VPN is not suspended (the VPN + // is able to transfer some data). + suspended &= !underlyingCaps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED); + } + } + if (!hadUnderlyingNetworks) { + // No idea what the underlying networks are; assume reasonable defaults + metered = true; + roaming = false; + congested = false; + suspended = false; + } + + caps.setTransportTypes(transportTypes); + caps.setLinkDownstreamBandwidthKbps(downKbps); + caps.setLinkUpstreamBandwidthKbps(upKbps); + caps.setCapability(NET_CAPABILITY_NOT_METERED, !metered); + caps.setCapability(NET_CAPABILITY_NOT_ROAMING, !roaming); + caps.setCapability(NET_CAPABILITY_NOT_CONGESTED, !congested); + caps.setCapability(NET_CAPABILITY_NOT_SUSPENDED, !suspended); } /** @@ -6422,7 +6476,7 @@ public class ConnectivityService extends IConnectivityManager.Stub } if (nai.supportsUnderlyingNetworks()) { - mixInUnderlyingCapabilities(nai, newNc); + applyUnderlyingCapabilities(nai.declaredUnderlyingNetworks, newNc, nai.declaredMetered); } return newNc; diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index 4165200d837a..c7e3b2344f54 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -91,6 +91,7 @@ import android.os.SystemClock; import android.os.SystemProperties; import android.os.TransactionTooLargeException; import android.os.UserHandle; +import android.os.UserManager; import android.provider.Settings; import android.stats.devicepolicy.DevicePolicyEnums; import android.text.TextUtils; @@ -165,6 +166,7 @@ public final class ActiveServices { public static final int FGS_FEATURE_ALLOWED_BY_DEVICE_IDLE_ALLOW_LIST = 15; public static final int FGS_FEATURE_ALLOWED_BY_SYSTEM_ALERT_WINDOW_PERMISSION = 16; public static final int FGS_FEATURE_ALLOWED_BY_FGS_BINDING = 17; + public static final int FGS_FEATURE_ALLOWED_BY_DEVICE_DEMO_MODE = 18; @IntDef(flag = true, prefix = { "FGS_FEATURE_" }, value = { FGS_FEATURE_DENIED, @@ -183,7 +185,8 @@ public final class ActiveServices { FGS_FEATURE_ALLOWED_BY_DEVICE_OWNER, FGS_FEATURE_ALLOWED_BY_DEVICE_IDLE_ALLOW_LIST, FGS_FEATURE_ALLOWED_BY_SYSTEM_ALERT_WINDOW_PERMISSION, - FGS_FEATURE_ALLOWED_BY_FGS_BINDING + FGS_FEATURE_ALLOWED_BY_FGS_BINDING, + FGS_FEATURE_ALLOWED_BY_DEVICE_DEMO_MODE }) @Retention(RetentionPolicy.SOURCE) public @interface FgsFeatureRetCode {} @@ -5287,6 +5290,12 @@ public final class ActiveServices { } } + if (ret == FGS_FEATURE_DENIED) { + if (UserManager.isDeviceInDemoMode(mAm.mContext)) { + ret = FGS_FEATURE_ALLOWED_BY_DEVICE_DEMO_MODE; + } + } + final String debugInfo = "[callingPackage: " + callingPackage + "; callingUid: " + callingUid @@ -5340,6 +5349,8 @@ public final class ActiveServices { return "ALLOWED_BY_SYSTEM_ALERT_WINDOW_PERMISSION"; case FGS_FEATURE_ALLOWED_BY_FGS_BINDING: return "ALLOWED_BY_FGS_BINDING"; + case FGS_FEATURE_ALLOWED_BY_DEVICE_DEMO_MODE: + return "ALLOWED_BY_DEVICE_DEMO_MODE"; default: return ""; } diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index b2f3bff7e277..63128ced8f93 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -253,6 +253,7 @@ import android.os.RemoteCallbackList; import android.os.RemoteException; import android.os.ResultReceiver; import android.os.ServiceManager; +import android.os.SharedMemory; import android.os.ShellCallback; import android.os.StrictMode; import android.os.SystemClock; @@ -348,6 +349,7 @@ import com.android.server.appop.AppOpsService; import com.android.server.compat.PlatformCompat; import com.android.server.contentcapture.ContentCaptureManagerInternal; import com.android.server.firewall.IntentFirewall; +import com.android.server.graphics.fonts.FontManagerInternal; import com.android.server.job.JobSchedulerInternal; import com.android.server.pm.Installer; import com.android.server.pm.permission.PermissionManagerServiceInternal; @@ -4359,6 +4361,11 @@ public class ActivityManagerService extends IActivityManager.Stub app.info.packageName); } } + SharedMemory serializedSystemFontMap = null; + final FontManagerInternal fm = LocalServices.getService(FontManagerInternal.class); + if (fm != null) { + serializedSystemFontMap = fm.getSerializedSystemFontMap(); + } checkTime(startTime, "attachApplicationLocked: immediately before bindApplication"); bindApplicationTimeMillis = SystemClock.elapsedRealtime(); @@ -4384,7 +4391,7 @@ public class ActivityManagerService extends IActivityManager.Stub app.compat, getCommonServicesLocked(app.isolated), mCoreSettingsObserver.getCoreSettingsLocked(), buildSerial, autofillOptions, contentCaptureOptions, - app.mDisabledCompatChanges); + app.mDisabledCompatChanges, serializedSystemFontMap); } else { thread.bindApplication(processName, appInfo, providerList, null, profilerInfo, null, null, null, testMode, @@ -4394,7 +4401,7 @@ public class ActivityManagerService extends IActivityManager.Stub app.compat, getCommonServicesLocked(app.isolated), mCoreSettingsObserver.getCoreSettingsLocked(), buildSerial, autofillOptions, contentCaptureOptions, - app.mDisabledCompatChanges); + app.mDisabledCompatChanges, serializedSystemFontMap); } if (profilerInfo != null) { profilerInfo.closeFd(); diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java index 234dcc9d74a5..3445275b76dd 100644 --- a/services/core/java/com/android/server/connectivity/Vpn.java +++ b/services/core/java/com/android/server/connectivity/Vpn.java @@ -18,10 +18,7 @@ package com.android.server.connectivity; import static android.Manifest.permission.BIND_VPN_SERVICE; import static android.net.ConnectivityManager.NETID_UNSET; -import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED; -import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING; -import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED; import static android.net.RouteInfo.RTN_THROW; import static android.net.RouteInfo.RTN_UNREACHABLE; @@ -111,7 +108,6 @@ import com.android.internal.net.LegacyVpnInfo; import com.android.internal.net.VpnConfig; import com.android.internal.net.VpnInfo; import com.android.internal.net.VpnProfile; -import com.android.internal.util.ArrayUtils; import com.android.server.DeviceIdleInternal; import com.android.server.LocalServices; import com.android.server.net.BaseNetworkObserver; @@ -463,99 +459,6 @@ public class Vpn { } /** - * Updates {@link #mNetworkCapabilities} based on current underlying networks and returns a - * defensive copy. - * - * <p>Does not propagate updated capabilities to apps. - * - * @param defaultNetwork underlying network for VPNs following platform's default - */ - public synchronized NetworkCapabilities updateCapabilities(@Nullable Network defaultNetwork) { - if (mConfig == null) { - // VPN is not running. - return null; - } - - Network[] underlyingNetworks = mConfig.underlyingNetworks; - if (underlyingNetworks == null && defaultNetwork != null) { - // null underlying networks means to track the default. - underlyingNetworks = new Network[] { defaultNetwork }; - } - // Only apps targeting Q and above can explicitly declare themselves as metered. - final boolean isAlwaysMetered = mIsPackageTargetingAtLeastQ && mConfig.isMetered; - - applyUnderlyingCapabilities( - mConnectivityManager, - underlyingNetworks, - mNetworkCapabilities, - isAlwaysMetered); - - return new NetworkCapabilities(mNetworkCapabilities); - } - - @VisibleForTesting - public static void applyUnderlyingCapabilities( - @NonNull final ConnectivityManager cm, - @Nullable final Network[] underlyingNetworks, - @NonNull final NetworkCapabilities caps, - final boolean isAlwaysMetered) { - int[] transportTypes = new int[] { NetworkCapabilities.TRANSPORT_VPN }; - int downKbps = NetworkCapabilities.LINK_BANDWIDTH_UNSPECIFIED; - int upKbps = NetworkCapabilities.LINK_BANDWIDTH_UNSPECIFIED; - boolean metered = isAlwaysMetered; // metered if any underlying is metered, or alwaysMetered - boolean roaming = false; // roaming if any underlying is roaming - boolean congested = false; // congested if any underlying is congested - boolean suspended = true; // suspended if all underlying are suspended - - boolean hadUnderlyingNetworks = false; - if (null != underlyingNetworks) { - for (Network underlying : underlyingNetworks) { - // TODO(b/124469351): Get capabilities directly from ConnectivityService instead. - final NetworkCapabilities underlyingCaps = cm.getNetworkCapabilities(underlying); - if (underlyingCaps == null) continue; - hadUnderlyingNetworks = true; - for (int underlyingType : underlyingCaps.getTransportTypes()) { - transportTypes = ArrayUtils.appendInt(transportTypes, underlyingType); - } - - // Merge capabilities of this underlying network. For bandwidth, assume the - // worst case. - downKbps = NetworkCapabilities.minBandwidth(downKbps, - underlyingCaps.getLinkDownstreamBandwidthKbps()); - upKbps = NetworkCapabilities.minBandwidth(upKbps, - underlyingCaps.getLinkUpstreamBandwidthKbps()); - // If this underlying network is metered, the VPN is metered (it may cost money - // to send packets on this network). - metered |= !underlyingCaps.hasCapability(NET_CAPABILITY_NOT_METERED); - // If this underlying network is roaming, the VPN is roaming (the billing structure - // is different than the usual, local one). - roaming |= !underlyingCaps.hasCapability(NET_CAPABILITY_NOT_ROAMING); - // If this underlying network is congested, the VPN is congested (the current - // condition of the network affects the performance of this network). - congested |= !underlyingCaps.hasCapability(NET_CAPABILITY_NOT_CONGESTED); - // If this network is not suspended, the VPN is not suspended (the VPN - // is able to transfer some data). - suspended &= !underlyingCaps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED); - } - } - if (!hadUnderlyingNetworks) { - // No idea what the underlying networks are; assume the safer defaults - metered = true; - roaming = false; - congested = false; - suspended = false; - } - - caps.setTransportTypes(transportTypes); - caps.setLinkDownstreamBandwidthKbps(downKbps); - caps.setLinkUpstreamBandwidthKbps(upKbps); - caps.setCapability(NET_CAPABILITY_NOT_METERED, !metered); - caps.setCapability(NET_CAPABILITY_NOT_ROAMING, !roaming); - caps.setCapability(NET_CAPABILITY_NOT_CONGESTED, !congested); - caps.setCapability(NET_CAPABILITY_NOT_SUSPENDED, !suspended); - } - - /** * Chooses whether to force all connections to go though VPN. * * Used to enable/disable legacy VPN lockdown. diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index 29b413d921e1..d4a19d6bc366 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -53,6 +53,7 @@ import android.hardware.display.AmbientBrightnessDayStats; import android.hardware.display.BrightnessChangeEvent; import android.hardware.display.BrightnessConfiguration; import android.hardware.display.Curve; +import android.hardware.display.DisplayManager; import android.hardware.display.DisplayManagerGlobal; import android.hardware.display.DisplayManagerInternal; import android.hardware.display.DisplayManagerInternal.DisplayTransactionListener; @@ -1252,11 +1253,19 @@ public final class DisplayManagerService extends SystemService { mDisplayModeDirector.setShouldAlwaysRespectAppRequestedMode(enabled); } - boolean shouldAlwaysRespectAppRequestedModeInternal() { return mDisplayModeDirector.shouldAlwaysRespectAppRequestedMode(); } + void setRefreshRateSwitchingTypeInternal(@DisplayManager.SwitchingType int newValue) { + mDisplayModeDirector.setModeSwitchingType(newValue); + } + + @DisplayManager.SwitchingType + int getRefreshRateSwitchingTypeInternal() { + return mDisplayModeDirector.getModeSwitchingType(); + } + private void setBrightnessConfigurationForUserInternal( @Nullable BrightnessConfiguration c, @UserIdInt int userId, @Nullable String packageName) { @@ -2595,6 +2604,32 @@ public final class DisplayManagerService extends SystemService { } } + @Override // Binder call + public void setRefreshRateSwitchingType(int newValue) { + mContext.enforceCallingOrSelfPermission( + Manifest.permission.MODIFY_REFRESH_RATE_SWITCHING_TYPE, + "Permission required to modify refresh rate switching type."); + final long token = Binder.clearCallingIdentity(); + try { + setRefreshRateSwitchingTypeInternal(newValue); + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override // Binder call + public int getRefreshRateSwitchingType() { + mContext.enforceCallingOrSelfPermission( + Manifest.permission.MODIFY_REFRESH_RATE_SWITCHING_TYPE, + "Permission required read refresh rate switching type."); + final long token = Binder.clearCallingIdentity(); + try { + return getRefreshRateSwitchingTypeInternal(); + } finally { + Binder.restoreCallingIdentity(token); + } + } + private boolean validatePackageName(int uid, String packageName) { if (packageName != null) { String[] packageNames = mContext.getPackageManager().getPackagesForUid(uid); diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java index 329081a8391f..95918945eecd 100644 --- a/services/core/java/com/android/server/display/DisplayModeDirector.java +++ b/services/core/java/com/android/server/display/DisplayModeDirector.java @@ -16,7 +16,6 @@ package com.android.server.display; -import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.ContentResolver; @@ -53,8 +52,6 @@ import com.android.server.display.utils.AmbientFilterFactory; import com.android.server.utils.DeviceConfigInterface; import java.io.PrintWriter; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -110,28 +107,11 @@ public class DisplayModeDirector { private boolean mAlwaysRespectAppRequest; - @IntDef(prefix = {"SWITCHING_TYPE_"}, value = { - SWITCHING_TYPE_NONE, - SWITCHING_TYPE_WITHIN_GROUPS, - SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS, - }) - @Retention(RetentionPolicy.SOURCE) - public @interface SwitchingType {} - - // No mode switching will happen. - public static final int SWITCHING_TYPE_NONE = 0; - // Allow only refresh rate switching between modes in the same configuration group. This way - // only switches without visual interruptions for the user will be allowed. - public static final int SWITCHING_TYPE_WITHIN_GROUPS = 1; - // Allow refresh rate switching between all refresh rates even if the switch with have visual - // interruptions for the user. - public static final int SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS = 2; - /** * The allowed refresh rate switching type. This is used by SurfaceFlinger. */ - @SwitchingType - private int mModeSwitchingType = SWITCHING_TYPE_WITHIN_GROUPS; + @DisplayManager.SwitchingType + private int mModeSwitchingType = DisplayManager.SWITCHING_TYPE_WITHIN_GROUPS; public DisplayModeDirector(@NonNull Context context, @NonNull Handler handler) { this(context, handler, new RealInjector()); @@ -337,7 +317,7 @@ public class DisplayModeDirector { if (availableModes.length > 0) { baseModeId = availableModes[0]; } - if (mModeSwitchingType == SWITCHING_TYPE_NONE) { + if (mModeSwitchingType == DisplayManager.SWITCHING_TYPE_NONE) { Display.Mode baseMode = null; for (Display.Mode mode : modes) { if (mode.getModeId() == baseModeId) { @@ -359,7 +339,7 @@ public class DisplayModeDirector { } boolean allowGroupSwitching = - mModeSwitchingType == SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS; + mModeSwitchingType == DisplayManager.SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS; return new DesiredDisplayModeSpecs(baseModeId, allowGroupSwitching, new RefreshRateRange( @@ -450,18 +430,21 @@ public class DisplayModeDirector { /** * Sets the display mode switching type. - * @param type + * @param newType */ - public void setModeSwitchingType(@SwitchingType int type) { + public void setModeSwitchingType(@DisplayManager.SwitchingType int newType) { synchronized (mLock) { - mModeSwitchingType = type; + if (newType != mModeSwitchingType) { + mModeSwitchingType = newType; + notifyDesiredDisplayModeSpecsChangedLocked(); + } } } /** * Returns the display mode switching type. */ - @SwitchingType + @DisplayManager.SwitchingType public int getModeSwitchingType() { synchronized (mLock) { return mModeSwitchingType; @@ -583,13 +566,13 @@ public class DisplayModeDirector { } } - private static String switchingTypeToString(@SwitchingType int type) { + private static String switchingTypeToString(@DisplayManager.SwitchingType int type) { switch (type) { - case SWITCHING_TYPE_NONE: + case DisplayManager.SWITCHING_TYPE_NONE: return "SWITCHING_TYPE_NONE"; - case SWITCHING_TYPE_WITHIN_GROUPS: + case DisplayManager.SWITCHING_TYPE_WITHIN_GROUPS: return "SWITCHING_TYPE_WITHIN_GROUPS"; - case SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS: + case DisplayManager.SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS: return "SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS"; default: return "Unknown SwitchingType " + type; diff --git a/services/core/java/com/android/server/graphics/fonts/FontManagerInternal.java b/services/core/java/com/android/server/graphics/fonts/FontManagerInternal.java new file mode 100644 index 000000000000..e4b7b03a8e07 --- /dev/null +++ b/services/core/java/com/android/server/graphics/fonts/FontManagerInternal.java @@ -0,0 +1,27 @@ +/* + * 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.server.graphics.fonts; + +import android.annotation.Nullable; +import android.os.SharedMemory; + +/** Local interface for {@link FontManagerService}. */ +public interface FontManagerInternal { + + /** Returns a SharedMemory in which the system font map is serialized. */ + @Nullable SharedMemory getSerializedSystemFontMap(); +} diff --git a/services/core/java/com/android/server/graphics/fonts/FontManagerService.java b/services/core/java/com/android/server/graphics/fonts/FontManagerService.java new file mode 100644 index 000000000000..521ce6973522 --- /dev/null +++ b/services/core/java/com/android/server/graphics/fonts/FontManagerService.java @@ -0,0 +1,88 @@ +/* + * 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.server.graphics.fonts; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.Context; +import android.graphics.Typeface; +import android.os.SharedMemory; +import android.system.ErrnoException; +import android.util.Slog; + +import com.android.internal.annotations.GuardedBy; +import com.android.server.LocalServices; +import com.android.server.SystemService; + +import java.io.IOException; + +/** A service for managing system fonts. */ +// TODO(b/173619554): Add API to update fonts. +public final class FontManagerService { + + private static final String TAG = "FontManagerService"; + + /** Class to manage FontManagerService's lifecycle. */ + public static final class Lifecycle extends SystemService { + private final FontManagerService mService; + + public Lifecycle(@NonNull Context context) { + super(context); + mService = new FontManagerService(); + } + + @Override + public void onStart() { + LocalServices.addService(FontManagerInternal.class, + new FontManagerInternal() { + @Override + @Nullable + public SharedMemory getSerializedSystemFontMap() { + return mService.getSerializedSystemFontMap(); + } + }); + } + } + + @GuardedBy("this") + @Nullable + private SharedMemory mSerializedSystemFontMap = null; + + @Nullable + private SharedMemory getSerializedSystemFontMap() { + if (!Typeface.ENABLE_LAZY_TYPEFACE_INITIALIZATION) { + return null; + } + synchronized (FontManagerService.this) { + if (mSerializedSystemFontMap == null) { + mSerializedSystemFontMap = createSerializedSystemFontMapLocked(); + } + return mSerializedSystemFontMap; + } + } + + @Nullable + private SharedMemory createSerializedSystemFontMapLocked() { + // TODO(b/173619554): use updated fonts. + try { + return Typeface.serializeFontMap(Typeface.getSystemFontMap()); + } catch (IOException | ErrnoException e) { + Slog.e(TAG, "Failed to serialize SystemServer system font map", e); + } + return null; + } +} diff --git a/services/core/java/com/android/server/hdmi/CecMessageBuffer.java b/services/core/java/com/android/server/hdmi/CecMessageBuffer.java new file mode 100644 index 000000000000..8f971fd7db07 --- /dev/null +++ b/services/core/java/com/android/server/hdmi/CecMessageBuffer.java @@ -0,0 +1,103 @@ +/* + * 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.server.hdmi; + +import java.util.ArrayList; +import java.util.List; + +/** + * Buffer for processing the incoming CEC messages while allocating logical addresses. + */ +final class CecMessageBuffer { + private List<HdmiCecMessage> mBuffer = new ArrayList<>(); + private HdmiControlService mHdmiControlService; + + CecMessageBuffer(HdmiControlService hdmiControlService) { + mHdmiControlService = hdmiControlService; + } + + /** + * Adds a message to the buffer. + * Only certain types of messages need to be buffered. + * @param message The message to add to the buffer + * @return Whether the message was added to the buffer + */ + public boolean bufferMessage(HdmiCecMessage message) { + switch (message.getOpcode()) { + case Constants.MESSAGE_ACTIVE_SOURCE: + bufferActiveSource(message); + return true; + case Constants.MESSAGE_IMAGE_VIEW_ON: + case Constants.MESSAGE_TEXT_VIEW_ON: + bufferImageOrTextViewOn(message); + return true; + case Constants.MESSAGE_SYSTEM_AUDIO_MODE_REQUEST: + bufferSystemAudioModeRequest(message); + return true; + // Add here if new message that needs to buffer + default: + // Do not need to buffer messages other than above + return false; + } + } + + /** + * Process all messages in the buffer. + */ + public void processMessages() { + for (final HdmiCecMessage message : mBuffer) { + mHdmiControlService.runOnServiceThread(new Runnable() { + @Override + public void run() { + mHdmiControlService.handleCecCommand(message); + } + }); + } + mBuffer.clear(); + } + + private void bufferActiveSource(HdmiCecMessage message) { + if (!replaceMessageIfBuffered(message, Constants.MESSAGE_ACTIVE_SOURCE)) { + mBuffer.add(message); + } + } + + private void bufferImageOrTextViewOn(HdmiCecMessage message) { + if (!replaceMessageIfBuffered(message, Constants.MESSAGE_IMAGE_VIEW_ON) + && !replaceMessageIfBuffered(message, Constants.MESSAGE_TEXT_VIEW_ON)) { + mBuffer.add(message); + } + } + + private void bufferSystemAudioModeRequest(HdmiCecMessage message) { + if (!replaceMessageIfBuffered(message, Constants.MESSAGE_SYSTEM_AUDIO_MODE_REQUEST)) { + mBuffer.add(message); + } + } + + // Returns true if the message is replaced + private boolean replaceMessageIfBuffered(HdmiCecMessage message, int opcode) { + for (int i = 0; i < mBuffer.size(); i++) { + HdmiCecMessage bufferedMessage = mBuffer.get(i); + if (bufferedMessage.getOpcode() == opcode) { + mBuffer.set(i, message); + return true; + } + } + return false; + } +} diff --git a/services/core/java/com/android/server/hdmi/HdmiCecConfig.java b/services/core/java/com/android/server/hdmi/HdmiCecConfig.java index 2374ece1dd65..a261fa1f2741 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecConfig.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecConfig.java @@ -22,19 +22,12 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.StringDef; -import android.content.ContentResolver; import android.content.Context; import android.content.SharedPreferences; -import android.database.ContentObserver; import android.hardware.hdmi.HdmiControlManager; -import android.net.Uri; import android.os.Environment; -import android.os.Handler; -import android.os.Looper; import android.os.SystemProperties; -import android.os.UserHandle; import android.provider.Settings.Global; -import android.util.ArrayMap; import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; @@ -95,23 +88,6 @@ public class HdmiCecConfig { @Nullable private final CecSettings mProductConfig; @Nullable private final CecSettings mVendorOverride; - private final ArrayMap<Setting, Set<SettingChangeListener>> - mSettingChangeListeners = new ArrayMap<>(); - - private SettingsObserver mSettingsObserver; - - /** - * Listener used to get notifications when value of a setting changes. - */ - public interface SettingChangeListener { - /** - * Called when value of a setting changes. - * - * @param setting name of a CEC setting that changed - */ - void onChange(@NonNull @CecSettingName String setting); - } - /** * Setting storage input/output helper class. */ @@ -183,18 +159,6 @@ public class HdmiCecConfig { } } - private class SettingsObserver extends ContentObserver { - SettingsObserver(Handler handler) { - super(handler); - } - - @Override - public void onChange(boolean selfChange, Uri uri) { - String setting = uri.getLastPathSegment(); - HdmiCecConfig.this.notifyGlobalSettingChanged(setting); - } - } - @VisibleForTesting HdmiCecConfig(@NonNull Context context, @NonNull StorageAdapter storageAdapter, @@ -347,7 +311,6 @@ public class HdmiCecConfig { } else if (storage == STORAGE_SHARED_PREFS) { Slog.d(TAG, "Setting '" + storageKey + "' shared pref."); mStorageAdapter.storeSharedPref(storageKey, value); - notifySettingChanged(setting); } } @@ -355,103 +318,6 @@ public class HdmiCecConfig { return Integer.decode(value.getIntValue()); } - private void notifyGlobalSettingChanged(String setting) { - switch (setting) { - case Global.HDMI_CONTROL_ENABLED: - notifySettingChanged(HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED); - break; - case Global.HDMI_CEC_VERSION: - notifySettingChanged(HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION); - break; - case Global.HDMI_CONTROL_SEND_STANDBY_ON_SLEEP: - notifySettingChanged(HdmiControlManager.CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP); - break; - } - } - - private void notifySettingChanged(@NonNull @CecSettingName String name) { - Setting setting = getSetting(name); - if (setting == null) { - throw new IllegalArgumentException("Setting '" + name + "' does not exist."); - } - notifySettingChanged(setting); - } - - private void notifySettingChanged(@NonNull Setting setting) { - Set<SettingChangeListener> listeners = mSettingChangeListeners.get(setting); - if (listeners == null) { - return; // No listeners registered, do nothing. - } - for (SettingChangeListener listener: listeners) { - listener.onChange(setting.getName()); - } - } - - /** - * This method registers Global Setting change observer. - * Needs to be called once after initialization of HdmiCecConfig. - */ - public void registerGlobalSettingsObserver(Looper looper) { - Handler handler = new Handler(looper); - mSettingsObserver = new SettingsObserver(handler); - ContentResolver resolver = mContext.getContentResolver(); - String[] settings = new String[] { - Global.HDMI_CONTROL_ENABLED, - Global.HDMI_CEC_VERSION, - Global.HDMI_CONTROL_SEND_STANDBY_ON_SLEEP, - }; - for (String setting: settings) { - resolver.registerContentObserver(Global.getUriFor(setting), false, - mSettingsObserver, UserHandle.USER_ALL); - } - } - - /** - * This method unregisters Global Setting change observer. - */ - public void unregisterGlobalSettingsObserver() { - ContentResolver resolver = mContext.getContentResolver(); - resolver.unregisterContentObserver(mSettingsObserver); - } - - /** - * Register change listener for a given setting name. - */ - public void registerChangeListener(@NonNull @CecSettingName String name, - SettingChangeListener listener) { - Setting setting = getSetting(name); - if (setting == null) { - throw new IllegalArgumentException("Setting '" + name + "' does not exist."); - } - @Storage int storage = getStorage(setting); - if (storage != STORAGE_GLOBAL_SETTINGS && storage != STORAGE_SHARED_PREFS) { - throw new IllegalArgumentException("Change listeners for setting '" + name - + "' not supported."); - } - if (!mSettingChangeListeners.containsKey(setting)) { - mSettingChangeListeners.put(setting, new HashSet<>()); - } - mSettingChangeListeners.get(setting).add(listener); - } - - /** - * Remove change listener for a given setting name. - */ - public void removeChangeListener(@NonNull @CecSettingName String name, - SettingChangeListener listener) { - Setting setting = getSetting(name); - if (setting == null) { - throw new IllegalArgumentException("Setting '" + name + "' does not exist."); - } - if (mSettingChangeListeners.containsKey(setting)) { - Set<SettingChangeListener> listeners = mSettingChangeListeners.get(setting); - listeners.remove(listener); - if (listeners.isEmpty()) { - mSettingChangeListeners.remove(setting); - } - } - } - /** * Returns a list of all settings based on the XML metadata. */ diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java index fd825d6f6288..56b73ba04d89 100644 --- a/services/core/java/com/android/server/hdmi/HdmiControlService.java +++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java @@ -405,74 +405,7 @@ public class HdmiControlService extends SystemService { // Use getAtomWriter() instead of accessing directly, to allow dependency injection for testing. private HdmiCecAtomWriter mAtomWriter = new HdmiCecAtomWriter(); - // Buffer for processing the incoming cec messages while allocating logical addresses. - private final class CecMessageBuffer { - private List<HdmiCecMessage> mBuffer = new ArrayList<>(); - - public boolean bufferMessage(HdmiCecMessage message) { - switch (message.getOpcode()) { - case Constants.MESSAGE_ACTIVE_SOURCE: - bufferActiveSource(message); - return true; - case Constants.MESSAGE_IMAGE_VIEW_ON: - case Constants.MESSAGE_TEXT_VIEW_ON: - bufferImageOrTextViewOn(message); - return true; - case Constants.MESSAGE_SYSTEM_AUDIO_MODE_REQUEST: - bufferSystemAudioModeRequest(message); - return true; - // Add here if new message that needs to buffer - default: - // Do not need to buffer messages other than above - return false; - } - } - - public void processMessages() { - for (final HdmiCecMessage message : mBuffer) { - runOnServiceThread(new Runnable() { - @Override - public void run() { - handleCecCommand(message); - } - }); - } - mBuffer.clear(); - } - - private void bufferActiveSource(HdmiCecMessage message) { - if (!replaceMessageIfBuffered(message, Constants.MESSAGE_ACTIVE_SOURCE)) { - mBuffer.add(message); - } - } - - private void bufferImageOrTextViewOn(HdmiCecMessage message) { - if (!replaceMessageIfBuffered(message, Constants.MESSAGE_IMAGE_VIEW_ON) && - !replaceMessageIfBuffered(message, Constants.MESSAGE_TEXT_VIEW_ON)) { - mBuffer.add(message); - } - } - - private void bufferSystemAudioModeRequest(HdmiCecMessage message) { - if (!replaceMessageIfBuffered(message, Constants.MESSAGE_SYSTEM_AUDIO_MODE_REQUEST)) { - mBuffer.add(message); - } - } - - // Returns true if the message is replaced - private boolean replaceMessageIfBuffered(HdmiCecMessage message, int opcode) { - for (int i = 0; i < mBuffer.size(); i++) { - HdmiCecMessage bufferedMessage = mBuffer.get(i); - if (bufferedMessage.getOpcode() == opcode) { - mBuffer.set(i, message); - return true; - } - } - return false; - } - } - - private final CecMessageBuffer mCecMessageBuffer = new CecMessageBuffer(); + private CecMessageBuffer mCecMessageBuffer = new CecMessageBuffer(this); private final SelectRequestBuffer mSelectRequestBuffer = new SelectRequestBuffer(); @@ -563,7 +496,6 @@ public class HdmiControlService extends SystemService { if (mMessageValidator == null) { mMessageValidator = new HdmiCecMessageValidator(this); } - mHdmiCecConfig.registerGlobalSettingsObserver(mIoLooper); } private void bootCompleted() { @@ -988,6 +920,11 @@ public class HdmiControlService extends SystemService { mMessageValidator = messageValidator; } + @VisibleForTesting + void setCecMessageBuffer(CecMessageBuffer cecMessageBuffer) { + this.mCecMessageBuffer = cecMessageBuffer; + } + /** * Returns {@link Looper} of main thread. Use this {@link Looper} instance * for tasks that are running on main service thread. diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java index f2eb5af51616..42aad7d7ad5c 100644 --- a/services/core/java/com/android/server/input/InputManagerService.java +++ b/services/core/java/com/android/server/input/InputManagerService.java @@ -95,6 +95,7 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; import com.android.internal.notification.SystemNotificationChannels; import com.android.internal.os.SomeArgs; +import com.android.internal.util.ArrayUtils; import com.android.internal.util.DumpUtils; import com.android.internal.util.XmlUtils; import com.android.server.DisplayThread; @@ -146,6 +147,16 @@ public class InputManagerService extends IInputManager.Stub private static final int DEFAULT_VIBRATION_MAGNITUDE = 192; + /** + * We know the issue and are working to fix it, so suppressing the toast to not annoy + * dogfooders. + * + * TODO(b/169067926): Remove this + */ + private static final String[] PACKAGE_BLOCKLIST_FOR_UNTRUSTED_TOUCHES_TOAST = { + "com.snapchat.android" // b/173297887 + }; + // Pointer to native input manager service object. private final long mPtr; @@ -2091,6 +2102,10 @@ public class InputManagerService extends IInputManager.Stub // Native callback private void notifyUntrustedTouch(String packageName) { // TODO(b/169067926): Remove toast after gathering feedback on dogfood. + if (ArrayUtils.contains(PACKAGE_BLOCKLIST_FOR_UNTRUSTED_TOUCHES_TOAST, packageName)) { + Log.i(TAG, "Suppressing untrusted touch toast for " + packageName); + return; + } DisplayThread.getHandler().post(() -> Toast.makeText(mContext, "Touch obscured by " + packageName diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 76b9c8619cfc..ee860e3ac6d7 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -230,9 +230,9 @@ import android.util.Pair; import android.util.Slog; import android.util.SparseArray; import android.util.StatsEvent; -import android.util.Xml; import android.util.TypedXmlPullParser; import android.util.TypedXmlSerializer; +import android.util.Xml; import android.util.proto.ProtoOutputStream; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityManager; @@ -257,7 +257,6 @@ import com.android.internal.util.ArrayUtils; import com.android.internal.util.CollectionUtils; import com.android.internal.util.ConcurrentUtils; import com.android.internal.util.DumpUtils; -import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.Preconditions; import com.android.internal.util.XmlUtils; import com.android.internal.util.function.TriPredicate; @@ -286,9 +285,7 @@ import libcore.io.IoUtils; import org.json.JSONException; import org.json.JSONObject; -import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; @@ -737,7 +734,7 @@ public class NotificationManagerService extends SystemService { null, MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE, userId); if (candidate != null && validAssistants.contains(candidate)) { - setNotificationAssistantAccessGrantedForUserInternal(candidate, userId, true); + setNotificationAssistantAccessGrantedForUserInternal(candidate, userId, true, false); return true; } return false; @@ -4913,7 +4910,8 @@ public class NotificationManagerService extends SystemService { } final long identity = Binder.clearCallingIdentity(); try { - setNotificationAssistantAccessGrantedForUserInternal(assistant, userId, granted); + setNotificationAssistantAccessGrantedForUserInternal(assistant, userId, granted, + true); } finally { Binder.restoreCallingIdentity(identity); } @@ -5164,7 +5162,7 @@ public class NotificationManagerService extends SystemService { @VisibleForTesting protected void setNotificationAssistantAccessGrantedForUserInternal( - ComponentName assistant, int baseUserId, boolean granted) { + ComponentName assistant, int baseUserId, boolean granted, boolean userSet) { List<UserInfo> users = mUm.getEnabledProfiles(baseUserId); if (users != null) { for (UserInfo user : users) { @@ -5174,7 +5172,7 @@ public class NotificationManagerService extends SystemService { mAssistants.getAllowedComponents(userId)); if (allowedAssistant != null) { setNotificationAssistantAccessGrantedForUserInternal( - allowedAssistant, userId, false); + allowedAssistant, userId, false, userSet); } continue; } @@ -5183,7 +5181,7 @@ public class NotificationManagerService extends SystemService { mConditionProviders.setPackageOrComponentEnabled(assistant.flattenToString(), userId, false, granted); mAssistants.setPackageOrComponentEnabled(assistant.flattenToString(), - userId, true, granted); + userId, true, granted, userSet); getContext().sendBroadcastAsUser( new Intent(ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED) @@ -9338,7 +9336,7 @@ public class NotificationManagerService extends SystemService { @Override protected void setPackageOrComponentEnabled(String pkgOrComponent, int userId, - boolean isPrimary, boolean enabled) { + boolean isPrimary, boolean enabled, boolean userSet) { // Ensures that only one component is enabled at a time if (enabled) { List<ComponentName> allowedComponents = getAllowedComponents(userId); @@ -9346,10 +9344,10 @@ public class NotificationManagerService extends SystemService { ComponentName currentComponent = CollectionUtils.firstOrNull(allowedComponents); if (currentComponent.flattenToString().equals(pkgOrComponent)) return; setNotificationAssistantAccessGrantedForUserInternal( - currentComponent, userId, false); + currentComponent, userId, false, userSet); } } - super.setPackageOrComponentEnabled(pkgOrComponent, userId, isPrimary, enabled); + super.setPackageOrComponentEnabled(pkgOrComponent, userId, isPrimary, enabled, userSet); } private boolean isVerboseLogEnabled() { diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 85659edd1321..8fc9cb725859 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -1738,6 +1738,7 @@ public class PackageManagerService extends IPackageManager.Stub final @Nullable String mOverlayConfigSignaturePackage; final @Nullable String mRecentsPackage; + @GuardedBy("mLock") private final PackageUsage mPackageUsage = new PackageUsage(); private final CompilerStats mCompilerStats = new CompilerStats(); @@ -2210,22 +2211,27 @@ public class PackageManagerService extends IPackageManager.Stub res.removedInfo.sendPackageRemovedBroadcasts(killApp, false /*removedBySystem*/); } + final PermissionManagerServiceInternal.PackageInstalledParams.Builder + permissionParamsBuilder = + new PermissionManagerServiceInternal.PackageInstalledParams.Builder(); final List<String> grantedPermissionsList; if (grantPermissions) { if (grantedPermissions != null) { - grantedPermissionsList = Arrays.asList(grantedPermissions); + permissionParamsBuilder.setGrantedPermissions(Arrays.asList( + grantedPermissions)); } else { - grantedPermissionsList = res.pkg.getRequestedPermissions(); + permissionParamsBuilder.setGrantedPermissions( + res.pkg.getRequestedPermissions()); } - } else { - grantedPermissionsList = Collections.emptyList(); } - if (allowlistedRestrictedPermissions == null) { - allowlistedRestrictedPermissions = Collections.emptyList(); + if (allowlistedRestrictedPermissions != null) { + permissionParamsBuilder.setAllowlistedRestrictedPermissions( + allowlistedRestrictedPermissions); } + permissionParamsBuilder.setAutoRevokePermissionsMode(autoRevokePermissionsMode); for (final int userId : res.newUsers) { - mPermissionManager.onPackageInstalled(res.pkg, grantedPermissionsList, - allowlistedRestrictedPermissions, autoRevokePermissionsMode, userId); + mPermissionManager.onPackageInstalled(res.pkg, permissionParamsBuilder.build(), + userId); } final String installerPackageName = @@ -3312,6 +3318,7 @@ public class PackageManagerService extends IPackageManager.Stub // Stub packages must either be replaced with full versions in the /data // partition or be disabled. final List<String> stubSystemApps = new ArrayList<>(); + final int[] userIds = mUserManager.getUserIds(); if (!mOnlyCore) { // do this first before mucking with mPackages for the "expecting better" case final int numPackages = mPackages.size(); @@ -3362,15 +3369,9 @@ public class PackageManagerService extends IPackageManager.Stub } if (!mSettings.isDisabledSystemPackageLPr(ps.name)) { - mSettings.mPackages.removeAt(index); logCriticalInfo(Log.WARN, "System package " + ps.name + " no longer exists; it's data will be wiped"); - - // Assume package is truly gone and wipe residual permissions. - mPermissionManager.updatePermissions(ps.name, null); - - // Actual deletion of code and data will be handled by later - // reconciliation step + removePackageDataLIF(ps, userIds, null, 0, false); } else { // we still have a disabled system package, but, it still might have // been removed. check the code path still exists and check there's @@ -3429,7 +3430,6 @@ public class PackageManagerService extends IPackageManager.Stub // Remove disable package settings for updated system apps that were // removed via an OTA. If the update is no longer present, remove the // app completely. Otherwise, revoke their system privileges. - final int[] userIds = mUserManager.getUserIds(); for (int i = possiblyDeletedUpdatedSystemApps.size() - 1; i >= 0; --i) { final String packageName = possiblyDeletedUpdatedSystemApps.get(i); final AndroidPackage pkg = mPackages.get(packageName); @@ -3764,7 +3764,6 @@ public class PackageManagerService extends IPackageManager.Stub UserHandle.USER_SYSTEM).getLongVersionCode()); // Initialize InstantAppRegistry's Instant App list for all users. - final int[] userIds = UserManagerService.getInstance().getUserIds(); for (AndroidPackage pkg : mPackages.values()) { if (pkg.isSystem()) { continue; @@ -3930,7 +3929,12 @@ public class PackageManagerService extends IPackageManager.Stub } catch (PackageManagerException e) { Slog.e(TAG, "updateAllSharedLibrariesLPw failed: ", e); } - mPermissionManager.updatePermissions(pkg.getPackageName(), pkg); + final int[] userIds = mUserManager.getUserIds(); + for (final int userId : userIds) { + mPermissionManager.onPackageInstalled(pkg, + PermissionManagerServiceInternal.PackageInstalledParams.DEFAULT, + userId); + } writeSettingsLPrTEMP(); } } catch (PackageManagerException e) { @@ -10630,13 +10634,14 @@ public class PackageManagerService extends IPackageManager.Stub } public void shutdown() { - mPackageUsage.writeNow(mSettings.mPackages); mCompilerStats.writeNow(); mDexManager.writePackageDexUsageNow(); PackageWatchdog.getInstance(mContext).writeNow(); - // This is the last chance to write out pending restriction settings synchronized (mLock) { + mPackageUsage.writeNow(mSettings.mPackages); + + // This is the last chance to write out pending restriction settings if (mHandler.hasMessages(WRITE_PACKAGE_RESTRICTIONS)) { mHandler.removeMessages(WRITE_PACKAGE_RESTRICTIONS); for (int userId : mDirtyUsers) { @@ -13671,14 +13676,16 @@ public class PackageManagerService extends IPackageManager.Stub if (installed) { if (pkgSetting.pkg != null) { + final PermissionManagerServiceInternal.PackageInstalledParams.Builder + permissionParamsBuilder = + new PermissionManagerServiceInternal.PackageInstalledParams.Builder(); if ((installFlags & PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS) != 0) { - allowlistedRestrictedPermissions = pkgSetting.pkg.getRequestedPermissions(); - } else if (allowlistedRestrictedPermissions == null) { - allowlistedRestrictedPermissions = Collections.emptyList(); + permissionParamsBuilder.setAllowlistedRestrictedPermissions( + pkgSetting.pkg.getRequestedPermissions()); } - mPermissionManager.onPackageInstalled(pkgSetting.pkg, Collections.emptyList(), - allowlistedRestrictedPermissions, MODE_DEFAULT, userId); + mPermissionManager.onPackageInstalled(pkgSetting.pkg, + permissionParamsBuilder.build(), userId); } if (pkgSetting.pkg != null) { @@ -13937,7 +13944,7 @@ public class PackageManagerService extends IPackageManager.Stub synchronized (mLock) { final PackageSetting ps = mSettings.mPackages.get(packageName); if (ps == null) { - throw new IllegalArgumentException("Unknown target package: " + packageName); + return null; } final PackageUserState pus = ps.readUserState(userId); final Bundle allExtras = new Bundle(); @@ -16480,8 +16487,6 @@ public class PackageManagerService extends IPackageManager.Stub if (DEBUG_INSTALL) Slog.d(TAG, "New package installed in " + pkg.getPath()); synchronized (mLock) { -// NOTE: This changes slightly to include UPDATE_PERMISSIONS_ALL regardless of the size of pkg.permissions - mPermissionManager.updatePermissions(pkgName, pkg); // For system-bundled packages, we assume that installing an upgraded version // of the package implies that the user actually wants to run that new code, // so we enable the package. @@ -19501,13 +19506,20 @@ public class PackageManagerService extends IPackageManager.Stub if (outInfo != null) { outInfo.removedAppId = removedAppId; } - final SharedUserSetting sus = deletedPs.getSharedUser(); - List<AndroidPackage> sharedUserPkgs = sus != null ? sus.getPackages() : null; - if (sharedUserPkgs == null) { - sharedUserPkgs = Collections.emptyList(); + if (!mSettings.isDisabledSystemPackageLPr(packageName)) { + // If we don't have a disabled system package to reinstall, the package is + // really gone and its permission state should be removed. + final SharedUserSetting sus = deletedPs.getSharedUser(); + List<AndroidPackage> sharedUserPkgs = sus != null ? sus.getPackages() + : null; + if (sharedUserPkgs == null) { + sharedUserPkgs = Collections.emptyList(); + } + for (final int userId : allUserHandles) { + mPermissionManager.onPackageUninstalled(packageName, deletedPs.appId, + deletedPs.pkg, sharedUserPkgs, userId); + } } - mPermissionManager.onPackageStateRemoved(packageName, deletedPs.appId, - deletedPs.pkg, sharedUserPkgs); clearPackagePreferredActivitiesLPw( deletedPs.name, changedUsers, UserHandle.USER_ALL); } @@ -19696,10 +19708,6 @@ public class PackageManagerService extends IPackageManager.Stub synchronized (mLock) { PackageSetting ps = mSettings.mPackages.get(pkg.getPackageName()); - // The update permissions method below will take care of removing obsolete permissions - // and granting install permissions. - mPermissionManager.updatePermissions(pkg.getPackageName(), pkg); - final boolean applyUserRestrictions = origUserHandles != null; if (applyUserRestrictions) { boolean installedStateChanged = false; @@ -19718,8 +19726,6 @@ public class PackageManagerService extends IPackageManager.Stub if (installed) { ps.setUninstallReason(UNINSTALL_REASON_UNKNOWN, userId); } - - mSettings.writeRuntimePermissionsForUserLPr(userId, false); } // Regardless of writeSettings we need to ensure that this restriction // state propagation is persisted @@ -19728,6 +19734,17 @@ public class PackageManagerService extends IPackageManager.Stub mSettings.writeKernelMappingLPr(ps); } } + + for (final int userId : allUserHandles) { + // The method below will take care of removing obsolete permissions and granting + // install permissions. + mPermissionManager.onPackageInstalled(pkg, + PermissionManagerServiceInternal.PackageInstalledParams.DEFAULT, userId); + if (applyUserRestrictions) { + mSettings.writeRuntimePermissionsForUserLPr(userId, false); + } + } + // can downgrade to reader here if (writeSettings) { writeSettingsLPrTEMP(); @@ -20026,6 +20043,11 @@ public class PackageManagerService extends IPackageManager.Stub destroyAppProfilesLIF(pkg); + final SharedUserSetting sus = ps.getSharedUser(); + List<AndroidPackage> sharedUserPkgs = sus != null ? sus.getPackages() : null; + if (sharedUserPkgs == null) { + sharedUserPkgs = Collections.emptyList(); + } final int[] userIds = (userId == UserHandle.USER_ALL) ? mUserManager.getUserIds() : new int[] {userId}; for (int nextUserId : userIds) { @@ -20040,7 +20062,8 @@ public class PackageManagerService extends IPackageManager.Stub clearDefaultBrowserIfNeededForUser(ps.name, nextUserId); removeKeystoreDataIfNeeded(mInjector.getUserManagerInternal(), nextUserId, ps.appId); clearPackagePreferredActivities(ps.name, nextUserId); - mPermissionManager.resetRuntimePermissions(pkg, nextUserId); + mPermissionManager.onPackageUninstalled(ps.name, ps.appId, pkg, sharedUserPkgs, + nextUserId); } if (outInfo != null) { @@ -23562,7 +23585,7 @@ public class PackageManagerService extends IPackageManager.Stub // Note: this code block is executed with the Installer lock // already held, since it's invoked as a side-effect of // executeBatchLI() - if ((e != null) && pkg.isSystem()) { + if (e != null) { logCriticalInfo(Log.ERROR, "Failed to create app data for " + packageName + ", but trying to recover: " + e); destroyAppDataLeafLIF(pkg, userId, flags); @@ -26032,7 +26055,9 @@ public class PackageManagerService extends IPackageManager.Stub } boolean isHistoricalPackageUsageAvailable() { - return mPackageUsage.isHistoricalPackageUsageAvailable(); + synchronized (mLock) { + return mPackageUsage.isHistoricalPackageUsageAvailable(); + } } /** diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java index 9aa1a621a760..9720819a62ab 100644 --- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java +++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java @@ -146,7 +146,7 @@ class PackageManagerShellCommand extends ShellCommand { final IPackageManager mInterface; final IPermissionManager mPermissionManager; - final Context mShellPackageContext; + final Context mContext; final private WeakHashMap<String, Resources> mResourceCache = new WeakHashMap<String, Resources>(); int mTargetUser; @@ -158,12 +158,7 @@ class PackageManagerShellCommand extends ShellCommand { PackageManagerService service, IPermissionManager permissionManager, Context context) { mInterface = service; mPermissionManager = permissionManager; - try { - mShellPackageContext = context.createPackageContext("com.android.shell", 0); - } catch (NameNotFoundException e) { - // should not happen - throw new RuntimeException(e); - } + mContext = context; } @Override @@ -486,8 +481,17 @@ class PackageManagerShellCommand extends ShellCommand { return 1; } + final Context shellPackageContext; + try { + shellPackageContext = mContext.createPackageContextAsUser( + "com.android.shell", 0, Binder.getCallingUserHandle()); + } catch (NameNotFoundException e) { + // should not happen + throw new RuntimeException(e); + } + final LocalIntentReceiver receiver = new LocalIntentReceiver(); - RollbackManager rm = mShellPackageContext.getSystemService(RollbackManager.class); + RollbackManager rm = shellPackageContext.getSystemService(RollbackManager.class); RollbackInfo rollback = null; for (RollbackInfo r : rm.getAvailableRollbacks()) { for (PackageRollbackInfo info : r.getPackages()) { diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java index 52bb3d772387..48e18f1b8b38 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java @@ -154,7 +154,6 @@ import com.android.server.pm.parsing.pkg.AndroidPackage; import com.android.server.pm.permission.PermissionManagerServiceInternal.DefaultBrowserProvider; import com.android.server.pm.permission.PermissionManagerServiceInternal.DefaultDialerProvider; import com.android.server.pm.permission.PermissionManagerServiceInternal.DefaultHomeProvider; -import com.android.server.pm.permission.PermissionManagerServiceInternal.PermissionCallback; import com.android.server.policy.PermissionPolicyInternal; import com.android.server.policy.SoftRestrictedPermissionPolicy; @@ -4139,17 +4138,14 @@ public class PermissionManagerService extends IPermissionManager.Stub { * * @param packageName The package that is updated * @param pkg The package that is updated, or {@code null} if package is deleted - * @param allPackages All currently known packages - * @param callback Callback to call after permission changes */ - private void updatePermissions(@NonNull String packageName, @Nullable AndroidPackage pkg, - @NonNull PermissionCallback callback) { + private void updatePermissions(@NonNull String packageName, @Nullable AndroidPackage pkg) { // If the package is being deleted, update the permissions of all the apps final int flags = (pkg == null ? UPDATE_PERMISSIONS_ALL | UPDATE_PERMISSIONS_REPLACE_PKG : UPDATE_PERMISSIONS_REPLACE_PKG); updatePermissions( - packageName, pkg, getVolumeUuidForPackage(pkg), flags, callback); + packageName, pkg, getVolumeUuidForPackage(pkg), flags, mDefaultPermissionCallback); } /** @@ -4712,13 +4708,13 @@ public class PermissionManagerService extends IPermissionManager.Stub { return userState.getUidState(appId); } - private void removeAppIdState(@AppIdInt int appId) { + private void removeUidState(@AppIdInt int appId, @UserIdInt int userId) { synchronized (mLock) { - final int[] userIds = mState.getUserIds(); - for (final int userId : userIds) { - final UserPermissionState userState = mState.getUserState(userId); - userState.removeUidState(appId); + final UserPermissionState userState = mState.getUserState(userId); + if (userState == null) { + return; } + userState.removeUidState(appId); } } @@ -4947,17 +4943,19 @@ public class PermissionManagerService extends IPermissionManager.Stub { } private void onPackageInstalledInternal(@NonNull AndroidPackage pkg, - @NonNull List<String> grantedPermissions, - @NonNull List<String> allowlistedRestrictedPermissions, int autoRevokePermissionsMode, + @NonNull PermissionManagerServiceInternal.PackageInstalledParams params, @UserIdInt int userId) { - addAllowlistedRestrictedPermissionsInternal(pkg, allowlistedRestrictedPermissions, + updatePermissions(pkg.getPackageName(), pkg); + addAllowlistedRestrictedPermissionsInternal(pkg, + params.getAllowlistedRestrictedPermissions(), FLAG_PERMISSION_WHITELIST_INSTALLER, userId); + final int autoRevokePermissionsMode = params.getAutoRevokePermissionsMode(); if (autoRevokePermissionsMode == AppOpsManager.MODE_ALLOWED || autoRevokePermissionsMode == AppOpsManager.MODE_IGNORED) { setAutoRevokeExemptedInternal(pkg, autoRevokePermissionsMode == AppOpsManager.MODE_IGNORED, userId); } - grantRequestedRuntimePermissionsInternal(pkg, grantedPermissions, userId); + grantRequestedRuntimePermissionsInternal(pkg, params.getGrantedPermissions(), userId); } private void addAllowlistedRestrictedPermissionsInternal(@NonNull AndroidPackage pkg, @@ -4978,25 +4976,34 @@ public class PermissionManagerService extends IPermissionManager.Stub { removeAllPermissionsInternal(pkg); } - private void onPackageStateRemovedInternal(@NonNull String packageName, int appId, - @Nullable AndroidPackage pkg, @NonNull List<AndroidPackage> sharedUserPkgs) { - if (sharedUserPkgs.isEmpty() - && mPackageManagerInt.getDisabledSystemPackage(packageName) == null) { - removeAppIdState(appId); + private void onPackageUninstalledInternal(@NonNull String packageName, int appId, + @Nullable AndroidPackage pkg, @NonNull List<AndroidPackage> sharedUserPkgs, + @UserIdInt int userId) { + // TODO: Move these checks to check PackageState to be more reliable. + // System packages should always have an available APK. + if (pkg != null && pkg.isSystem() + // We may be fully removing invalid system packages during boot, and in that case we + // do want to remove their permission state. So make sure that the package is only + // being marked as uninstalled instead of fully removed. + && mPackageManagerInt.getPackage(packageName) != null) { + // If we are only marking a system package as uninstalled, we need to keep its + // pregranted permission state so that it still works once it gets reinstalled, thus + // only reset the user modifications to its permission state. + resetRuntimePermissionsInternal(pkg, userId); + return; } - updatePermissions(packageName, null, mDefaultPermissionCallback); - if (!sharedUserPkgs.isEmpty()) { + updatePermissions(packageName, null); + if (sharedUserPkgs.isEmpty()) { + removeUidState(appId, userId); + } else { // Remove permissions associated with package. Since runtime // permissions are per user we have to kill the removed package // or packages running under the shared user of the removed // package if revoking the permissions requested only by the removed // package is successful and this causes a change in gids. - boolean shouldKill = false; - for (int userId : UserManagerService.getInstance().getUserIds()) { - final int userIdToKill = revokeSharedUserPermissionsForDeletedPackageInternal(pkg, - sharedUserPkgs, userId); - shouldKill |= userIdToKill != UserHandle.USER_NULL; - } + final int userIdToKill = revokeSharedUserPermissionsForDeletedPackageInternal(pkg, + sharedUserPkgs, userId); + final boolean shouldKill = userIdToKill != UserHandle.USER_NULL; // If gids changed, kill all affected packages. if (shouldKill) { mHandler.post(() -> { @@ -5140,11 +5147,6 @@ public class PermissionManagerService extends IPermissionManager.Stub { return PermissionManagerService.this.getAppOpPermissionPackagesInternal(permissionName); } @Override - public void updatePermissions(@NonNull String packageName, @Nullable AndroidPackage pkg) { - PermissionManagerService.this - .updatePermissions(packageName, pkg, mDefaultPermissionCallback); - } - @Override public void updateAllPermissions(@Nullable String volumeUuid, boolean sdkUpdated) { PermissionManagerService.this .updateAllPermissions(volumeUuid, sdkUpdated, mDefaultPermissionCallback); @@ -5411,16 +5413,11 @@ public class PermissionManagerService extends IPermissionManager.Stub { @Override public void onPackageInstalled(@NonNull AndroidPackage pkg, - @NonNull List<String> grantedPermissions, - @NonNull List<String> allowlistedRestrictedPermissions, - int autoRevokePermissionsMode, @UserIdInt int userId) { + @NonNull PackageInstalledParams params, @UserIdInt int userId) { Objects.requireNonNull(pkg, "pkg"); - Objects.requireNonNull(grantedPermissions, "grantedPermissions"); - Objects.requireNonNull(allowlistedRestrictedPermissions, - "allowlistedRestrictedPermissions"); + Objects.requireNonNull(params, "params"); Preconditions.checkArgumentNonNegative(userId, "userId"); - onPackageInstalledInternal(pkg, grantedPermissions, allowlistedRestrictedPermissions, - autoRevokePermissionsMode, userId); + onPackageInstalledInternal(pkg, params, userId); } @Override @@ -5430,11 +5427,13 @@ public class PermissionManagerService extends IPermissionManager.Stub { } @Override - public void onPackageStateRemoved(@NonNull String packageName, int appId, - @Nullable AndroidPackage pkg, @NonNull List<AndroidPackage> sharedUserPkgs) { - Objects.requireNonNull(packageName); - Objects.requireNonNull(sharedUserPkgs); - onPackageStateRemovedInternal(packageName, appId, pkg, sharedUserPkgs); + public void onPackageUninstalled(@NonNull String packageName, int appId, + @Nullable AndroidPackage pkg, @NonNull List<AndroidPackage> sharedUserPkgs, + @UserIdInt int userId) { + Objects.requireNonNull(packageName, "packageName"); + Objects.requireNonNull(sharedUserPkgs, "sharedUserPkgs"); + Preconditions.checkArgumentNonNegative(userId, "userId"); + onPackageUninstalledInternal(packageName, appId, pkg, sharedUserPkgs, userId); } @Override @@ -5467,6 +5466,32 @@ public class PermissionManagerService extends IPermissionManager.Stub { } } + /** + * Callbacks invoked when interesting actions have been taken on a permission. + * <p> + * NOTE: The current arguments are merely to support the existing use cases. This + * needs to be properly thought out with appropriate arguments for each of the + * callback methods. + */ + private static class PermissionCallback { + public void onGidsChanged(@AppIdInt int appId, @UserIdInt int userId) {} + public void onPermissionChanged() {} + public void onPermissionGranted(int uid, @UserIdInt int userId) {} + public void onInstallPermissionGranted() {} + public void onPermissionRevoked(int uid, @UserIdInt int userId, String reason) {} + public void onInstallPermissionRevoked() {} + public void onPermissionUpdated(@UserIdInt int[] updatedUserIds, boolean sync) {} + public void onPermissionUpdatedNotifyListener(@UserIdInt int[] updatedUserIds, boolean sync, + int uid) { + onPermissionUpdated(updatedUserIds, sync); + } + public void onPermissionRemoved() {} + public void onInstallPermissionUpdated() {} + public void onInstallPermissionUpdatedNotifyListener(int uid) { + onInstallPermissionUpdated(); + } + } + private static final class OnPermissionChangeListeners extends Handler { private static final int MSG_ON_PERMISSIONS_CHANGED = 1; diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java index 457fe36ca2b8..f924651f1051 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java @@ -16,17 +16,19 @@ package com.android.server.pm.permission; -import android.annotation.AppIdInt; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; +import android.app.AppOpsManager; import android.content.pm.PermissionInfo; import android.permission.PermissionManagerInternal; import com.android.server.pm.parsing.pkg.AndroidPackage; import java.util.ArrayList; +import java.util.Collections; import java.util.List; +import java.util.Objects; import java.util.Set; import java.util.function.Consumer; @@ -140,41 +142,6 @@ public abstract class PermissionManagerServiceInternal extends PermissionManager @NonNull Consumer<Boolean> callback); } - /** - * Callbacks invoked when interesting actions have been taken on a permission. - * <p> - * NOTE: The current arguments are merely to support the existing use cases. This - * needs to be properly thought out with appropriate arguments for each of the - * callback methods. - */ - public static class PermissionCallback { - public void onGidsChanged(@AppIdInt int appId, @UserIdInt int userId) { - } - public void onPermissionChanged() { - } - public void onPermissionGranted(int uid, @UserIdInt int userId) { - } - public void onInstallPermissionGranted() { - } - public void onPermissionRevoked(int uid, @UserIdInt int userId, String reason) { - } - public void onInstallPermissionRevoked() { - } - public void onPermissionUpdated(@UserIdInt int[] updatedUserIds, boolean sync) { - } - public void onPermissionUpdatedNotifyListener(@UserIdInt int[] updatedUserIds, boolean sync, - int uid) { - onPermissionUpdated(updatedUserIds, sync); - } - public void onPermissionRemoved() { - } - public void onInstallPermissionUpdated() { - } - public void onInstallPermissionUpdatedNotifyListener(int uid) { - onInstallPermissionUpdated(); - } - } - public abstract void systemReady(); /** @@ -189,22 +156,6 @@ public abstract class PermissionManagerServiceInternal extends PermissionManager @UserIdInt int userId); /** - * Update permissions when a package changed. - * - * <p><ol> - * <li>Reconsider the ownership of permission</li> - * <li>Update the state (grant, flags) of the permissions</li> - * </ol> - * - * @param packageName The package that is updated - * @param pkg The package that is updated, or {@code null} if package is deleted - * @param allPackages All currently known packages - * @param callback Callback to call after permission changes - */ - public abstract void updatePermissions(@NonNull String packageName, - @Nullable AndroidPackage pkg); - - /** * Update all permissions for all apps. * * <p><ol> @@ -489,19 +440,15 @@ public abstract class PermissionManagerServiceInternal extends PermissionManager @Nullable AndroidPackage oldPkg); /** - * Callback when a package has been installed for certain users. + * Callback when a package has been installed for a user. * * @param pkg the installed package - * @param grantedPermissions the permissions to be granted - * @param allowlistedRestrictedPermissions the restricted permissions to be allowlisted - * @param autoRevokePermissionsMode the auto revoke permissions mode for this package + * @param params the parameters passed in for package installation * @param userId the user ID this package is installed for */ //@SystemApi(client = SystemApi.Client.SYSTEM_SERVER) public abstract void onPackageInstalled(@NonNull AndroidPackage pkg, - @NonNull List<String> grantedPermissions, - @NonNull List<String> allowlistedRestrictedPermissions, - int autoRevokePermissionsMode, @UserIdInt int userId); + @NonNull PackageInstalledParams params, @UserIdInt int userId); /** * Callback when a package has been removed. @@ -512,16 +459,23 @@ public abstract class PermissionManagerServiceInternal extends PermissionManager public abstract void onPackageRemoved(@NonNull AndroidPackage pkg); /** - * Callback when the state for a package has been removed. + * Callback when a package has been uninstalled. + * <p> + * The package may have been fully removed from the system, or only marked as uninstalled for + * this user but still instlaled for other users. + * + * TODO: Pass PackageState instead. * - * @param packageName the name of the removed package - * @param appId the app ID of the removed package - * @param pkg the removed package, or {@code null} if unavailable + * @param packageName the name of the uninstalled package + * @param appId the app ID of the uninstalled package + * @param pkg the uninstalled package, or {@code null} if unavailable * @param sharedUserPkgs the packages that are in the same shared user + * @param userId the user ID the package is uninstalled for */ //@SystemApi(client = SystemApi.Client.SYSTEM_SERVER) - public abstract void onPackageStateRemoved(@NonNull String packageName, int appId, - @Nullable AndroidPackage pkg, @NonNull List<AndroidPackage> sharedUserPkgs); + public abstract void onPackageUninstalled(@NonNull String packageName, int appId, + @Nullable AndroidPackage pkg, @NonNull List<AndroidPackage> sharedUserPkgs, + @UserIdInt int userId); /** * Check whether a permission can be propagated to instant app. @@ -530,4 +484,132 @@ public abstract class PermissionManagerServiceInternal extends PermissionManager * @return whether the permission can be propagated */ public abstract boolean canPropagatePermissionToInstantApp(@NonNull String permissionName); + + /** + * The permission-related parameters passed in for package installation. + * + * @see android.content.pm.PackageInstaller.SessionParams + */ + //@SystemApi(client = SystemApi.Client.SYSTEM_SERVER) + public static final class PackageInstalledParams { + /** + * A static instance whose parameters are all in their default state. + */ + public static final PackageInstalledParams DEFAULT = new Builder().build(); + + @NonNull + private final List<String> mGrantedPermissions; + @NonNull + private final List<String> mAllowlistedRestrictedPermissions; + @NonNull + private final int mAutoRevokePermissionsMode; + + private PackageInstalledParams(@NonNull List<String> grantedPermissions, + @NonNull List<String> allowlistedRestrictedPermissions, + int autoRevokePermissionsMode) { + mGrantedPermissions = grantedPermissions; + mAllowlistedRestrictedPermissions = allowlistedRestrictedPermissions; + mAutoRevokePermissionsMode = autoRevokePermissionsMode; + } + + /** + * Get the permissions to be granted. + * + * @return the permissions to be granted + */ + @NonNull + public List<String> getGrantedPermissions() { + return mGrantedPermissions; + } + + /** + * Get the restricted permissions to be allowlisted. + * + * @return the restricted permissions to be allowlisted + */ + @NonNull + public List<String> getAllowlistedRestrictedPermissions() { + return mAllowlistedRestrictedPermissions; + } + + /** + * Get the mode for auto revoking permissions. + * + * @return the mode for auto revoking permissions + */ + public int getAutoRevokePermissionsMode() { + return mAutoRevokePermissionsMode; + } + + /** + * Builder class for {@link PackageInstalledParams}. + */ + public static final class Builder { + @NonNull + private List<String> mGrantedPermissions = Collections.emptyList(); + @NonNull + private List<String> mAllowlistedRestrictedPermissions = Collections.emptyList(); + @NonNull + private int mAutoRevokePermissionsMode = AppOpsManager.MODE_DEFAULT; + + /** + * Set the permissions to be granted. + * + * @param grantedPermissions the permissions to be granted + * + * @see android.content.pm.PackageInstaller.SessionParams#setGrantedRuntimePermissions( + * java.lang.String[]) + */ + public void setGrantedPermissions(@NonNull List<String> grantedPermissions) { + Objects.requireNonNull(grantedPermissions); + mGrantedPermissions = new ArrayList<>(grantedPermissions); + } + + /** + * Set the restricted permissions to be allowlisted. + * <p> + * Permissions that are not restricted are ignored, so one can just pass in all + * requested permissions of a package to get all its restricted permissions allowlisted. + * + * @param allowlistedRestrictedPermissions the restricted permissions to be allowlisted + * + * @see android.content.pm.PackageInstaller.SessionParams#setWhitelistedRestrictedPermissions(Set) + */ + public void setAllowlistedRestrictedPermissions( + @NonNull List<String> allowlistedRestrictedPermissions) { + Objects.requireNonNull(mGrantedPermissions); + mAllowlistedRestrictedPermissions = new ArrayList<>( + allowlistedRestrictedPermissions); + } + + /** + * Set the mode for auto revoking permissions. + * <p> + * {@link AppOpsManager#MODE_ALLOWED} means the system is allowed to auto revoke + * permissions from this package, and {@link AppOpsManager#MODE_IGNORED} means this + * package should be ignored when auto revoking permissions. + * {@link AppOpsManager#MODE_DEFAULT} means no changes will be made to the auto revoke + * mode of this package. + * + * @param autoRevokePermissionsMode the mode for auto revoking permissions + * + * @see android.content.pm.PackageInstaller.SessionParams#setAutoRevokePermissionsMode( + * boolean) + */ + public void setAutoRevokePermissionsMode(int autoRevokePermissionsMode) { + mAutoRevokePermissionsMode = autoRevokePermissionsMode; + } + + /** + * Build a new instance of {@link PackageInstalledParams}. + * + * @return the {@link PackageInstalledParams} built + */ + @NonNull + public PackageInstalledParams build() { + return new PackageInstalledParams(mGrantedPermissions, + mAllowlistedRestrictedPermissions, mAutoRevokePermissionsMode); + } + } + } } diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java b/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java index 1883f4e4e86c..ccd4e0ffa3e7 100644 --- a/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java +++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java @@ -29,6 +29,7 @@ import android.os.PowerSaveState; import android.provider.Settings; import android.text.TextUtils; import android.util.ArrayMap; +import android.util.IndentingPrintWriter; import android.util.KeyValueListParser; import android.util.Slog; import android.view.accessibility.AccessibilityManager; @@ -1031,90 +1032,92 @@ public class BatterySaverPolicy extends ContentObserver { } public void dump(PrintWriter pw) { + final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " "); + synchronized (mLock) { - pw.println(); - mBatterySavingStats.dump(pw, ""); - - pw.println(); - pw.println("Battery saver policy (*NOTE* they only apply when battery saver is ON):"); - pw.println(" Settings: " + Settings.Global.BATTERY_SAVER_CONSTANTS); - pw.println(" value: " + mSettings); - pw.println(" Settings: " + mDeviceSpecificSettingsSource); - pw.println(" value: " + mDeviceSpecificSettings); - - pw.println(" Adaptive Settings: " + Settings.Global.BATTERY_SAVER_ADAPTIVE_CONSTANTS); - pw.println(" value: " + mAdaptiveSettings); - pw.println(" Adaptive Device Specific Settings: " + ipw.println(); + mBatterySavingStats.dump(ipw); + + ipw.println(); + ipw.println("Battery saver policy (*NOTE* they only apply when battery saver is ON):"); + ipw.increaseIndent(); + ipw.println("Settings: " + Settings.Global.BATTERY_SAVER_CONSTANTS); + ipw.increaseIndent(); + ipw.println("value: " + mSettings); + ipw.decreaseIndent(); + ipw.println("Settings: " + mDeviceSpecificSettingsSource); + ipw.increaseIndent(); + ipw.println("value: " + mDeviceSpecificSettings); + ipw.decreaseIndent(); + + ipw.println("Adaptive Settings: " + Settings.Global.BATTERY_SAVER_ADAPTIVE_CONSTANTS); + ipw.increaseIndent(); + ipw.println("value: " + mAdaptiveSettings); + ipw.decreaseIndent(); + ipw.println("Adaptive Device Specific Settings: " + Settings.Global.BATTERY_SAVER_ADAPTIVE_DEVICE_SPECIFIC_CONSTANTS); - pw.println(" value: " + mAdaptiveDeviceSpecificSettings); + ipw.increaseIndent(); + ipw.println("value: " + mAdaptiveDeviceSpecificSettings); + ipw.decreaseIndent(); + + ipw.println("mAccessibilityEnabled=" + mAccessibilityEnabled.get()); + ipw.println("mAutomotiveProjectionActive=" + mAutomotiveProjectionActive.get()); + ipw.println("mPolicyLevel=" + mPolicyLevel); - pw.println(" mAccessibilityEnabled=" + mAccessibilityEnabled.get()); - pw.println(" mAutomotiveProjectionActive=" + mAutomotiveProjectionActive.get()); - pw.println(" mPolicyLevel=" + mPolicyLevel); + dumpPolicyLocked(ipw, "full", mFullPolicy); + dumpPolicyLocked(ipw, "default adaptive", mDefaultAdaptivePolicy); + dumpPolicyLocked(ipw, "current adaptive", mAdaptivePolicy); + dumpPolicyLocked(ipw, "effective", mEffectivePolicyRaw); - dumpPolicyLocked(pw, " ", "full", mFullPolicy); - dumpPolicyLocked(pw, " ", "default adaptive", mDefaultAdaptivePolicy); - dumpPolicyLocked(pw, " ", "current adaptive", mAdaptivePolicy); - dumpPolicyLocked(pw, " ", "effective", mEffectivePolicyRaw); + ipw.decreaseIndent(); } } - private void dumpPolicyLocked(PrintWriter pw, String indent, String label, Policy p) { + private void dumpPolicyLocked(IndentingPrintWriter pw, String label, Policy p) { pw.println(); - pw.print(indent); pw.println("Policy '" + label + "'"); - pw.print(indent); - pw.println(" " + KEY_ADVERTISE_IS_ENABLED + "=" + p.advertiseIsEnabled); - pw.print(indent); - pw.println(" " + KEY_VIBRATION_DISABLED + "=" + p.disableVibration); - pw.print(indent); - pw.println(" " + KEY_ANIMATION_DISABLED + "=" + p.disableAnimation); - pw.print(indent); - pw.println(" " + KEY_FULLBACKUP_DEFERRED + "=" + p.deferFullBackup); - pw.print(indent); - pw.println(" " + KEY_KEYVALUE_DEFERRED + "=" + p.deferKeyValueBackup); - pw.print(indent); - pw.println(" " + KEY_ACTIVATE_FIREWALL_DISABLED + "=" + !p.enableFirewall); - pw.print(indent); - pw.println(" " + KEY_ACTIVATE_DATASAVER_DISABLED + "=" + !p.enableDataSaver); - pw.print(indent); - pw.println(" " + KEY_LAUNCH_BOOST_DISABLED + "=" + p.disableLaunchBoost); - pw.println( - " " + KEY_ADJUST_BRIGHTNESS_DISABLED + "=" + !p.enableAdjustBrightness); - pw.print(indent); - pw.println(" " + KEY_ADJUST_BRIGHTNESS_FACTOR + "=" + p.adjustBrightnessFactor); - pw.print(indent); - pw.println(" " + KEY_GPS_MODE + "=" + p.locationMode); - pw.print(indent); - pw.println(" " + KEY_FORCE_ALL_APPS_STANDBY + "=" + p.forceAllAppsStandby); - pw.print(indent); - pw.println(" " + KEY_FORCE_BACKGROUND_CHECK + "=" + p.forceBackgroundCheck); - pw.println( - " " + KEY_OPTIONAL_SENSORS_DISABLED + "=" + p.disableOptionalSensors); - pw.print(indent); - pw.println(" " + KEY_AOD_DISABLED + "=" + p.disableAod); - pw.print(indent); - pw.println(" " + KEY_SOUNDTRIGGER_DISABLED + "=" + p.disableSoundTrigger); - pw.print(indent); - pw.println(" " + KEY_QUICK_DOZE_ENABLED + "=" + p.enableQuickDoze); - pw.print(indent); - pw.println(" " + KEY_ENABLE_NIGHT_MODE + "=" + p.enableNightMode); - - pw.print(" Interactive File values:\n"); - dumpMap(pw, " ", p.filesForInteractive); + pw.increaseIndent(); + pw.println(KEY_ADVERTISE_IS_ENABLED + "=" + p.advertiseIsEnabled); + pw.println(KEY_VIBRATION_DISABLED + "=" + p.disableVibration); + pw.println(KEY_ANIMATION_DISABLED + "=" + p.disableAnimation); + pw.println(KEY_FULLBACKUP_DEFERRED + "=" + p.deferFullBackup); + pw.println(KEY_KEYVALUE_DEFERRED + "=" + p.deferKeyValueBackup); + pw.println(KEY_ACTIVATE_FIREWALL_DISABLED + "=" + !p.enableFirewall); + pw.println(KEY_ACTIVATE_DATASAVER_DISABLED + "=" + !p.enableDataSaver); + pw.println(KEY_LAUNCH_BOOST_DISABLED + "=" + p.disableLaunchBoost); + pw.println(KEY_ADJUST_BRIGHTNESS_DISABLED + "=" + !p.enableAdjustBrightness); + pw.println(KEY_ADJUST_BRIGHTNESS_FACTOR + "=" + p.adjustBrightnessFactor); + pw.println(KEY_GPS_MODE + "=" + p.locationMode); + pw.println(KEY_FORCE_ALL_APPS_STANDBY + "=" + p.forceAllAppsStandby); + pw.println(KEY_FORCE_BACKGROUND_CHECK + "=" + p.forceBackgroundCheck); + pw.println(KEY_OPTIONAL_SENSORS_DISABLED + "=" + p.disableOptionalSensors); + pw.println(KEY_AOD_DISABLED + "=" + p.disableAod); + pw.println(KEY_SOUNDTRIGGER_DISABLED + "=" + p.disableSoundTrigger); + pw.println(KEY_QUICK_DOZE_ENABLED + "=" + p.enableQuickDoze); + pw.println(KEY_ENABLE_NIGHT_MODE + "=" + p.enableNightMode); + + pw.println("Interactive File values:"); + pw.increaseIndent(); + dumpMap(pw, p.filesForInteractive); + pw.decreaseIndent(); pw.println(); - pw.print(" Noninteractive File values:\n"); - dumpMap(pw, " ", p.filesForNoninteractive); + pw.println("Noninteractive File values:"); + pw.increaseIndent(); + dumpMap(pw, p.filesForNoninteractive); + pw.decreaseIndent(); + + // Decrease from indent right after "Policy" line + pw.decreaseIndent(); } - private void dumpMap(PrintWriter pw, String prefix, ArrayMap<String, String> map) { + private void dumpMap(PrintWriter pw, ArrayMap<String, String> map) { if (map == null) { + pw.println("N/A"); return; } final int size = map.size(); for (int i = 0; i < size; i++) { - pw.print(prefix); pw.print(map.keyAt(i)); pw.print(": '"); pw.print(map.valueAt(i)); diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java b/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java index af14d84d12b8..21500f649099 100644 --- a/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java +++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java @@ -34,6 +34,7 @@ import android.os.PowerManager; import android.os.SystemClock; import android.os.UserHandle; import android.provider.Settings; +import android.util.IndentingPrintWriter; import android.util.Slog; import android.util.proto.ProtoOutputStream; @@ -888,70 +889,75 @@ public class BatterySaverStateMachine { } public void dump(PrintWriter pw) { + final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " "); + + ipw.println(); + ipw.println("Battery saver state machine:"); + ipw.increaseIndent(); synchronized (mLock) { - pw.println(); - pw.println("Battery saver state machine:"); - - pw.print(" Enabled="); - pw.println(mBatterySaverController.isEnabled()); - pw.print(" full="); - pw.println(mBatterySaverController.isFullEnabled()); - pw.print(" adaptive="); - pw.print(mBatterySaverController.isAdaptiveEnabled()); + ipw.print("Enabled="); + ipw.println(mBatterySaverController.isEnabled()); + ipw.increaseIndent(); + ipw.print("full="); + ipw.println(mBatterySaverController.isFullEnabled()); + ipw.print("adaptive="); + ipw.print(mBatterySaverController.isAdaptiveEnabled()); if (mBatterySaverController.isAdaptiveEnabled()) { - pw.print(" (advertise="); - pw.print( + ipw.print(" (advertise="); + ipw.print( mBatterySaverController.getBatterySaverPolicy().shouldAdvertiseIsEnabled()); - pw.print(")"); + ipw.print(")"); } - pw.println(); - pw.print(" mState="); - pw.println(mState); - - pw.print(" mLastChangedIntReason="); - pw.println(mLastChangedIntReason); - pw.print(" mLastChangedStrReason="); - pw.println(mLastChangedStrReason); - - pw.print(" mBootCompleted="); - pw.println(mBootCompleted); - pw.print(" mSettingsLoaded="); - pw.println(mSettingsLoaded); - pw.print(" mBatteryStatusSet="); - pw.println(mBatteryStatusSet); - - pw.print(" mIsPowered="); - pw.println(mIsPowered); - pw.print(" mBatteryLevel="); - pw.println(mBatteryLevel); - pw.print(" mIsBatteryLevelLow="); - pw.println(mIsBatteryLevelLow); - - pw.print(" mSettingAutomaticBatterySaver="); - pw.println(mSettingAutomaticBatterySaver); - pw.print(" mSettingBatterySaverEnabled="); - pw.println(mSettingBatterySaverEnabled); - pw.print(" mSettingBatterySaverEnabledSticky="); - pw.println(mSettingBatterySaverEnabledSticky); - pw.print(" mSettingBatterySaverStickyAutoDisableEnabled="); - pw.println(mSettingBatterySaverStickyAutoDisableEnabled); - pw.print(" mSettingBatterySaverStickyAutoDisableThreshold="); - pw.println(mSettingBatterySaverStickyAutoDisableThreshold); - pw.print(" mSettingBatterySaverTriggerThreshold="); - pw.println(mSettingBatterySaverTriggerThreshold); - pw.print(" mBatterySaverStickyBehaviourDisabled="); - pw.println(mBatterySaverStickyBehaviourDisabled); - - pw.print(" mDynamicPowerSavingsDefaultDisableThreshold="); - pw.println(mDynamicPowerSavingsDefaultDisableThreshold); - pw.print(" mDynamicPowerSavingsDisableThreshold="); - pw.println(mDynamicPowerSavingsDisableThreshold); - pw.print(" mDynamicPowerSavingsEnableBatterySaver="); - pw.println(mDynamicPowerSavingsEnableBatterySaver); - - pw.print(" mLastAdaptiveBatterySaverChangedExternallyElapsed="); - pw.println(mLastAdaptiveBatterySaverChangedExternallyElapsed); + ipw.decreaseIndent(); + ipw.println(); + ipw.print("mState="); + ipw.println(mState); + + ipw.print("mLastChangedIntReason="); + ipw.println(mLastChangedIntReason); + ipw.print("mLastChangedStrReason="); + ipw.println(mLastChangedStrReason); + + ipw.print("mBootCompleted="); + ipw.println(mBootCompleted); + ipw.print("mSettingsLoaded="); + ipw.println(mSettingsLoaded); + ipw.print("mBatteryStatusSet="); + ipw.println(mBatteryStatusSet); + + ipw.print("mIsPowered="); + ipw.println(mIsPowered); + ipw.print("mBatteryLevel="); + ipw.println(mBatteryLevel); + ipw.print("mIsBatteryLevelLow="); + ipw.println(mIsBatteryLevelLow); + + ipw.print("mSettingAutomaticBatterySaver="); + ipw.println(mSettingAutomaticBatterySaver); + ipw.print("mSettingBatterySaverEnabled="); + ipw.println(mSettingBatterySaverEnabled); + ipw.print("mSettingBatterySaverEnabledSticky="); + ipw.println(mSettingBatterySaverEnabledSticky); + ipw.print("mSettingBatterySaverStickyAutoDisableEnabled="); + ipw.println(mSettingBatterySaverStickyAutoDisableEnabled); + ipw.print("mSettingBatterySaverStickyAutoDisableThreshold="); + ipw.println(mSettingBatterySaverStickyAutoDisableThreshold); + ipw.print("mSettingBatterySaverTriggerThreshold="); + ipw.println(mSettingBatterySaverTriggerThreshold); + ipw.print("mBatterySaverStickyBehaviourDisabled="); + ipw.println(mBatterySaverStickyBehaviourDisabled); + + ipw.print("mDynamicPowerSavingsDefaultDisableThreshold="); + ipw.println(mDynamicPowerSavingsDefaultDisableThreshold); + ipw.print("mDynamicPowerSavingsDisableThreshold="); + ipw.println(mDynamicPowerSavingsDisableThreshold); + ipw.print("mDynamicPowerSavingsEnableBatterySaver="); + ipw.println(mDynamicPowerSavingsEnableBatterySaver); + + ipw.print("mLastAdaptiveBatterySaverChangedExternallyElapsed="); + ipw.println(mLastAdaptiveBatterySaverChangedExternallyElapsed); } + ipw.decreaseIndent(); } public void dumpProto(ProtoOutputStream proto, long tag) { diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySavingStats.java b/services/core/java/com/android/server/power/batterysaver/BatterySavingStats.java index 05695d919910..a7be2677cf23 100644 --- a/services/core/java/com/android/server/power/batterysaver/BatterySavingStats.java +++ b/services/core/java/com/android/server/power/batterysaver/BatterySavingStats.java @@ -17,6 +17,7 @@ package com.android.server.power.batterysaver; import android.os.BatteryManagerInternal; import android.os.SystemClock; +import android.util.IndentingPrintWriter; import android.util.Slog; import android.util.SparseArray; import android.util.TimeUtils; @@ -26,7 +27,6 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.server.EventLogTags; import com.android.server.LocalServices; -import java.io.PrintWriter; import java.text.SimpleDateFormat; import java.util.Date; @@ -391,18 +391,15 @@ public class BatterySavingStats { stat.endTime = 0; } - public void dump(PrintWriter pw, String indent) { - synchronized (mLock) { - pw.print(indent); - pw.println("Battery saving stats:"); - - indent = indent + " "; + public void dump(IndentingPrintWriter pw) { + pw.println("Battery saving stats:"); + pw.increaseIndent(); + synchronized (mLock) { final long now = System.currentTimeMillis(); final long nowElapsed = injectCurrentTime(); final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); - pw.print(indent); pw.print("Battery Saver is currently: "); switch (BatterySaverState.fromIndex(mCurrentState)) { case BatterySaverState.OFF: @@ -416,9 +413,8 @@ public class BatterySavingStats { break; } + pw.increaseIndent(); if (mLastBatterySaverEnabledTime > 0) { - pw.print(indent); - pw.print(" "); pw.print("Last ON time: "); pw.print(sdf.format(new Date(now - nowElapsed + mLastBatterySaverEnabledTime))); pw.print(" "); @@ -427,8 +423,6 @@ public class BatterySavingStats { } if (mLastBatterySaverDisabledTime > 0) { - pw.print(indent); - pw.print(" "); pw.print("Last OFF time: "); pw.print(sdf.format(new Date(now - nowElapsed + mLastBatterySaverDisabledTime))); pw.print(" "); @@ -436,14 +430,10 @@ public class BatterySavingStats { pw.println(); } - pw.print(indent); - pw.print(" "); pw.print("Times full enabled: "); pw.println(mBatterySaverEnabledCount); if (mLastAdaptiveBatterySaverEnabledTime > 0) { - pw.print(indent); - pw.print(" "); pw.print("Last ADAPTIVE ON time: "); pw.print(sdf.format( new Date(now - nowElapsed + mLastAdaptiveBatterySaverEnabledTime))); @@ -452,8 +442,6 @@ public class BatterySavingStats { pw.println(); } if (mLastAdaptiveBatterySaverDisabledTime > 0) { - pw.print(indent); - pw.print(" "); pw.print("Last ADAPTIVE OFF time: "); pw.print(sdf.format( new Date(now - nowElapsed + mLastAdaptiveBatterySaverDisabledTime))); @@ -461,39 +449,36 @@ public class BatterySavingStats { TimeUtils.formatDuration(mLastAdaptiveBatterySaverDisabledTime, nowElapsed, pw); pw.println(); } - pw.print(indent); - pw.print(" "); pw.print("Times adaptive enabled: "); pw.println(mAdaptiveBatterySaverEnabledCount); + pw.decreaseIndent(); pw.println(); - pw.print(indent); pw.println("Drain stats:"); - pw.print(indent); pw.println(" Battery saver OFF ON"); - dumpLineLocked(pw, indent, InteractiveState.NON_INTERACTIVE, "NonIntr", + dumpLineLocked(pw, InteractiveState.NON_INTERACTIVE, "NonIntr", DozeState.NOT_DOZING, "NonDoze"); - dumpLineLocked(pw, indent, InteractiveState.INTERACTIVE, " Intr", + dumpLineLocked(pw, InteractiveState.INTERACTIVE, " Intr", DozeState.NOT_DOZING, " "); - dumpLineLocked(pw, indent, InteractiveState.NON_INTERACTIVE, "NonIntr", + dumpLineLocked(pw, InteractiveState.NON_INTERACTIVE, "NonIntr", DozeState.DEEP, "Deep "); - dumpLineLocked(pw, indent, InteractiveState.INTERACTIVE, " Intr", + dumpLineLocked(pw, InteractiveState.INTERACTIVE, " Intr", DozeState.DEEP, " "); - dumpLineLocked(pw, indent, InteractiveState.NON_INTERACTIVE, "NonIntr", + dumpLineLocked(pw, InteractiveState.NON_INTERACTIVE, "NonIntr", DozeState.LIGHT, "Light "); - dumpLineLocked(pw, indent, InteractiveState.INTERACTIVE, " Intr", + dumpLineLocked(pw, InteractiveState.INTERACTIVE, " Intr", DozeState.LIGHT, " "); } + pw.decreaseIndent(); } - private void dumpLineLocked(PrintWriter pw, String indent, + private void dumpLineLocked(IndentingPrintWriter pw, int interactiveState, String interactiveLabel, int dozeState, String dozeLabel) { - pw.print(indent); pw.print(dozeLabel); pw.print(" "); pw.print(interactiveLabel); diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyCallbackImpl.java b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyCallbackImpl.java index 5b6de0518999..4f3f9dce8adb 100644 --- a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyCallbackImpl.java +++ b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyCallbackImpl.java @@ -16,6 +16,8 @@ package com.android.server.timedetector; +import static com.android.server.timedetector.TimeDetectorStrategy.ORIGIN_NETWORK; +import static com.android.server.timedetector.TimeDetectorStrategy.ORIGIN_TELEPHONY; import static com.android.server.timedetector.TimeDetectorStrategy.stringToOrigin; import android.annotation.NonNull; @@ -30,6 +32,9 @@ import android.os.SystemProperties; import android.provider.Settings; import android.util.Slog; +import com.android.internal.R; +import com.android.server.timedetector.TimeDetectorStrategy.Origin; + import java.time.Instant; import java.util.Objects; @@ -50,6 +55,13 @@ public final class TimeDetectorStrategyCallbackImpl implements TimeDetectorStrat Long.max(Environment.getRootDirectory().lastModified(), Build.TIME)); /** + * By default telephony and network only suggestions are accepted and telephony takes + * precedence over network. + */ + private static final @Origin int[] DEFAULT_AUTOMATIC_TIME_ORIGIN_PRIORITIES = + { ORIGIN_TELEPHONY, ORIGIN_NETWORK }; + + /** * If a newly calculated system clock time and the current system clock time differs by this or * more the system clock will actually be updated. Used to prevent the system clock being set * for only minor differences. @@ -76,14 +88,7 @@ public final class TimeDetectorStrategyCallbackImpl implements TimeDetectorStrat SystemProperties.getInt("ro.sys.time_detector_update_diff", SYSTEM_CLOCK_UPDATE_THRESHOLD_MILLIS_DEFAULT); - // TODO(b/172230856): Obtain these values from configuration. - String[] originStrings = { "telephony", "network" }; - int[] origins = new int[originStrings.length]; - for (int i = 0; i < originStrings.length; i++) { - int origin = stringToOrigin(originStrings[i]); - origins[i] = origin; - } - mOriginPriorities = origins; + mOriginPriorities = getOriginPriorities(context); } @Override @@ -106,7 +111,7 @@ public final class TimeDetectorStrategyCallbackImpl implements TimeDetectorStrat } @Override - public int[] getAutoOriginPriorities() { + public int[] autoOriginPriorities() { return mOriginPriorities; } @@ -145,4 +150,20 @@ public final class TimeDetectorStrategyCallbackImpl implements TimeDetectorStrat Slog.wtf(TAG, "WakeLock " + mWakeLock + " not held"); } } + + private static int[] getOriginPriorities(@NonNull Context context) { + String[] originStrings = + context.getResources().getStringArray(R.array.config_autoTimeSourcesPriority); + if (originStrings.length == 0) { + return DEFAULT_AUTOMATIC_TIME_ORIGIN_PRIORITIES; + } else { + int[] origins = new int[originStrings.length]; + for (int i = 0; i < originStrings.length; i++) { + int origin = stringToOrigin(originStrings[i]); + origins[i] = origin; + } + + return origins; + } + } } diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java index d8cada55781c..b5d49cfbe9c8 100644 --- a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java +++ b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java @@ -18,6 +18,8 @@ package com.android.server.timedetector; import static com.android.server.timedetector.TimeDetectorStrategy.originToString; +import static java.util.stream.Collectors.joining; + import android.annotation.NonNull; import android.annotation.Nullable; import android.app.AlarmManager; @@ -140,7 +142,7 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { * Returns the order to look at time suggestions when automatically detecting time. * See {@code #ORIGIN_} constants */ - @Origin int[] getAutoOriginPriorities(); + @Origin int[] autoOriginPriorities(); /** Acquire a suitable wake lock. Must be followed by {@link #releaseWakeLock()} */ void acquireWakeLock(); @@ -252,6 +254,14 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { ipw.println("mCallback.systemClockMillis()=" + mCallback.systemClockMillis()); ipw.println("mCallback.systemClockUpdateThresholdMillis()=" + mCallback.systemClockUpdateThresholdMillis()); + ipw.printf("mCallback.autoTimeLowerBound()=%s(%s)\n", + mCallback.autoTimeLowerBound(), + mCallback.autoTimeLowerBound().toEpochMilli()); + String priorities = + Arrays.stream(mCallback.autoOriginPriorities()) + .mapToObj(TimeDetectorStrategy::originToString) + .collect(joining(",", "[", "]")); + ipw.println("mCallback.autoOriginPriorities()=" + priorities); ipw.println("Time change log:"); ipw.increaseIndent(); // level 2 @@ -353,7 +363,7 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { } // Try the different origins one at a time. - int[] originPriorities = mCallback.getAutoOriginPriorities(); + int[] originPriorities = mCallback.autoOriginPriorities(); for (int origin : originPriorities) { TimestampedValue<Long> newUtcTime = null; String cause = null; diff --git a/services/core/java/com/android/server/tv/TEST_MAPPING b/services/core/java/com/android/server/tv/TEST_MAPPING new file mode 100644 index 000000000000..f718f909fecb --- /dev/null +++ b/services/core/java/com/android/server/tv/TEST_MAPPING @@ -0,0 +1,7 @@ +{ + "imports": [ + { + "path": "cts/tests/tests/tv" + } + ] +}
\ No newline at end of file diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java index 510893b2940b..608012e4a310 100755 --- a/services/core/java/com/android/server/tv/TvInputManagerService.java +++ b/services/core/java/com/android/server/tv/TvInputManagerService.java @@ -768,6 +768,7 @@ public final class TvInputManagerService extends SystemService { SessionState sessionState = userState.sessionStateMap.remove(sessionToken); if (sessionState == null) { + Slog.e(TAG, "sessionState null, no more remove session action!"); return; } @@ -2525,8 +2526,16 @@ public final class TvInputManagerService extends SystemService { ClientState clientState = userState.clientStateMap.get(clientToken); if (clientState != null) { while (clientState.sessionTokens.size() > 0) { + IBinder sessionToken = clientState.sessionTokens.get(0); releaseSessionLocked( - clientState.sessionTokens.get(0), Process.SYSTEM_UID, userId); + sessionToken, Process.SYSTEM_UID, userId); + // the releaseSessionLocked function may return before the sessionToken + // is removed if the related sessionState is null. So need to check again + // to avoid death curculation. + if (clientState.sessionTokens.contains(sessionToken)) { + Slog.d(TAG, "remove sessionToken " + sessionToken + " for " + clientToken); + clientState.sessionTokens.remove(sessionToken); + } } } clientToken = null; diff --git a/services/core/java/com/android/server/utils/Snappable.java b/services/core/java/com/android/server/utils/Snappable.java new file mode 100644 index 000000000000..9b9460b8f757 --- /dev/null +++ b/services/core/java/com/android/server/utils/Snappable.java @@ -0,0 +1,35 @@ +/* + * 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.server.utils; + +import android.annotation.NonNull; + +/** + * A class that implements Snappable can generate a read-only copy its instances. A + * snapshot is like a clone except that it is only required to support read-only class + * methods. Snapshots are immutable. Attempts to modify the state of a snapshot throw + * {@link UnsupporteOperationException}. + * @param <T> The type returned by the snapshot() method. + */ +public interface Snappable<T> { + + /** + * Create an immutable copy of the object, suitable for read-only methods. A snapshot + * is free to omit state that is only needed for mutating methods. + */ + @NonNull T snapshot(); +} diff --git a/services/core/java/com/android/server/utils/Snapshots.java b/services/core/java/com/android/server/utils/Snapshots.java new file mode 100644 index 000000000000..33b2bd48d802 --- /dev/null +++ b/services/core/java/com/android/server/utils/Snapshots.java @@ -0,0 +1,133 @@ +/* + * 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.server.utils; + +import android.annotation.NonNull; +import android.util.SparseArray; +import android.util.SparseIntArray; +import android.util.SparseSetArray; + +/** + * A collection of useful methods for manipulating Snapshot classes. This is similar to + * java.util.Objects or java.util.Arrays. + */ +public class Snapshots { + + /** + * Return the snapshot of an object, if the object extends {@link Snapper}, or the object + * itself. + * @param o The object to be copied + * @return A snapshot of the object, if the object extends {@link Snapper} + */ + public static <T> T maybeSnapshot(T o) { + if (o instanceof Snappable) { + return ((Snappable<T>) o).snapshot(); + } else { + return o; + } + } + + /** + * Copy a SparseArray in a manner suitable for a snapshot. The destination must be + * empty. This is not a snapshot because the elements are copied by reference even if + * they are {@link Snappable}. + * @param dst The destination array. It must be empty. + * @param src The source array + */ + public <E> void copy(@NonNull SparseArray<E> dst, @NonNull SparseArray<E> src) { + if (dst.size() != 0) { + throw new IllegalArgumentException("copy destination is not empty"); + } + final int end = src.size(); + for (int i = 0; i < end; i++) { + dst.put(src.keyAt(i), src.valueAt(i)); + } + } + + /** + * Copy a SparseSetArray in a manner suitable for a snapshot. The destination must be + * empty. This is not a snapshot because the elements are copied by reference even if + * they are {@link Snappable}. + * @param dst The destination array. It must be empty. + * @param src The source array + */ + public static <E> void copy(@NonNull SparseSetArray<E> dst, @NonNull SparseSetArray<E> src) { + if (dst.size() != 0) { + throw new IllegalArgumentException("copy destination is not empty"); + } + final int end = src.size(); + for (int i = 0; i < end; i++) { + final int size = src.sizeAt(i); + for (int j = 0; j < size; j++) { + dst.add(src.keyAt(i), src.valueAt(i, j)); + } + } + } + + /** + * Make <dst> a snapshot of <src> . + * @param dst The destination array. It must be empty. + * @param src The source array + */ + public void snapshot(@NonNull SparseIntArray dst, @NonNull SparseIntArray src) { + if (dst.size() != 0) { + throw new IllegalArgumentException("snapshot destination is not empty"); + } + final int end = src.size(); + for (int i = 0; i < end; i++) { + dst.put(src.keyAt(i), src.valueAt(i)); + } + } + + /** + * Make <dst> a "snapshot" of <src>. <dst> mst be empty. The destination is just a + * copy of the source except that if the source elements implement Snappable, then + * the elements in the destination will be snapshots of elements from the source. + * @param dst The destination array. It must be empty. + * @param src The source array + */ + public static <E extends Snappable<E>> void snapshot(@NonNull SparseArray<E> dst, + @NonNull SparseArray<E> src) { + if (dst.size() != 0) { + throw new IllegalArgumentException("snapshot destination is not empty"); + } + final int end = src.size(); + for (int i = 0; i < end; i++) { + dst.put(src.keyAt(i), src.valueAt(i).snapshot()); + } + } + + /** + * Make <dst> a "snapshot" of <src>. <dst> mst be empty. The destination is a + * copy of the source except that snapshots are taken of the elements. + * @param dst The destination array. It must be empty. + * @param src The source array + */ + public static <E extends Snappable<E>> void snapshot(@NonNull SparseSetArray<E> dst, + @NonNull SparseSetArray<E> src) { + if (dst.size() != 0) { + throw new IllegalArgumentException("snapshot destination is not empty"); + } + final int end = src.size(); + for (int i = 0; i < end; i++) { + final int size = src.sizeAt(i); + for (int j = 0; j < size; j++) { + dst.add(src.keyAt(i), src.valueAt(i, j).snapshot()); + } + } + } +} diff --git a/services/core/java/com/android/server/utils/WatchableImpl.java b/services/core/java/com/android/server/utils/WatchableImpl.java index 94ab1d49807f..16400b186ab0 100644 --- a/services/core/java/com/android/server/utils/WatchableImpl.java +++ b/services/core/java/com/android/server/utils/WatchableImpl.java @@ -19,11 +19,15 @@ package com.android.server.utils; import android.annotation.NonNull; import android.annotation.Nullable; +import com.android.internal.annotations.GuardedBy; + import java.util.ArrayList; import java.util.Objects; /** - * A concrete implementation of {@link Watchable} + * A concrete implementation of {@link Watchable}. This includes one commonly needed feature: + * the Watchable may be sealed, so that it throws an {@link IllegalStateException} if + * a change is detected. */ public class WatchableImpl implements Watchable { /** @@ -78,10 +82,37 @@ public class WatchableImpl implements Watchable { @Override public void dispatchChange(@Nullable Watchable what) { synchronized (mObservers) { + if (mSealed) { + throw new IllegalStateException("attempt to change a sealed object"); + } final int end = mObservers.size(); for (int i = 0; i < end; i++) { mObservers.get(i).onChange(what); } } } + + /** + * True if the object is sealed. + */ + @GuardedBy("mObservers") + private boolean mSealed = false; + + /** + * Freeze the {@link Watchable}. + **/ + public void seal() { + synchronized (mObservers) { + mSealed = true; + } + } + + /** + * Return the sealed state. + */ + public boolean isFrozen() { + synchronized (mObservers) { + return mSealed; + } + } } diff --git a/services/core/java/com/android/server/utils/WatchedArrayMap.java b/services/core/java/com/android/server/utils/WatchedArrayMap.java index 7b3298086aba..e8065f140af7 100644 --- a/services/core/java/com/android/server/utils/WatchedArrayMap.java +++ b/services/core/java/com/android/server/utils/WatchedArrayMap.java @@ -18,9 +18,7 @@ package com.android.server.utils; import android.annotation.NonNull; import android.annotation.Nullable; - import android.util.ArrayMap; -import android.util.Log; import java.util.Collection; import java.util.Collections; @@ -31,14 +29,17 @@ import java.util.Set; * WatchedArrayMap is an {@link android.util.ArrayMap} that can report changes to itself. If its * values are {@link Watchable} then the WatchedArrayMap will also report changes to the values. * A {@link Watchable} is notified only once, no matter how many times it is stored in the array. + * @param <K> The key type. + * @param <V> The value type. */ -public class WatchedArrayMap<K, V> extends WatchableImpl implements Map<K, V> { +public class WatchedArrayMap<K, V> extends WatchableImpl + implements Map<K, V>, Snappable { // The storage private final ArrayMap<K, V> mStorage; // If true, the array is watching its children - private boolean mWatching = false; + private volatile boolean mWatching = false; // The local observer private final Watcher mObserver = new Watcher() { @@ -386,4 +387,38 @@ public class WatchedArrayMap<K, V> extends WatchableImpl implements Map<K, V> { onChanged(); return result; } + + /** + * Create a copy of the array. If the element is a subclass of Snapper then the copy + * contains snapshots of the elements. Otherwise the copy contains references to the + * elements. The returned snapshot is immutable. + * @return A new array whose elements are the elements of <this>. + */ + public WatchedArrayMap<K, V> snapshot() { + WatchedArrayMap<K, V> l = new WatchedArrayMap<>(); + snapshot(l, this); + return l; + } + + /** + * Make the destination a copy of the source. If the element is a subclass of Snapper then the + * copy contains snapshots of the elements. Otherwise the copy contains references to the + * elements. The destination must be initially empty. Upon return, the destination is + * immutable. + * @param dst The destination array. It must be empty. + * @param src The source array. It is not modified. + */ + public static <K, V> void snapshot(@NonNull WatchedArrayMap<K, V> dst, + @NonNull WatchedArrayMap<K, V> src) { + if (dst.size() != 0) { + throw new IllegalArgumentException("snapshot destination is not empty"); + } + final int end = src.size(); + for (int i = 0; i < end; i++) { + final V val = Snapshots.maybeSnapshot(src.valueAt(i)); + final K key = src.keyAt(i); + dst.put(key, val); + } + dst.seal(); + } } diff --git a/services/core/java/com/android/server/utils/WatchedSparseArray.java b/services/core/java/com/android/server/utils/WatchedSparseArray.java index 4d43b682e113..6797c6eb7801 100644 --- a/services/core/java/com/android/server/utils/WatchedSparseArray.java +++ b/services/core/java/com/android/server/utils/WatchedSparseArray.java @@ -18,7 +18,6 @@ package com.android.server.utils; import android.annotation.NonNull; import android.annotation.Nullable; - import android.util.SparseArray; import java.util.ArrayList; @@ -28,14 +27,16 @@ import java.util.ArrayList; * array registers with the {@link Watchable}. The array registers only once with each * {@link Watchable} no matter how many times the {@link Watchable} is stored in the * array. + * @param <E> The element type, stored in the array. */ -public class WatchedSparseArray<E> extends WatchableImpl { +public class WatchedSparseArray<E> extends WatchableImpl + implements Snappable { // The storage private final SparseArray<E> mStorage; // If true, the array is watching its children - private boolean mWatching = false; + private volatile boolean mWatching = false; // The local observer private final Watcher mObserver = new Watcher() { @@ -398,4 +399,39 @@ public class WatchedSparseArray<E> extends WatchableImpl { public String toString() { return mStorage.toString(); } + + /** + * Create a copy of the array. If the element is a subclass of Snapper then the copy + * contains snapshots of the elements. Otherwise the copy contains references to the + * elements. The returned snapshot is immutable. + * @return A new array whose elements are the elements of <this>. + */ + public WatchedSparseArray<E> snapshot() { + WatchedSparseArray<E> l = new WatchedSparseArray<>(); + snapshot(l, this); + return l; + } + + /** + * Make the destination a copy of the source. If the element is a subclass of Snapper then the + * copy contains snapshots of the elements. Otherwise the copy contains references to the + * elements. The destination must be initially empty. Upon return, the destination is + * immutable. + * @param dst The destination array. It must be empty. + * @param src The source array. It is not modified. + */ + public static <E> void snapshot(@NonNull WatchedSparseArray<E> dst, + @NonNull WatchedSparseArray<E> src) { + if (dst.size() != 0) { + throw new IllegalArgumentException("snapshot destination is not empty"); + } + final int end = src.size(); + for (int i = 0; i < end; i++) { + final E val = Snapshots.maybeSnapshot(src.valueAt(i)); + final int key = src.keyAt(i); + dst.put(key, val); + } + dst.seal(); + } + } diff --git a/services/core/java/com/android/server/utils/WatchedSparseBooleanArray.java b/services/core/java/com/android/server/utils/WatchedSparseBooleanArray.java index edf7d27b61dd..b845eea168a5 100644 --- a/services/core/java/com/android/server/utils/WatchedSparseBooleanArray.java +++ b/services/core/java/com/android/server/utils/WatchedSparseBooleanArray.java @@ -17,21 +17,20 @@ package com.android.server.utils; import android.annotation.NonNull; -import android.annotation.Nullable; - import android.util.SparseBooleanArray; /** * A watched variant of SparseBooleanArray. Changes to the array are notified to * registered {@link Watcher}s. */ -public class WatchedSparseBooleanArray extends WatchableImpl { +public class WatchedSparseBooleanArray extends WatchableImpl + implements Snappable { // The storage private final SparseBooleanArray mStorage; // A private convenience function - private void dispatchChange() { + private void onChanged() { dispatchChange(this); } @@ -81,7 +80,7 @@ public class WatchedSparseBooleanArray extends WatchableImpl { */ public void delete(int key) { mStorage.delete(key); - dispatchChange(); + onChanged(); } /** @@ -91,7 +90,7 @@ public class WatchedSparseBooleanArray extends WatchableImpl { */ public void removeAt(int index) { mStorage.removeAt(index); - dispatchChange(); + onChanged(); } /** @@ -102,7 +101,7 @@ public class WatchedSparseBooleanArray extends WatchableImpl { public void put(int key, boolean value) { if (mStorage.get(key) != value) { mStorage.put(key, value); - dispatchChange(); + onChanged(); } } @@ -164,7 +163,7 @@ public class WatchedSparseBooleanArray extends WatchableImpl { public void setValueAt(int index, boolean value) { if (mStorage.valueAt(index) != value) { mStorage.setValueAt(index, value); - dispatchChange(); + onChanged(); } } @@ -172,7 +171,7 @@ public class WatchedSparseBooleanArray extends WatchableImpl { public void setKeyAt(int index, int key) { if (mStorage.keyAt(index) != key) { mStorage.setKeyAt(index, key); - dispatchChange(); + onChanged(); } } @@ -202,7 +201,7 @@ public class WatchedSparseBooleanArray extends WatchableImpl { */ public void clear() { mStorage.clear(); - dispatchChange(); + onChanged(); } /** @@ -211,7 +210,7 @@ public class WatchedSparseBooleanArray extends WatchableImpl { */ public void append(int key, boolean value) { mStorage.append(key, value); - dispatchChange(); + onChanged(); } @Override @@ -233,4 +232,30 @@ public class WatchedSparseBooleanArray extends WatchableImpl { public String toString() { return mStorage.toString(); } + + /** + * Create a snapshot. The snapshot does not include any {@link Watchable} + * information. + */ + public WatchedSparseBooleanArray snapshot() { + WatchedSparseBooleanArray l = new WatchedSparseBooleanArray(this); + l.seal(); + return l; + } + + /** + * Make <this> a snapshot of the argument. Note that <this> is immutable when the + * method returns. <this> must be empty when the function is called. + * @param r The source array, which is copied into <this> + */ + public void snapshot(@NonNull WatchedSparseBooleanArray r) { + if (size() != 0) { + throw new IllegalArgumentException("snapshot destination is not empty"); + } + final int end = r.size(); + for (int i = 0; i < end; i++) { + put(r.keyAt(i), r.valueAt(i)); + } + seal(); + } } diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java index 583441207a36..c3d5874de609 100644 --- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java +++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java @@ -2846,8 +2846,9 @@ public class WallpaperManagerService extends IWallpaperManager.Stub if (!uidMatchPackage) { return false; // callingPackage was faked. } - if (LocalServices.getService(DevicePolicyManagerInternal.class) - .isDeviceOrProfileOwnerInCallingUser(callingPackage)) { + final DevicePolicyManagerInternal dpmi = + LocalServices.getService(DevicePolicyManagerInternal.class); + if (dpmi != null && dpmi.isDeviceOrProfileOwnerInCallingUser(callingPackage)) { return true; } final int callingUserId = UserHandle.getCallingUserId(); diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 1336c81f18ed..2e55e92a4dcd 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -1491,13 +1491,14 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } } - ActivityRecord(ActivityTaskManagerService _service, WindowProcessController _caller, + private ActivityRecord(ActivityTaskManagerService _service, WindowProcessController _caller, int _launchedFromPid, int _launchedFromUid, String _launchedFromPackage, @Nullable String _launchedFromFeature, Intent _intent, String _resolvedType, ActivityInfo aInfo, Configuration _configuration, ActivityRecord _resultTo, String _resultWho, int _reqCode, boolean _componentSpecified, boolean _rootVoiceInteraction, ActivityTaskSupervisor supervisor, - ActivityOptions options, ActivityRecord sourceRecord) { + ActivityOptions options, ActivityRecord sourceRecord, PersistableBundle persistentState, + TaskDescription _taskDescription, long _createTime) { super(_service.mWindowManager, new Token(_intent).asBinder(), TYPE_APPLICATION, true, null /* displayContent */, false /* ownerCanManageAppTokens */); @@ -1652,6 +1653,12 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A mHandoverLaunchDisplayId = options.getLaunchDisplayId(); mLaunchCookie = options.getLaunchCookie(); } + + mPersistentState = persistentState; + taskDescription = _taskDescription; + if (_createTime > 0) { + createTime = _createTime; + } } /** @@ -7544,18 +7551,18 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A throw new XmlPullParserException("restoreActivity resolver error. Intent=" + intent + " resolvedType=" + resolvedType); } - final ActivityRecord r = new ActivityRecord(service, null /* caller */, - 0 /* launchedFromPid */, launchedFromUid, launchedFromPackage, launchedFromFeature, - intent, resolvedType, aInfo, service.getConfiguration(), null /* resultTo */, - null /* resultWho */, 0 /* reqCode */, componentSpecified, - false /* rootVoiceInteraction */, taskSupervisor, null /* options */, - null /* sourceRecord */); - - r.mPersistentState = persistentState; - r.taskDescription = taskDescription; - r.createTime = createTime; - - return r; + return new ActivityRecord.Builder(service) + .setLaunchedFromUid(launchedFromUid) + .setLaunchedFromPackage(launchedFromPackage) + .setLaunchedFromFeature(launchedFromFeature) + .setIntent(intent) + .setResolvedType(resolvedType) + .setActivityInfo(aInfo) + .setComponentSpecified(componentSpecified) + .setPersistentState(persistentState) + .setTaskDescription(taskDescription) + .setCreateTime(createTime) + .build(); } private static boolean isInVrUiMode(Configuration config) { @@ -7983,4 +7990,138 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } return false; } + + static class Builder { + private final ActivityTaskManagerService mAtmService; + private WindowProcessController mCallerApp; + private int mLaunchedFromPid; + private int mLaunchedFromUid; + private String mLaunchedFromPackage; + private String mLaunchedFromFeature; + private Intent mIntent; + private String mResolvedType; + private ActivityInfo mActivityInfo; + private Configuration mConfiguration; + private ActivityRecord mResultTo; + private String mResultWho; + private int mRequestCode; + private boolean mComponentSpecified; + private boolean mRootVoiceInteraction; + private ActivityOptions mOptions; + private ActivityRecord mSourceRecord; + private PersistableBundle mPersistentState; + private TaskDescription mTaskDescription; + private long mCreateTime; + + Builder(ActivityTaskManagerService service) { + mAtmService = service; + } + + Builder setCaller(@NonNull WindowProcessController caller) { + mCallerApp = caller; + return this; + } + + Builder setLaunchedFromPid(int pid) { + mLaunchedFromPid = pid; + return this; + } + + Builder setLaunchedFromUid(int uid) { + mLaunchedFromUid = uid; + return this; + } + + Builder setLaunchedFromPackage(String fromPackage) { + mLaunchedFromPackage = fromPackage; + return this; + } + + Builder setLaunchedFromFeature(String fromFeature) { + mLaunchedFromFeature = fromFeature; + return this; + } + + Builder setIntent(Intent intent) { + mIntent = intent; + return this; + } + + Builder setResolvedType(String resolvedType) { + mResolvedType = resolvedType; + return this; + } + + Builder setActivityInfo(ActivityInfo activityInfo) { + mActivityInfo = activityInfo; + return this; + } + + Builder setResultTo(ActivityRecord resultTo) { + mResultTo = resultTo; + return this; + } + + Builder setResultWho(String resultWho) { + mResultWho = resultWho; + return this; + } + + Builder setRequestCode(int reqCode) { + mRequestCode = reqCode; + return this; + } + + Builder setComponentSpecified(boolean componentSpecified) { + mComponentSpecified = componentSpecified; + return this; + } + + Builder setRootVoiceInteraction(boolean rootVoiceInteraction) { + mRootVoiceInteraction = rootVoiceInteraction; + return this; + } + + Builder setActivityOptions(ActivityOptions options) { + mOptions = options; + return this; + } + + Builder setConfiguration(Configuration config) { + mConfiguration = config; + return this; + } + + Builder setSourceRecord(ActivityRecord source) { + mSourceRecord = source; + return this; + } + + private Builder setPersistentState(PersistableBundle persistentState) { + mPersistentState = persistentState; + return this; + } + + private Builder setTaskDescription(TaskDescription taskDescription) { + mTaskDescription = taskDescription; + return this; + } + + private Builder setCreateTime(long createTime) { + mCreateTime = createTime; + return this; + } + + ActivityRecord build() { + if (mConfiguration == null) { + mConfiguration = mAtmService.getConfiguration(); + } + return new ActivityRecord(mAtmService, mCallerApp, mLaunchedFromPid, + mLaunchedFromUid, mLaunchedFromPackage, mLaunchedFromFeature, mIntent, + mResolvedType, mActivityInfo, mConfiguration, mResultTo, mResultWho, + mRequestCode, mComponentSpecified, mRootVoiceInteraction, + mAtmService.mTaskSupervisor, mOptions, mSourceRecord, mPersistentState, + mTaskDescription, mCreateTime); + } + } } diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index 0bea25174f06..1ff3a3fb1d35 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -1161,12 +1161,25 @@ class ActivityStarter { aInfo = mSupervisor.resolveActivity(intent, rInfo, startFlags, null /*profilerInfo*/); } + final ActivityRecord r = new ActivityRecord.Builder(mService) + .setCaller(callerApp) + .setLaunchedFromPid(callingPid) + .setLaunchedFromUid(callingUid) + .setLaunchedFromPackage(callingPackage) + .setLaunchedFromFeature(callingFeatureId) + .setIntent(intent) + .setResolvedType(resolvedType) + .setActivityInfo(aInfo) + .setConfiguration(mService.getGlobalConfiguration()) + .setResultTo(resultRecord) + .setResultWho(resultWho) + .setRequestCode(requestCode) + .setComponentSpecified(request.componentSpecified) + .setRootVoiceInteraction(voiceSession != null) + .setActivityOptions(checkedOptions) + .setSourceRecord(sourceRecord) + .build(); - final ActivityRecord r = new ActivityRecord(mService, callerApp, callingPid, callingUid, - callingPackage, callingFeatureId, intent, resolvedType, aInfo, - mService.getGlobalConfiguration(), resultRecord, resultWho, requestCode, - request.componentSpecified, voiceSession != null, mSupervisor, checkedOptions, - sourceRecord); mLastStartActivityRecord = r; if (r.appTimeTracker == null && sourceRecord != null) { diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java index d94c7af0f190..73d99724c65f 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java +++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java @@ -2570,6 +2570,11 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { mService.getActivityStartController().postStartActivityProcessingForLastStarter( task.getTopNonFinishingActivity(), ActivityManager.START_TASK_TO_FRONT, task.getRootTask()); + + // As it doesn't go to ActivityStarter.executeRequest() path, we need to resume + // app switching here also. + mService.resumeAppSwitches(); + return ActivityManager.START_TASK_TO_FRONT; } callingPackage = task.mCallingPackage; diff --git a/services/core/java/com/android/server/wm/DisplayArea.java b/services/core/java/com/android/server/wm/DisplayArea.java index a4ac16f2ec77..cd3f32278165 100644 --- a/services/core/java/com/android/server/wm/DisplayArea.java +++ b/services/core/java/com/android/server/wm/DisplayArea.java @@ -307,6 +307,11 @@ public class DisplayArea<T extends WindowContainer> extends WindowContainer<T> { return this; } + /** Cheap way of doing cast and instanceof. */ + DisplayArea.Tokens asTokens() { + return null; + } + @Override void forAllDisplayAreas(Consumer<DisplayArea> callback) { super.forAllDisplayAreas(callback); @@ -544,6 +549,11 @@ public class DisplayArea<T extends WindowContainer> extends WindowContainer<T> { mLastOrientationSource = win; return req; } + + @Override + final DisplayArea.Tokens asTokens() { + return this; + } } @Override diff --git a/services/core/java/com/android/server/wm/DisplayAreaPolicy.java b/services/core/java/com/android/server/wm/DisplayAreaPolicy.java index 211bd5d9eb77..7573e92e82a8 100644 --- a/services/core/java/com/android/server/wm/DisplayAreaPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayAreaPolicy.java @@ -86,7 +86,7 @@ public abstract class DisplayAreaPolicy { @Override public DisplayAreaPolicy instantiate(WindowManagerService wmService, DisplayContent content, RootDisplayArea root, - DisplayArea<? extends WindowContainer> imeContainer) { + DisplayArea.Tokens imeContainer) { final TaskDisplayArea defaultTaskDisplayArea = new TaskDisplayArea(content, wmService, "DefaultTaskDisplayArea", FEATURE_DEFAULT_TASK_CONTAINER); final List<TaskDisplayArea> tdaList = new ArrayList<>(); @@ -151,7 +151,7 @@ public abstract class DisplayAreaPolicy { * @see DisplayAreaPolicy#DisplayAreaPolicy */ DisplayAreaPolicy instantiate(WindowManagerService wmService, DisplayContent content, - RootDisplayArea root, DisplayArea<? extends WindowContainer> imeContainer); + RootDisplayArea root, DisplayArea.Tokens imeContainer); /** * Instantiates the device-specific {@link Provider}. diff --git a/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java b/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java index 6a420874b924..bc3220673ed3 100644 --- a/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java +++ b/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java @@ -247,7 +247,7 @@ class DisplayAreaPolicyBuilder { private final ArrayList<DisplayAreaPolicyBuilder.Feature> mFeatures = new ArrayList<>(); private final ArrayList<TaskDisplayArea> mTaskDisplayAreas = new ArrayList<>(); @Nullable - private DisplayArea<? extends WindowContainer> mImeContainer; + private DisplayArea.Tokens mImeContainer; HierarchyBuilder(RootDisplayArea root) { mRoot = root; @@ -270,7 +270,7 @@ class DisplayAreaPolicyBuilder { } /** Sets IME container as a child of this hierarchy root. */ - HierarchyBuilder setImeContainer(DisplayArea<? extends WindowContainer> imeContainer) { + HierarchyBuilder setImeContainer(DisplayArea.Tokens imeContainer) { mImeContainer = imeContainer; return this; } @@ -706,6 +706,10 @@ class DisplayAreaPolicyBuilder { private DisplayArea createArea(DisplayArea<DisplayArea> parent, DisplayArea.Tokens[] areaForLayer) { if (mExisting != null) { + if (mExisting.asTokens() != null) { + // Store the WindowToken container for layers + fillAreaForLayers(mExisting.asTokens(), areaForLayer); + } return mExisting; } if (mSkipTokens) { @@ -722,14 +726,18 @@ class DisplayAreaPolicyBuilder { if (mFeature == null) { final DisplayArea.Tokens leaf = new DisplayArea.Tokens(parent.mWmService, type, "Leaf:" + mMinLayer + ":" + mMaxLayer); - for (int i = mMinLayer; i <= mMaxLayer; i++) { - areaForLayer[i] = leaf; - } + fillAreaForLayers(leaf, areaForLayer); return leaf; } else { return mFeature.mNewDisplayAreaSupplier.create(parent.mWmService, type, mFeature.mName + ":" + mMinLayer + ":" + mMaxLayer, mFeature.mId); } } + + private void fillAreaForLayers(DisplayArea.Tokens leaf, DisplayArea.Tokens[] areaForLayer) { + for (int i = mMinLayer; i <= mMaxLayer; i++) { + areaForLayer[i] = leaf; + } + } } } diff --git a/services/core/java/com/android/server/wm/DisplayWindowSettingsProvider.java b/services/core/java/com/android/server/wm/DisplayWindowSettingsProvider.java index aa603aba0f78..f4fdd7370e9c 100644 --- a/services/core/java/com/android/server/wm/DisplayWindowSettingsProvider.java +++ b/services/core/java/com/android/server/wm/DisplayWindowSettingsProvider.java @@ -16,9 +16,9 @@ package com.android.server.wm; +import static android.view.WindowManager.DISPLAY_IME_POLICY_FALLBACK_DISPLAY; import static android.view.WindowManager.DISPLAY_IME_POLICY_LOCAL; import static android.view.WindowManager.REMOVE_CONTENT_MODE_UNDEFINED; -import static android.view.WindowManager.DISPLAY_IME_POLICY_FALLBACK_DISPLAY; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; @@ -64,7 +64,7 @@ class DisplayWindowSettingsProvider implements SettingsProvider { ? "DisplayWindowSettingsProvider" : TAG_WM; private static final String DATA_DISPLAY_SETTINGS_FILE_PATH = "system/display_settings.xml"; - private static final String VENDOR_DISPLAY_SETTINGS_PATH = "etc/display_settings.xml"; + private static final String VENDOR_DISPLAY_SETTINGS_FILE_PATH = "etc/display_settings.xml"; private static final String WM_DISPLAY_COMMIT_TAG = "wm-displays"; private static final int IDENTIFIER_UNIQUE_ID = 0; @@ -86,34 +86,8 @@ class DisplayWindowSettingsProvider implements SettingsProvider { void finishWrite(OutputStream os, boolean success); } - private final ReadableSettingsStorage mVendorSettingsStorage; - /** - * The preferred type of a display identifier to use when storing and retrieving entries from - * the base (vendor) settings file. - * - * @see #getIdentifier(DisplayInfo, int) - */ - @DisplayIdentifierType - private int mVendorIdentifierType; - private final Map<String, SettingsEntry> mVendorSettings = new HashMap<>(); - - private final WritableSettingsStorage mOverrideSettingsStorage; - /** - * The preferred type of a display identifier to use when storing and retrieving entries from - * the data (override) settings file. - * - * @see #getIdentifier(DisplayInfo, int) - */ - @DisplayIdentifierType - private int mOverrideIdentifierType; - private final Map<String, SettingsEntry> mOverrideSettings = new HashMap<>(); - - /** - * Enables or disables settings provided from the vendor settings storage. - * - * @see #setVendorSettingsIgnored(boolean) - */ - private boolean mIgnoreVendorSettings = true; + private ReadableSettings mBaseSettings; + private final WritableSettings mOverrideSettings; DisplayWindowSettingsProvider() { this(new AtomicFileStorage(getVendorSettingsFile()), @@ -121,43 +95,48 @@ class DisplayWindowSettingsProvider implements SettingsProvider { } @VisibleForTesting - DisplayWindowSettingsProvider(@NonNull ReadableSettingsStorage vendorSettingsStorage, + DisplayWindowSettingsProvider(@NonNull ReadableSettingsStorage baseSettingsStorage, @NonNull WritableSettingsStorage overrideSettingsStorage) { - mVendorSettingsStorage = vendorSettingsStorage; - mOverrideSettingsStorage = overrideSettingsStorage; - readSettings(); + mBaseSettings = new ReadableSettings(baseSettingsStorage); + mOverrideSettings = new WritableSettings(overrideSettingsStorage); } /** - * Enables or disables settings provided from the vendor settings storage. If {@code true}, the - * vendor settings will be ignored and only the override settings will be returned from - * {@link #getSettings(DisplayInfo)}. If {@code false}, settings returned from - * {@link #getSettings(DisplayInfo)} will be a merged result of the vendor settings and the - * override settings. + * Overrides the path for the file that should be used to read base settings. If {@code null} is + * passed the default base settings file path will be used. + * + * @see #VENDOR_DISPLAY_SETTINGS_FILE_PATH */ - void setVendorSettingsIgnored(boolean ignored) { - mIgnoreVendorSettings = ignored; + void setBaseSettingsFilePath(@Nullable String path) { + AtomicFile settingsFile; + if (path != null) { + settingsFile = new AtomicFile(new File(path), WM_DISPLAY_COMMIT_TAG); + } else { + settingsFile = getVendorSettingsFile(); + } + + setBaseSettingsStorage(new AtomicFileStorage(settingsFile)); } /** - * Returns whether or not the vendor settings are being ignored. + * Overrides the storage that should be used to read base settings. * - * @see #setVendorSettingsIgnored(boolean) + * @see #setBaseSettingsFilePath(String) */ @VisibleForTesting - boolean getVendorSettingsIgnored() { - return mIgnoreVendorSettings; + void setBaseSettingsStorage(@NonNull ReadableSettingsStorage baseSettingsStorage) { + mBaseSettings = new ReadableSettings(baseSettingsStorage); } @Override @NonNull public SettingsEntry getSettings(@NonNull DisplayInfo info) { - SettingsEntry vendorSettings = getVendorSettingsEntry(info); - SettingsEntry overrideSettings = getOrCreateOverrideSettingsEntry(info); - if (vendorSettings == null) { + SettingsEntry baseSettings = mBaseSettings.getSettingsEntry(info); + SettingsEntry overrideSettings = mOverrideSettings.getOrCreateSettingsEntry(info); + if (baseSettings == null) { return new SettingsEntry(overrideSettings); } else { - SettingsEntry mergedSettings = new SettingsEntry(vendorSettings); + SettingsEntry mergedSettings = new SettingsEntry(baseSettings); mergedSettings.updateFrom(overrideSettings); return mergedSettings; } @@ -166,99 +145,126 @@ class DisplayWindowSettingsProvider implements SettingsProvider { @Override @NonNull public SettingsEntry getOverrideSettings(@NonNull DisplayInfo info) { - return new SettingsEntry(getOrCreateOverrideSettingsEntry(info)); + return new SettingsEntry(mOverrideSettings.getOrCreateSettingsEntry(info)); } @Override public void updateOverrideSettings(@NonNull DisplayInfo info, @NonNull SettingsEntry overrides) { - final SettingsEntry overrideSettings = getOrCreateOverrideSettingsEntry(info); - boolean changed = overrideSettings.setTo(overrides); - if (changed) { - writeOverrideSettings(); - } + mOverrideSettings.updateSettingsEntry(info, overrides); } - @Nullable - private SettingsEntry getVendorSettingsEntry(DisplayInfo info) { - if (mIgnoreVendorSettings) { + /** + * Class that allows reading {@link SettingsEntry entries} from a + * {@link ReadableSettingsStorage}. + */ + private static class ReadableSettings { + /** + * The preferred type of a display identifier to use when storing and retrieving entries + * from the settings entries. + * + * @see #getIdentifier(DisplayInfo) + */ + @DisplayIdentifierType + protected int mIdentifierType; + protected final Map<String, SettingsEntry> mSettings = new HashMap<>(); + + ReadableSettings(ReadableSettingsStorage settingsStorage) { + loadSettings(settingsStorage); + } + + @Nullable + final SettingsEntry getSettingsEntry(DisplayInfo info) { + final String identifier = getIdentifier(info); + SettingsEntry settings; + // Try to get corresponding settings using preferred identifier for the current config. + if ((settings = mSettings.get(identifier)) != null) { + return settings; + } + // Else, fall back to the display name. + if ((settings = mSettings.get(info.name)) != null) { + // Found an entry stored with old identifier. + mSettings.remove(info.name); + mSettings.put(identifier, settings); + return settings; + } return null; } - final String identifier = getIdentifier(info, mVendorIdentifierType); - SettingsEntry settings; - // Try to get corresponding settings using preferred identifier for the current config. - if ((settings = mVendorSettings.get(identifier)) != null) { - return settings; + /** Gets the identifier of choice for the current config. */ + protected final String getIdentifier(DisplayInfo displayInfo) { + if (mIdentifierType == IDENTIFIER_PORT && displayInfo.address != null) { + // Config suggests using port as identifier for physical displays. + if (displayInfo.address instanceof DisplayAddress.Physical) { + return "port:" + ((DisplayAddress.Physical) displayInfo.address).getPort(); + } + } + return displayInfo.uniqueId; } - // Else, fall back to the display name. - if ((settings = mVendorSettings.get(info.name)) != null) { - // Found an entry stored with old identifier. - mVendorSettings.remove(info.name); - mVendorSettings.put(identifier, settings); - return settings; + + private void loadSettings(ReadableSettingsStorage settingsStorage) { + FileData fileData = readSettings(settingsStorage); + if (fileData != null) { + mIdentifierType = fileData.mIdentifierType; + mSettings.putAll(fileData.mSettings); + } } - return null; } - @NonNull - private SettingsEntry getOrCreateOverrideSettingsEntry(DisplayInfo info) { - final String identifier = getIdentifier(info, mOverrideIdentifierType); - SettingsEntry settings; - // Try to get corresponding settings using preferred identifier for the current config. - if ((settings = mOverrideSettings.get(identifier)) != null) { - return settings; - } - // Else, fall back to the display name. - if ((settings = mOverrideSettings.get(info.name)) != null) { - // Found an entry stored with old identifier. - mOverrideSettings.remove(info.name); - mOverrideSettings.put(identifier, settings); - writeOverrideSettings(); - return settings; + /** + * Class that allows reading {@link SettingsEntry entries} from, and writing entries to, a + * {@link WritableSettingsStorage}. + */ + private static final class WritableSettings extends ReadableSettings { + private final WritableSettingsStorage mSettingsStorage; + + WritableSettings(WritableSettingsStorage settingsStorage) { + super(settingsStorage); + mSettingsStorage = settingsStorage; } - settings = new SettingsEntry(); - mOverrideSettings.put(identifier, settings); - return settings; - } + @NonNull + SettingsEntry getOrCreateSettingsEntry(DisplayInfo info) { + final String identifier = getIdentifier(info); + SettingsEntry settings; + // Try to get corresponding settings using preferred identifier for the current config. + if ((settings = mSettings.get(identifier)) != null) { + return settings; + } + // Else, fall back to the display name. + if ((settings = mSettings.get(info.name)) != null) { + // Found an entry stored with old identifier. + mSettings.remove(info.name); + mSettings.put(identifier, settings); + writeSettings(); + return settings; + } - private void readSettings() { - FileData vendorFileData = readSettings(mVendorSettingsStorage); - if (vendorFileData != null) { - mVendorIdentifierType = vendorFileData.mIdentifierType; - mVendorSettings.putAll(vendorFileData.mSettings); + settings = new SettingsEntry(); + mSettings.put(identifier, settings); + return settings; } - FileData overrideFileData = readSettings(mOverrideSettingsStorage); - if (overrideFileData != null) { - mOverrideIdentifierType = overrideFileData.mIdentifierType; - mOverrideSettings.putAll(overrideFileData.mSettings); + void updateSettingsEntry(DisplayInfo info, SettingsEntry settings) { + final SettingsEntry overrideSettings = getOrCreateSettingsEntry(info); + final boolean changed = overrideSettings.setTo(settings); + if (changed) { + writeSettings(); + } } - } - - private void writeOverrideSettings() { - FileData fileData = new FileData(); - fileData.mIdentifierType = mOverrideIdentifierType; - fileData.mSettings.putAll(mOverrideSettings); - writeSettings(mOverrideSettingsStorage, fileData); - } - /** Gets the identifier of choice for the current config. */ - private static String getIdentifier(DisplayInfo displayInfo, @DisplayIdentifierType int type) { - if (type == IDENTIFIER_PORT && displayInfo.address != null) { - // Config suggests using port as identifier for physical displays. - if (displayInfo.address instanceof DisplayAddress.Physical) { - return "port:" + ((DisplayAddress.Physical) displayInfo.address).getPort(); - } + private void writeSettings() { + FileData fileData = new FileData(); + fileData.mIdentifierType = mIdentifierType; + fileData.mSettings.putAll(mSettings); + DisplayWindowSettingsProvider.writeSettings(mSettingsStorage, fileData); } - return displayInfo.uniqueId; } @NonNull private static AtomicFile getVendorSettingsFile() { final File vendorFile = new File(Environment.getVendorDirectory(), - VENDOR_DISPLAY_SETTINGS_PATH); + VENDOR_DISPLAY_SETTINGS_FILE_PATH); return new AtomicFile(vendorFile, WM_DISPLAY_COMMIT_TAG); } diff --git a/services/core/java/com/android/server/wm/RootDisplayArea.java b/services/core/java/com/android/server/wm/RootDisplayArea.java index da04f438a496..c4fcea68a6fc 100644 --- a/services/core/java/com/android/server/wm/RootDisplayArea.java +++ b/services/core/java/com/android/server/wm/RootDisplayArea.java @@ -16,11 +16,17 @@ package com.android.server.wm; +import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; +import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG; import static android.view.WindowManagerPolicyConstants.APPLICATION_LAYER; import static android.window.DisplayAreaOrganizer.FEATURE_IME_PLACEHOLDER; import static com.android.server.wm.DisplayAreaPolicyBuilder.Feature; +import android.annotation.Nullable; + +import com.android.server.policy.WindowManagerPolicy; + import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -72,7 +78,8 @@ class RootDisplayArea extends DisplayArea<DisplayArea> { * match the root. */ void placeImeContainer(DisplayArea.Tokens imeContainer) { - if (imeContainer.getRootDisplayArea() == this) { + final RootDisplayArea previousRoot = imeContainer.getRootDisplayArea(); + if (previousRoot == this) { // No need to reparent if IME container is below the same root. return; } @@ -88,7 +95,9 @@ class RootDisplayArea extends DisplayArea<DisplayArea> { + "FEATURE_IME_PLACEHOLDER"); } + previousRoot.updateImeContainerForLayers(null /* imeContainer */); imeContainer.reparent(imeDisplayAreas.get(0), POSITION_TOP); + updateImeContainerForLayers(imeContainer); return; } } @@ -119,4 +128,10 @@ class RootDisplayArea extends DisplayArea<DisplayArea> { mAreaForLayer = areaForLayer; mFeatureToDisplayAreas = featureToDisplayAreas; } + + private void updateImeContainerForLayers(@Nullable DisplayArea.Tokens imeContainer) { + final WindowManagerPolicy policy = mWmService.mPolicy; + mAreaForLayer[policy.getWindowLayerFromTypeLw(TYPE_INPUT_METHOD)] = imeContainer; + mAreaForLayer[policy.getWindowLayerFromTypeLw(TYPE_INPUT_METHOD_DIALOG)] = imeContainer; + } } diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 87a5baf6feb0..7c290c465c32 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -44,8 +44,8 @@ import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDO import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_SIZECOMPAT_FREEFORM; import static android.provider.Settings.Global.DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS; import static android.provider.Settings.Global.DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES; -import static android.provider.Settings.Global.DEVELOPMENT_IGNORE_VENDOR_DISPLAY_SETTINGS; import static android.provider.Settings.Global.DEVELOPMENT_RENDER_SHADOWS_IN_COMPOSITOR; +import static android.provider.Settings.Global.DEVELOPMENT_WM_DISPLAY_SETTINGS_PATH; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.Display.INVALID_DISPLAY; import static android.view.WindowManager.DISPLAY_IME_POLICY_FALLBACK_DISPLAY; @@ -796,8 +796,8 @@ public class WindowManagerService extends IWindowManager.Stub DEVELOPMENT_ENABLE_SIZECOMPAT_FREEFORM); private final Uri mRenderShadowsInCompositorUri = Settings.Global.getUriFor( DEVELOPMENT_RENDER_SHADOWS_IN_COMPOSITOR); - private final Uri mIgnoreVendorDisplaySettingsUri = Settings.Global.getUriFor( - DEVELOPMENT_IGNORE_VENDOR_DISPLAY_SETTINGS); + private final Uri mDisplaySettingsPathUri = Settings.Global.getUriFor( + DEVELOPMENT_WM_DISPLAY_SETTINGS_PATH); public SettingsObserver() { super(new Handler()); @@ -822,7 +822,7 @@ public class WindowManagerService extends IWindowManager.Stub UserHandle.USER_ALL); resolver.registerContentObserver(mRenderShadowsInCompositorUri, false, this, UserHandle.USER_ALL); - resolver.registerContentObserver(mIgnoreVendorDisplaySettingsUri, false, this, + resolver.registerContentObserver(mDisplaySettingsPathUri, false, this, UserHandle.USER_ALL); } @@ -867,8 +867,8 @@ public class WindowManagerService extends IWindowManager.Stub return; } - if (mIgnoreVendorDisplaySettingsUri.equals(uri)) { - updateIgnoreVendorDisplaySettings(); + if (mDisplaySettingsPathUri.equals(uri)) { + updateDisplaySettingsLocation(); return; } @@ -962,12 +962,12 @@ public class WindowManagerService extends IWindowManager.Stub mAtmService.mSizeCompatFreeform = sizeCompatFreeform; } - void updateIgnoreVendorDisplaySettings() { + void updateDisplaySettingsLocation() { final ContentResolver resolver = mContext.getContentResolver(); - final boolean ignoreVendorSettings = Settings.Global.getInt(resolver, - DEVELOPMENT_IGNORE_VENDOR_DISPLAY_SETTINGS, 0) != 0; + final String filePath = Settings.Global.getString(resolver, + DEVELOPMENT_WM_DISPLAY_SETTINGS_PATH); synchronized (mGlobalLock) { - mDisplayWindowSettingsProvider.setVendorSettingsIgnored(ignoreVendorSettings); + mDisplayWindowSettingsProvider.setBaseSettingsFilePath(filePath); mRoot.forAllDisplays(display -> { mDisplayWindowSettings.applySettingsToDisplayLocked(display); display.reconfigureDisplayLocked(); @@ -1329,10 +1329,12 @@ public class WindowManagerService extends IWindowManager.Stub mForceDesktopModeOnExternalDisplays = Settings.Global.getInt(resolver, DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS, 0) != 0; - final boolean ignoreVendorDisplaySettings = Settings.Global.getInt(resolver, - DEVELOPMENT_IGNORE_VENDOR_DISPLAY_SETTINGS, 0) != 0; + final String displaySettingsPath = Settings.Global.getString(resolver, + DEVELOPMENT_WM_DISPLAY_SETTINGS_PATH); mDisplayWindowSettingsProvider = new DisplayWindowSettingsProvider(); - mDisplayWindowSettingsProvider.setVendorSettingsIgnored(ignoreVendorDisplaySettings); + if (displaySettingsPath != null) { + mDisplayWindowSettingsProvider.setBaseSettingsFilePath(displaySettingsPath); + } mDisplayWindowSettings = new DisplayWindowSettings(this, mDisplayWindowSettingsProvider); IntentFilter filter = new IntentFilter(); diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp index 6da9517743d2..d37c7b03dfb5 100644 --- a/services/core/jni/Android.bp +++ b/services/core/jni/Android.bp @@ -152,7 +152,7 @@ cc_defaults { "android.hardware.power.stats@1.0", "android.hardware.thermal@1.0", "android.hardware.tv.input@1.0", - "android.hardware.vibrator-cpp", + "android.hardware.vibrator-unstable-cpp", "android.hardware.vibrator@1.0", "android.hardware.vibrator@1.1", "android.hardware.vibrator@1.2", diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp index 9e0fb567975a..13450be73d88 100644 --- a/services/core/jni/com_android_server_input_InputManagerService.cpp +++ b/services/core/jni/com_android_server_input_InputManagerService.cpp @@ -238,16 +238,16 @@ public: /* --- InputReaderPolicyInterface implementation --- */ - virtual void getReaderConfiguration(InputReaderConfiguration* outConfig); - virtual std::shared_ptr<PointerControllerInterface> obtainPointerController(int32_t deviceId); - virtual void notifyInputDevicesChanged(const std::vector<InputDeviceInfo>& inputDevices); - virtual std::shared_ptr<KeyCharacterMap> getKeyboardLayoutOverlay( - const InputDeviceIdentifier& identifier); - virtual std::string getDeviceAlias(const InputDeviceIdentifier& identifier); - virtual TouchAffineTransformation getTouchAffineTransformation(JNIEnv *env, - jfloatArray matrixArr); - virtual TouchAffineTransformation getTouchAffineTransformation( - const std::string& inputDeviceDescriptor, int32_t surfaceRotation); + void getReaderConfiguration(InputReaderConfiguration* outConfig) override; + std::shared_ptr<PointerControllerInterface> obtainPointerController(int32_t deviceId) override; + void notifyInputDevicesChanged(const std::vector<InputDeviceInfo>& inputDevices) override; + std::shared_ptr<KeyCharacterMap> getKeyboardLayoutOverlay( + const InputDeviceIdentifier& identifier) override; + std::string getDeviceAlias(const InputDeviceIdentifier& identifier) override; + TouchAffineTransformation getTouchAffineTransformation(const std::string& inputDeviceDescriptor, + int32_t surfaceRotation) override; + + TouchAffineTransformation getTouchAffineTransformation(JNIEnv* env, jfloatArray matrixArr); /* --- InputDispatcherPolicyInterface implementation --- */ diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java index 05b1e4253bc0..ce61d50df1d9 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java @@ -101,9 +101,4 @@ abstract class BaseIDevicePolicyManager extends IDevicePolicyManager.Stub { public boolean canProfileOwnerResetPasswordWhenLocked(int userId) { return false; } - - public boolean hasKeyPair(String callerPackage, String alias) { - // STOPSHIP: implement delegation code in ArcDevicePolicyManagerWrapperService & nuke this. - return false; - } } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 01b8c0b99c68..513543e0aec8 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -218,8 +218,10 @@ import android.os.Process; import android.os.RecoverySystem; import android.os.RemoteCallback; import android.os.RemoteException; +import android.os.ResultReceiver; import android.os.ServiceManager; import android.os.ServiceSpecificException; +import android.os.ShellCallback; import android.os.SystemClock; import android.os.SystemProperties; import android.os.UserHandle; @@ -267,7 +269,6 @@ import android.view.inputmethod.InputMethodInfo; import com.android.internal.R; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.compat.IPlatformCompat; import com.android.internal.logging.MetricsLogger; import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; import com.android.internal.notification.SystemNotificationChannels; @@ -555,7 +556,6 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { private final LockSettingsInternal mLockSettingsInternal; private final DeviceAdminServiceController mDeviceAdminServiceController; private final OverlayPackagesProvider mOverlayPackagesProvider; - private final IPlatformCompat mIPlatformCompat; private final DevicePolicyCacheImpl mPolicyCache = new DevicePolicyCacheImpl(); private final DeviceStateCacheImpl mStateCache = new DeviceStateCacheImpl(); @@ -1163,11 +1163,6 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return LocalServices.getService(LockSettingsInternal.class); } - IPlatformCompat getIPlatformCompat() { - return IPlatformCompat.Stub.asInterface( - ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE)); - } - boolean hasUserSetupCompleted(DevicePolicyData userData) { return userData.mUserSetupComplete; } @@ -1438,7 +1433,6 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { mUsageStatsManagerInternal = Objects.requireNonNull( injector.getUsageStatsManagerInternal()); mIPackageManager = Objects.requireNonNull(injector.getIPackageManager()); - mIPlatformCompat = Objects.requireNonNull(injector.getIPlatformCompat()); mIPermissionManager = Objects.requireNonNull(injector.getIPermissionManager()); mTelephonyManager = Objects.requireNonNull(injector.getTelephonyManager()); @@ -3382,13 +3376,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } private boolean passwordQualityInvocationOrderCheckEnabled(String packageName, int userId) { - try { - return mIPlatformCompat.isChangeEnabledByPackageName(ADMIN_APP_PASSWORD_COMPLEXITY, - packageName, userId); - } catch (RemoteException e) { - Log.e(LOG_TAG, "Failed to get a response from PLATFORM_COMPAT_SERVICE", e); - } - return getTargetSdk(packageName, userId) > Build.VERSION_CODES.Q; + return mInjector.isChangeEnabled(ADMIN_APP_PASSWORD_COMPLEXITY, packageName, userId); } /** @@ -4302,11 +4290,17 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { updatePasswordQualityCacheForUserGroup(caller.getUserId()); saveSettingsLocked(caller.getUserId()); }); + + DevicePolicyEventLogger + .createEvent(DevicePolicyEnums.SET_PASSWORD_COMPLEXITY) + .setAdmin(admin.info.getPackageName()) + .setInt(passwordComplexity) + .setBoolean(calledOnParent) + .write(); } logPasswordComplexityRequiredIfSecurityLogEnabled(admin.info.getComponent(), caller.getUserId(), calledOnParent, passwordComplexity); } - //TODO: Log metrics. } private void logPasswordComplexityRequiredIfSecurityLogEnabled(ComponentName who, int userId, @@ -8741,6 +8735,14 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { pw.decreaseIndent(); } + @Override + public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err, + String[] args, ShellCallback callback, ResultReceiver resultReceiver) { + new DevicePolicyManagerServiceShellCommand(DevicePolicyManagerService.this).exec( + this, in, out, err, args, callback, resultReceiver); + + } + private String getEncryptionStatusName(int encryptionStatus) { switch (encryptionStatus) { case DevicePolicyManager.ENCRYPTION_STATUS_INACTIVE: @@ -11048,16 +11050,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } private boolean isSetSecureSettingLocationModeCheckEnabled(String packageName, int userId) { - long ident = mInjector.binderClearCallingIdentity(); - try { - return mIPlatformCompat.isChangeEnabledByPackageName(USE_SET_LOCATION_ENABLED, - packageName, userId); - } catch (RemoteException e) { - Log.e(LOG_TAG, "Failed to get a response from PLATFORM_COMPAT_SERVICE", e); - return getTargetSdk(packageName, userId) > Build.VERSION_CODES.Q; - } finally { - mInjector.binderRestoreCallingIdentity(ident); - } + return mInjector.isChangeEnabled(USE_SET_LOCATION_ENABLED, packageName, userId); } @Override diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java new file mode 100644 index 000000000000..0b0aee9afa6d --- /dev/null +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java @@ -0,0 +1,82 @@ +/* + * 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.server.devicepolicy; + +import android.app.admin.DevicePolicyManager; +import android.os.ShellCommand; + +import java.io.PrintWriter; +import java.util.Objects; + +final class DevicePolicyManagerServiceShellCommand extends ShellCommand { + + private static final String CMD_IS_SAFE_OPERATION = "is-operation-safe"; + + private final DevicePolicyManagerService mService; + + DevicePolicyManagerServiceShellCommand(DevicePolicyManagerService service) { + mService = Objects.requireNonNull(service); + } + + @Override + public void onHelp() { + try (PrintWriter pw = getOutPrintWriter();) { + pw.printf("DevicePolicyManager Service (device_policy) commands:\n\n"); + showHelp(pw); + } + } + + @Override + public int onCommand(String cmd) { + if (cmd == null) { + return handleDefaultCommands(cmd); + } + try (PrintWriter pw = getOutPrintWriter();) { + switch (cmd) { + case CMD_IS_SAFE_OPERATION: + return runIsSafeOperation(pw); + default: + return onInvalidCommand(pw, cmd); + } + } + } + + private int onInvalidCommand(PrintWriter pw, String cmd) { + if (super.handleDefaultCommands(cmd) == 0) { + return 0; + } + + pw.println("Usage: "); + showHelp(pw); + return -1; + } + + + private void showHelp(PrintWriter pw) { + pw.printf(" help\n"); + pw.printf(" Prints this help text.\n\n"); + pw.printf(" %s <OPERATION_ID>\n", CMD_IS_SAFE_OPERATION); + pw.printf(" Checks if the give operation is safe \n\n"); + } + + private int runIsSafeOperation(PrintWriter pw) { + int operation = Integer.parseInt(getNextArgRequired()); + boolean safe = mService.canExecute(operation); + pw.printf("Operation %s is %s\n", DevicePolicyManager.operationToString(operation), + safe ? "SAFE" : "UNSAFE"); + return 0; + } +} diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 10b3265cd081..6525e1126478 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -47,6 +47,7 @@ import android.content.res.Resources.Theme; import android.database.sqlite.SQLiteCompatibilityWalFlags; import android.database.sqlite.SQLiteGlobal; import android.graphics.GraphicsStatsService; +import android.graphics.Typeface; import android.hardware.display.DisplayManagerInternal; import android.net.ConnectivityManager; import android.net.ConnectivityModuleConnector; @@ -120,6 +121,7 @@ import com.android.server.display.color.ColorDisplayService; import com.android.server.dreams.DreamManagerService; import com.android.server.emergency.EmergencyAffordanceService; import com.android.server.gpu.GpuService; +import com.android.server.graphics.fonts.FontManagerService; import com.android.server.hdmi.HdmiControlService; import com.android.server.incident.IncidentCompanionService; import com.android.server.input.InputManagerService; @@ -673,6 +675,11 @@ public final class SystemServer implements Dumpable { SystemServerInitThreadPool tp = SystemServerInitThreadPool.start(); mDumper.addDumpable(tp); + // Load preinstalled system fonts for system server, so that WindowManagerService, etc + // can start using Typeface. Note that fonts are required not only for text rendering, + // but also for some text operations (e.g. TextUtils.makeSafeForPresentation()). + Typeface.loadPreinstalledSystemFontMap(); + // Attach JVMTI agent if this is a debuggable build and the system property is set. if (Build.IS_DEBUGGABLE) { // Property is of the form "library_path=parameters". @@ -1604,6 +1611,10 @@ public final class SystemServer implements Dumpable { } t.traceEnd(); + t.traceBegin("StartFontManagerService"); + mSystemServiceManager.startService(FontManagerService.Lifecycle.class); + t.traceEnd(); + t.traceBegin("StartTextServicesManager"); mSystemServiceManager.startService(TextServicesManagerService.Lifecycle.class); t.traceEnd(); diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java index 77090a8e0d83..e2c5e97b8896 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java @@ -4252,10 +4252,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { mContext.packageName = admin1.getPackageName(); mContext.applicationInfo = new ApplicationInfo(); - when(mContext.resources.getColor(eq(R.color.notification_action_list), anyObject())) - .thenReturn(Color.WHITE); - when(mContext.resources.getColor(eq(R.color.notification_material_background_color), - anyObject())).thenReturn(Color.WHITE); + when(mContext.resources.getColor(anyInt(), anyObject())).thenReturn(Color.WHITE); // setUp() adds a secondary user for CALLER_USER_HANDLE. Remove it as otherwise the // feature is disabled because there are non-affiliated secondary users. @@ -4301,10 +4298,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { setupDeviceOwner(); mContext.packageName = admin1.getPackageName(); mContext.applicationInfo = new ApplicationInfo(); - when(mContext.resources.getColor(eq(R.color.notification_action_list), anyObject())) - .thenReturn(Color.WHITE); - when(mContext.resources.getColor(eq(R.color.notification_material_background_color), - anyObject())).thenReturn(Color.WHITE); + when(mContext.resources.getColor(anyInt(), anyObject())).thenReturn(Color.WHITE); // setUp() adds a secondary user for CALLER_USER_HANDLE. Remove it as otherwise the // feature is disabled because there are non-affiliated secondary users. diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java index cb5ca04c1fde..603608b23172 100644 --- a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java +++ b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java @@ -40,6 +40,7 @@ import android.content.ContentResolver; import android.content.Context; import android.content.ContextWrapper; import android.database.ContentObserver; +import android.hardware.display.DisplayManager; import android.hardware.Sensor; import android.hardware.SensorEventListener; import android.hardware.SensorManager; @@ -402,7 +403,7 @@ public class DisplayModeDirectorTest { director.injectVotesByDisplay(votesByDisplay); assertThat(director.getModeSwitchingType()) - .isNotEqualTo(DisplayModeDirector.SWITCHING_TYPE_NONE); + .isNotEqualTo(DisplayManager.SWITCHING_TYPE_NONE); DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(displayId); assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(30); @@ -411,9 +412,9 @@ public class DisplayModeDirectorTest { assertThat(desiredSpecs.appRequestRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(60); assertThat(desiredSpecs.baseModeId).isEqualTo(30); - director.setModeSwitchingType(DisplayModeDirector.SWITCHING_TYPE_NONE); + director.setModeSwitchingType(DisplayManager.SWITCHING_TYPE_NONE); assertThat(director.getModeSwitchingType()) - .isEqualTo(DisplayModeDirector.SWITCHING_TYPE_NONE); + .isEqualTo(DisplayManager.SWITCHING_TYPE_NONE); desiredSpecs = director.getDesiredDisplayModeSpecs(displayId); assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(30); @@ -428,9 +429,9 @@ public class DisplayModeDirectorTest { final int displayId = 0; DisplayModeDirector director = createDirectorFromFpsRange(0, 90); - director.setModeSwitchingType(DisplayModeDirector.SWITCHING_TYPE_WITHIN_GROUPS); + director.setModeSwitchingType(DisplayManager.SWITCHING_TYPE_WITHIN_GROUPS); assertThat(director.getModeSwitchingType()) - .isEqualTo(DisplayModeDirector.SWITCHING_TYPE_WITHIN_GROUPS); + .isEqualTo(DisplayManager.SWITCHING_TYPE_WITHIN_GROUPS); DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(displayId); assertThat(desiredSpecs.allowGroupSwitching).isFalse(); } @@ -440,9 +441,9 @@ public class DisplayModeDirectorTest { final int displayId = 0; DisplayModeDirector director = createDirectorFromFpsRange(0, 90); - director.setModeSwitchingType(DisplayModeDirector.SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS); + director.setModeSwitchingType(DisplayManager.SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS); assertThat(director.getModeSwitchingType()) - .isEqualTo(DisplayModeDirector.SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS); + .isEqualTo(DisplayManager.SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS); DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(displayId); assertThat(desiredSpecs.allowGroupSwitching).isTrue(); } diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecConfigTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecConfigTest.java index de9a5e4f0fe7..d10e075c5136 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecConfigTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecConfigTest.java @@ -18,17 +18,13 @@ package com.android.server.hdmi; import static com.google.common.truth.Truth.assertThat; import static junit.framework.Assert.assertTrue; -import static junit.framework.Assert.fail; -import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.testng.Assert.assertThrows; -import android.annotation.NonNull; import android.content.Context; import android.hardware.hdmi.HdmiControlManager; -import android.os.Looper; import android.platform.test.annotations.Presubmit; import android.provider.Settings.Global; @@ -42,21 +38,15 @@ import org.junit.runners.JUnit4; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; - @SmallTest @Presubmit @RunWith(JUnit4.class) public final class HdmiCecConfigTest { private static final String TAG = "HdmiCecConfigTest"; - private static final int TIMEOUT_CONTENT_CHANGE_SEC = 4; - private Context mContext; @Mock private HdmiCecConfig.StorageAdapter mStorageAdapter; - @Mock private HdmiCecConfig.SettingChangeListener mSettingChangeListener; @Before public void setUp() throws Exception { @@ -1029,105 +1019,4 @@ public final class HdmiCecConfigTest { HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING, Integer.toString(HdmiControlManager.SYSTEM_AUDIO_MODE_MUTING_DISABLED)); } - - @Test - public void registerChangeListener_SharedPref_BasicSanity() { - HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings( - mContext, mStorageAdapter, - "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>" - + "<cec-settings>" - + " <setting name=\"system_audio_mode_muting\"" - + " value-type=\"int\"" - + " user-configurable=\"true\">" - + " <allowed-values>" - + " <value int-value=\"0\" />" - + " <value int-value=\"1\" />" - + " </allowed-values>" - + " <default-value int-value=\"1\" />" - + " </setting>" - + "</cec-settings>", null); - hdmiCecConfig.registerChangeListener( - HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING, - mSettingChangeListener); - hdmiCecConfig.setIntValue( - HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING, - HdmiControlManager.SYSTEM_AUDIO_MODE_MUTING_DISABLED); - verify(mSettingChangeListener).onChange( - HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING); - } - - @Test - public void removeChangeListener_SharedPref_BasicSanity() { - HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings( - mContext, mStorageAdapter, - "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>" - + "<cec-settings>" - + " <setting name=\"system_audio_mode_muting\"" - + " value-type=\"int\"" - + " user-configurable=\"true\">" - + " <allowed-values>" - + " <value int-value=\"0\" />" - + " <value int-value=\"1\" />" - + " </allowed-values>" - + " <default-value int-value=\"1\" />" - + " </setting>" - + "</cec-settings>", null); - hdmiCecConfig.registerChangeListener( - HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING, - mSettingChangeListener); - hdmiCecConfig.removeChangeListener( - HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING, - mSettingChangeListener); - hdmiCecConfig.setIntValue( - HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING, - HdmiControlManager.SYSTEM_AUDIO_MODE_MUTING_DISABLED); - verify(mSettingChangeListener, never()).onChange( - HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING); - } - - /** - * Externally modified Global Settings still need to be supported. This test verifies that - * setting change notification is being forwarded to listeners registered via HdmiCecConfig. - */ - @Test - public void globalSettingObserver_BasicSanity() throws Exception { - CountDownLatch notifyLatch = new CountDownLatch(1); - // Get current value of the setting in the system. - String val = Global.getString(mContext.getContentResolver(), Global.HDMI_CONTROL_ENABLED); - HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings( - mContext, mStorageAdapter, - "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>" - + "<cec-settings>" - + " <setting name=\"hdmi_cec_enabled\"" - + " value-type=\"int\"" - + " user-configurable=\"true\">" - + " <allowed-values>" - + " <value int-value=\"0\" />" - + " <value int-value=\"1\" />" - + " </allowed-values>" - + " <default-value int-value=\"1\" />" - + " </setting>" - + "</cec-settings>", null); - hdmiCecConfig.registerGlobalSettingsObserver(Looper.getMainLooper()); - HdmiCecConfig.SettingChangeListener latchUpdateListener = - new HdmiCecConfig.SettingChangeListener() { - @Override - public void onChange(@NonNull @HdmiControlManager.CecSettingName String setting) { - notifyLatch.countDown(); - assertThat(setting).isEqualTo(HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED); - } - }; - hdmiCecConfig.registerChangeListener( - HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED, - latchUpdateListener); - // Flip the value of the setting. - Global.putString(mContext.getContentResolver(), Global.HDMI_CONTROL_ENABLED, - ((val == null || val.equals("1")) ? "0" : "1")); - if (!notifyLatch.await(TIMEOUT_CONTENT_CHANGE_SEC, TimeUnit.SECONDS)) { - fail("Timed out waiting for the notify callback"); - } - hdmiCecConfig.unregisterGlobalSettingsObserver(); - // Restore the previous value of the setting in the system. - Global.putString(mContext.getContentResolver(), Global.HDMI_CONTROL_ENABLED, val); - } } diff --git a/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySavingStatsTest.java b/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySavingStatsTest.java index 72d6caf1a5be..133f630b7a74 100644 --- a/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySavingStatsTest.java +++ b/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySavingStatsTest.java @@ -25,6 +25,7 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import android.metrics.LogMaker; +import android.util.IndentingPrintWriter; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; @@ -73,7 +74,7 @@ public class BatterySavingStatsTest { void assertDumpable() { final ByteArrayOutputStream out = new ByteArrayOutputStream(); - dump(new PrintWriter(out), ""); // Just make sure it won't crash. + dump(new IndentingPrintWriter(new PrintWriter(out))); // Just make sure it won't crash. } void advanceClock(int minutes) { diff --git a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java index c23fb8028224..21396fd0516e 100644 --- a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java +++ b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java @@ -525,6 +525,7 @@ public class TimeDetectorStrategyImplTest { @Test public void testSuggestNetworkTime_autoTimeEnabled() { mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO) + .pokeAutoOriginPriorities(ORIGIN_NETWORK) .pokeAutoTimeDetectionEnabled(true); NetworkTimeSuggestion timeSuggestion = @@ -541,6 +542,7 @@ public class TimeDetectorStrategyImplTest { @Test public void testSuggestNetworkTime_autoTimeDisabled() { mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO) + .pokeAutoOriginPriorities(ORIGIN_NETWORK) .pokeAutoTimeDetectionEnabled(false); NetworkTimeSuggestion timeSuggestion = @@ -552,10 +554,26 @@ public class TimeDetectorStrategyImplTest { } @Test - public void testSuggestNetworkTime_telephonySuggestionsBeatNetworkSuggestions() { + public void networkTimeSuggestion_ignoredWhenReferencedTimeIsInThePast() { mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO) + .pokeAutoOriginPriorities(ORIGIN_NETWORK) .pokeAutoTimeDetectionEnabled(true); + Instant suggestedTime = TIME_LOWER_BOUND.minus(Duration.ofDays(1)); + NetworkTimeSuggestion timeSuggestion = mScript + .generateNetworkTimeSuggestion(suggestedTime); + + mScript.simulateNetworkTimeSuggestion(timeSuggestion) + .verifySystemClockWasNotSetAndResetCallTracking() + .assertLatestNetworkSuggestion(null); + } + + @Test + public void highPrioritySuggestionsShouldBeatLowerPrioritySuggestions() { + mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO) + .pokeAutoTimeDetectionEnabled(true) + .pokeAutoOriginPriorities(ORIGIN_TELEPHONY, ORIGIN_NETWORK); + // Three obviously different times that could not be mistaken for each other. Instant networkTime1 = ARBITRARY_TEST_TIME; Instant networkTime2 = ARBITRARY_TEST_TIME.plus(Duration.ofDays(30)); @@ -576,7 +594,7 @@ public class TimeDetectorStrategyImplTest { mScript.assertLatestTelephonySuggestion(ARBITRARY_SLOT_INDEX, null) .assertLatestNetworkSuggestion(networkTimeSuggestion1); assertEquals(networkTimeSuggestion1, mScript.peekLatestValidNetworkSuggestion()); - assertNull(mScript.peekBestTelephonySuggestion()); + assertNull("No telephony suggestions were made:", mScript.peekBestTelephonySuggestion()); // Simulate a little time passing. mScript.simulateTimePassing(smallTimeIncrementMillis) @@ -631,7 +649,9 @@ public class TimeDetectorStrategyImplTest { mScript.assertLatestTelephonySuggestion(ARBITRARY_SLOT_INDEX, telephonyTimeSuggestion) .assertLatestNetworkSuggestion(networkTimeSuggestion2); assertEquals(networkTimeSuggestion2, mScript.peekLatestValidNetworkSuggestion()); - assertNull(mScript.peekBestTelephonySuggestion()); + assertNull( + "Telephony suggestion should be expired:", + mScript.peekBestTelephonySuggestion()); // Toggle auto-time off and on to force the detection logic to run. mScript.simulateAutoTimeDetectionToggle() @@ -646,27 +666,16 @@ public class TimeDetectorStrategyImplTest { mScript.assertLatestTelephonySuggestion(ARBITRARY_SLOT_INDEX, telephonyTimeSuggestion) .assertLatestNetworkSuggestion(networkTimeSuggestion2); assertEquals(networkTimeSuggestion2, mScript.peekLatestValidNetworkSuggestion()); - assertNull(mScript.peekBestTelephonySuggestion()); - } - - @Test - public void networkTimeSuggestion_ignoredWhenReferencedTimeIsInThePast() { - mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO) - .pokeAutoTimeDetectionEnabled(true); - - Instant suggestedTime = TIME_LOWER_BOUND.minus(Duration.ofDays(1)); - NetworkTimeSuggestion timeSuggestion = mScript - .generateNetworkTimeSuggestion(suggestedTime); - - mScript.simulateNetworkTimeSuggestion(timeSuggestion) - .verifySystemClockWasNotSetAndResetCallTracking() - .assertLatestNetworkSuggestion(null); + assertNull( + "Telephony suggestion should still be expired:", + mScript.peekBestTelephonySuggestion()); } @Test public void whenAllTimeSuggestionsAreAvailable_higherPriorityWins_lowerPriorityComesFirst() { mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO) - .pokeAutoTimeDetectionEnabled(true); + .pokeAutoTimeDetectionEnabled(true) + .pokeAutoOriginPriorities(ORIGIN_TELEPHONY, ORIGIN_NETWORK); Instant networkTime = ARBITRARY_TEST_TIME; Instant telephonyTime = ARBITRARY_TEST_TIME.plus(Duration.ofDays(30)); @@ -686,7 +695,8 @@ public class TimeDetectorStrategyImplTest { @Test public void whenAllTimeSuggestionsAreAvailable_higherPriorityWins_higherPriorityComesFirst() { mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO) - .pokeAutoTimeDetectionEnabled(true); + .pokeAutoTimeDetectionEnabled(true) + .pokeAutoOriginPriorities(ORIGIN_TELEPHONY, ORIGIN_NETWORK); Instant networkTime = ARBITRARY_TEST_TIME; Instant telephonyTime = ARBITRARY_TEST_TIME.plus(Duration.ofDays(30)); @@ -706,7 +716,8 @@ public class TimeDetectorStrategyImplTest { @Test public void whenHighestPrioritySuggestionIsNotAvailable_fallbacksToNext() { mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO) - .pokeAutoTimeDetectionEnabled(true); + .pokeAutoTimeDetectionEnabled(true) + .pokeAutoOriginPriorities(ORIGIN_TELEPHONY, ORIGIN_NETWORK); NetworkTimeSuggestion timeSuggestion = mScript.generateNetworkTimeSuggestion(ARBITRARY_TEST_TIME); @@ -720,7 +731,7 @@ public class TimeDetectorStrategyImplTest { public void suggestionsFromSourceNotListedInPrioritiesList_areIgnored() { mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO) .pokeAutoTimeDetectionEnabled(true) - .pokeAutoOriginPriorities(new int[]{ORIGIN_TELEPHONY}); + .pokeAutoOriginPriorities(ORIGIN_TELEPHONY); NetworkTimeSuggestion timeSuggestion = mScript.generateNetworkTimeSuggestion( ARBITRARY_TEST_TIME); @@ -730,6 +741,19 @@ public class TimeDetectorStrategyImplTest { .verifySystemClockWasNotSetAndResetCallTracking(); } + @Test + public void autoOriginPrioritiesList_doesNotAffectManualSuggestion() { + mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO) + .pokeAutoTimeDetectionEnabled(false) + .pokeAutoOriginPriorities(ORIGIN_TELEPHONY); + + ManualTimeSuggestion timeSuggestion = + mScript.generateManualTimeSuggestion(ARBITRARY_TEST_TIME); + + mScript.simulateManualTimeSuggestion(timeSuggestion, true /* expectedResult */) + .verifySystemClockWasSetAndResetCallTracking(ARBITRARY_TEST_TIME.toEpochMilli()); + } + /** * A fake implementation of TimeDetectorStrategy.Callback. Besides tracking changes and behaving * like the real thing should, it also asserts preconditions. @@ -761,7 +785,7 @@ public class TimeDetectorStrategyImplTest { } @Override - public int[] getAutoOriginPriorities() { + public int[] autoOriginPriorities() { return mAutoOriginPriorities; } @@ -886,8 +910,8 @@ public class TimeDetectorStrategyImplTest { return this; } - Script pokeAutoOriginPriorities(@Origin int[] autoOriginPriorites) { - mFakeCallback.pokeAutoOriginPriorities(autoOriginPriorites); + Script pokeAutoOriginPriorities(@Origin int... autoOriginPriorities) { + mFakeCallback.pokeAutoOriginPriorities(autoOriginPriorities); return this; } diff --git a/services/tests/servicestests/src/com/android/server/utils/WatchableTester.java b/services/tests/servicestests/src/com/android/server/utils/WatchableTester.java new file mode 100644 index 000000000000..590df3c18f5a --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/utils/WatchableTester.java @@ -0,0 +1,87 @@ +/* + * 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.server.utils; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +/** + * A class to count the number of notifications received. + */ +public class WatchableTester extends Watcher { + + // The count of changes. + public int mChanges = 0; + + // The change count at the last verifyChangeReported() call. + public int mLastChangeCount = 0; + + // The single Watchable that this monitors. + public final Watchable mWatched; + + // The key, used for messages + public String mKey; + + // Clear the changes count, for when the tester is reused. + public void clear() { + mChanges = 0; + } + + /** + * Create the WatchableTester with a Watcher and a key. The key is used for logging + * test failures. + * @param w The {@link Watchable} under test + * @param k A key that is prefixed to any test failures. + **/ + public WatchableTester(Watchable w, String k) { + mWatched = w; + mKey = k; + } + + // Listen for events + public void register() { + mWatched.registerObserver(this); + } + + // Stop listening for events + public void unregister() { + mWatched.unregisterObserver(this); + } + + // Count the number of notifications received. + @Override + public void onChange(Watchable what) { + mChanges++; + } + + // Verify the count. + public void verify(int want, String msg) { + assertEquals(mKey + " " + msg, want, mChanges); + } + + // Verify that at least one change was reported since the last verify. The actual + // number of changes is not important. This resets the count of changes. + public void verifyChangeReported(String msg) { + assertTrue(mKey + " " + msg, mLastChangeCount < mChanges); + mLastChangeCount = mChanges; + } + + // Verify that no change was reported since the last verify. + public void verifyNoChangeReported(String msg) { + assertTrue(mKey + " " + msg, mLastChangeCount == mChanges); + } +} diff --git a/services/tests/servicestests/src/com/android/server/utils/WatcherTest.java b/services/tests/servicestests/src/com/android/server/utils/WatcherTest.java index 40575e4cf16f..9bea9d4cedbd 100644 --- a/services/tests/servicestests/src/com/android/server/utils/WatcherTest.java +++ b/services/tests/servicestests/src/com/android/server/utils/WatcherTest.java @@ -18,15 +18,10 @@ package com.android.server.utils; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; - -import android.content.Context; -import android.platform.test.annotations.Presubmit; +import static org.junit.Assert.fail; import androidx.test.filters.SmallTest; -import com.android.internal.util.Preconditions; -import com.android.internal.util.TraceBuffer; - import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -42,56 +37,76 @@ import org.junit.Test; @SmallTest public class WatcherTest { + // A counter to generate unique IDs for Leaf elements. + private int mLeafId = 0; + + // Useful indices used int the tests. + private static final int INDEX_A = 1; + private static final int INDEX_B = 2; + private static final int INDEX_C = 3; + private static final int INDEX_D = 4; + // A small Watchable leaf node - private class Leaf extends WatchableImpl { - private int datum = 0; + private class Leaf extends WatchableImpl implements Snappable { + private int mId; + private int mDatum; + + Leaf() { + mDatum = 0; + mId = mLeafId++; + } + void set(int i) { - if (datum != i) { - datum = i; + if (mDatum != i) { + mDatum = i; dispatchChange(this); } } + int get() { + return mDatum; + } void tick() { - set(datum + 1); + set(mDatum + 1); } - } - - // A top-most watcher. It counts the number of notifications that it receives. - private class Tester extends Watcher { - // The count of changes. - public int changes = 0; - - // The single Watchable that this monitors. - public final Watchable mWatched; - - // The key, used for messages - public String mKey; - - // Create the Tester with a Watcher - public Tester(Watchable w, String k) { - mWatched = w; - mKey = k; + public Leaf snapshot() { + Leaf result = new Leaf(); + result.mDatum = mDatum; + result.mId = mId; + result.seal(); + return result; } - - // Listen for events - public void register() { - mWatched.registerObserver(this); + @Override + public boolean equals(Object o) { + if (o instanceof Leaf) { + return mDatum == ((Leaf) o).mDatum && mId == ((Leaf) o).mId; + } else { + return false; + } } - - // Stop listening for events - public void unregister() { - mWatched.unregisterObserver(this); + @Override + public String toString() { + return "Leaf(" + mDatum + "," + mId + ")"; } + } - // Count the number of notifications received. - @Override - public void onChange(Watchable what) { - changes++; + // Execute the {@link Runnable} and if {@link UnsupportedOperationException} is + // thrown, do nothing. If no exception is thrown, fail the test. + private void verifySealed(String msg, Runnable test) { + try { + test.run(); + fail(msg + " should be sealed"); + } catch (IllegalStateException e) { + // The exception was expected. } + } - // Verify the count. - public void verify(int want, String msg) { - assertEquals(mKey + " " + msg, want, changes); + // Execute the {@link Runnable} and if {@link UnsupportedOperationException} is + // thrown, fail the test. If no exception is thrown, do nothing. + private void verifyNotSealed(String msg, Runnable test) { + try { + test.run(); + } catch (IllegalStateException e) { + fail(msg + " should be not sealed"); } } @@ -104,168 +119,212 @@ public class WatcherTest { } @Test - public void test_notify() { - - Tester tester; + public void testBasicBehavior() { + WatchableTester tester; // Create a few leaves - Leaf a = new Leaf(); - Leaf b = new Leaf(); - Leaf c = new Leaf(); - Leaf d = new Leaf(); + Leaf leafA = new Leaf(); // Basic test. Create a leaf and verify that changes to the leaf get notified to // the tester. - tester = new Tester(a, "Leaf"); + tester = new WatchableTester(leafA, "Leaf"); tester.verify(0, "Initial leaf - no registration"); - a.tick(); + leafA.tick(); tester.verify(0, "Updates with no registration"); tester.register(); - a.tick(); + leafA.tick(); tester.verify(1, "Updates with registration"); - a.tick(); - a.tick(); + leafA.tick(); + leafA.tick(); tester.verify(3, "Updates with registration"); + // Create a snapshot. Verify that the snapshot matches the + Leaf leafASnapshot = leafA.snapshot(); + assertEquals("Leaf snapshot", leafA.get(), leafASnapshot.get()); + leafA.tick(); + assertTrue(leafA.get() != leafASnapshot.get()); + tester.verify(4, "Tick after snapshot"); + verifySealed("Leaf", ()->leafASnapshot.tick()); // Add the same leaf to more than one tester. Verify that a change to the leaf is seen by // all registered listeners. - Tester buddy1 = new Tester(a, "Leaf2"); - Tester buddy2 = new Tester(a, "Leaf3"); + tester.clear(); + WatchableTester buddy1 = new WatchableTester(leafA, "Leaf2"); + WatchableTester buddy2 = new WatchableTester(leafA, "Leaf3"); buddy1.verify(0, "Initial leaf - no registration"); buddy2.verify(0, "Initial leaf - no registration"); - a.tick(); - tester.verify(4, "Updates with buddies"); + leafA.tick(); + tester.verify(1, "Updates with buddies"); buddy1.verify(0, "Updates - no registration"); buddy2.verify(0, "Updates - no registration"); buddy1.register(); buddy2.register(); buddy1.verify(0, "No updates - registered"); buddy2.verify(0, "No updates - registered"); - a.tick(); + leafA.tick(); buddy1.verify(1, "First update"); buddy2.verify(1, "First update"); buddy1.unregister(); - a.tick(); + leafA.tick(); buddy1.verify(1, "Second update - unregistered"); buddy2.verify(2, "Second update"); + } - buddy1 = null; - buddy2 = null; + @Test + public void testWatchedArrayMap() { + WatchableTester tester; - final int INDEX_A = 1; - final int INDEX_B = 2; - final int INDEX_C = 3; - final int INDEX_D = 4; + // Create a few leaves + Leaf leafA = new Leaf(); + Leaf leafB = new Leaf(); + Leaf leafC = new Leaf(); + Leaf leafD = new Leaf(); // Test WatchedArrayMap - WatchedArrayMap<Integer, Leaf> am = new WatchedArrayMap<>(); - am.put(INDEX_A, a); - am.put(INDEX_B, b); - tester = new Tester(am, "WatchedArrayMap"); + WatchedArrayMap<Integer, Leaf> array = new WatchedArrayMap<>(); + array.put(INDEX_A, leafA); + array.put(INDEX_B, leafB); + tester = new WatchableTester(array, "WatchedArrayMap"); tester.verify(0, "Initial array - no registration"); - a.tick(); + leafA.tick(); tester.verify(0, "Updates with no registration"); tester.register(); tester.verify(0, "Updates with no registration"); - a.tick(); + leafA.tick(); tester.verify(1, "Updates with registration"); - b.tick(); + leafB.tick(); tester.verify(2, "Updates with registration"); - am.remove(INDEX_B); + array.remove(INDEX_B); tester.verify(3, "Removed b"); - b.tick(); + leafB.tick(); tester.verify(3, "Updates with b not watched"); - am.put(INDEX_B, b); - am.put(INDEX_C, b); + array.put(INDEX_B, leafB); + array.put(INDEX_C, leafB); tester.verify(5, "Added b twice"); - b.tick(); + leafB.tick(); tester.verify(6, "Changed b - single notification"); - am.remove(INDEX_C); + array.remove(INDEX_C); tester.verify(7, "Removed first b"); - b.tick(); + leafB.tick(); tester.verify(8, "Changed b - single notification"); - am.remove(INDEX_B); + array.remove(INDEX_B); tester.verify(9, "Removed second b"); - b.tick(); + leafB.tick(); tester.verify(9, "Updated b - no change"); - am.clear(); + array.clear(); tester.verify(10, "Cleared array"); - b.tick(); + leafB.tick(); tester.verify(10, "Change to b not in array"); // Special methods - am.put(INDEX_C, c); + array.put(INDEX_C, leafC); tester.verify(11, "Added c"); - c.tick(); + leafC.tick(); tester.verify(12, "Ticked c"); - am.setValueAt(am.indexOfKey(INDEX_C), d); + array.setValueAt(array.indexOfKey(INDEX_C), leafD); tester.verify(13, "Replaced c with d"); - c.tick(); - d.tick(); + leafC.tick(); + leafD.tick(); tester.verify(14, "Ticked d and c (c not registered)"); - am = null; + // Snapshot + { + final WatchedArrayMap<Integer, Leaf> arraySnap = array.snapshot(); + tester.verify(14, "Generate snapshot (no changes)"); + // Verify that the snapshot is a proper copy of the source. + assertEquals("WatchedArrayMap snap same size", + array.size(), arraySnap.size()); + for (int i = 0; i < array.size(); i++) { + for (int j = 0; j < arraySnap.size(); j++) { + assertTrue("WatchedArrayMap elements differ", + array.valueAt(i) != arraySnap.valueAt(j)); + } + assertTrue("WatchedArrayMap element copy", + array.valueAt(i).equals(arraySnap.valueAt(i))); + } + leafD.tick(); + tester.verify(15, "Tick after snapshot"); + // Verify that the snapshot is sealed + verifySealed("WatchedArrayMap", ()->arraySnap.put(INDEX_A, leafA)); + } + // Recreate the snapshot since the test corrupted it. + { + final WatchedArrayMap<Integer, Leaf> arraySnap = array.snapshot(); + // Verify that elements are also snapshots + final Leaf arraySnapElement = arraySnap.valueAt(0); + verifySealed("ArraySnapshotElement", ()->arraySnapElement.tick()); + } + } + + @Test + public void testWatchedSparseArray() { + WatchableTester tester; + + // Create a few leaves + Leaf leafA = new Leaf(); + Leaf leafB = new Leaf(); + Leaf leafC = new Leaf(); + Leaf leafD = new Leaf(); // Test WatchedSparseArray - WatchedSparseArray<Leaf> sa = new WatchedSparseArray<>(); - sa.put(INDEX_A, a); - sa.put(INDEX_B, b); - tester = new Tester(sa, "WatchedSparseArray"); + WatchedSparseArray<Leaf> array = new WatchedSparseArray<>(); + array.put(INDEX_A, leafA); + array.put(INDEX_B, leafB); + tester = new WatchableTester(array, "WatchedSparseArray"); tester.verify(0, "Initial array - no registration"); - a.tick(); + leafA.tick(); tester.verify(0, "Updates with no registration"); tester.register(); tester.verify(0, "Updates with no registration"); - a.tick(); + leafA.tick(); tester.verify(1, "Updates with registration"); - b.tick(); + leafB.tick(); tester.verify(2, "Updates with registration"); - sa.remove(INDEX_B); + array.remove(INDEX_B); tester.verify(3, "Removed b"); - b.tick(); + leafB.tick(); tester.verify(3, "Updates with b not watched"); - sa.put(INDEX_B, b); - sa.put(INDEX_C, b); + array.put(INDEX_B, leafB); + array.put(INDEX_C, leafB); tester.verify(5, "Added b twice"); - b.tick(); + leafB.tick(); tester.verify(6, "Changed b - single notification"); - sa.remove(INDEX_C); + array.remove(INDEX_C); tester.verify(7, "Removed first b"); - b.tick(); + leafB.tick(); tester.verify(8, "Changed b - single notification"); - sa.remove(INDEX_B); + array.remove(INDEX_B); tester.verify(9, "Removed second b"); - b.tick(); - tester.verify(9, "Updated b - no change"); - sa.clear(); + leafB.tick(); + tester.verify(9, "Updated leafB - no change"); + array.clear(); tester.verify(10, "Cleared array"); - b.tick(); + leafB.tick(); tester.verify(10, "Change to b not in array"); // Special methods - sa.put(INDEX_A, a); - sa.put(INDEX_B, b); - sa.put(INDEX_C, c); + array.put(INDEX_A, leafA); + array.put(INDEX_B, leafB); + array.put(INDEX_C, leafC); tester.verify(13, "Added c"); - c.tick(); + leafC.tick(); tester.verify(14, "Ticked c"); - sa.setValueAt(sa.indexOfKey(INDEX_C), d); + array.setValueAt(array.indexOfKey(INDEX_C), leafD); tester.verify(15, "Replaced c with d"); - c.tick(); - d.tick(); + leafC.tick(); + leafD.tick(); tester.verify(16, "Ticked d and c (c not registered)"); - sa.append(INDEX_D, c); + array.append(INDEX_D, leafC); tester.verify(17, "Append c"); - c.tick(); - d.tick(); + leafC.tick(); + leafD.tick(); tester.verify(19, "Ticked d and c"); - assertEquals("Verify four elements", 4, sa.size()); + assertEquals("Verify four elements", 4, array.size()); // Figure out which elements are at which indices. Leaf[] x = new Leaf[4]; for (int i = 0; i < 4; i++) { - x[i] = sa.valueAt(i); + x[i] = array.valueAt(i); } - sa.removeAtRange(0, 2); + array.removeAtRange(0, 2); tester.verify(20, "Removed two elements in one operation"); x[0].tick(); x[1].tick(); @@ -274,31 +333,77 @@ public class WatcherTest { x[3].tick(); tester.verify(22, "Ticked two remaining elements"); - sa = null; + // Snapshot + { + final WatchedSparseArray<Leaf> arraySnap = array.snapshot(); + tester.verify(22, "Generate snapshot (no changes)"); + // Verify that the snapshot is a proper copy of the source. + assertEquals("WatchedSparseArray snap same size", + array.size(), arraySnap.size()); + for (int i = 0; i < array.size(); i++) { + for (int j = 0; j < arraySnap.size(); j++) { + assertTrue("WatchedSparseArray elements differ", + array.valueAt(i) != arraySnap.valueAt(j)); + } + assertTrue("WatchedArrayMap element copy", + array.valueAt(i).equals(arraySnap.valueAt(i))); + } + leafD.tick(); + tester.verify(23, "Tick after snapshot"); + // Verify that the array snapshot is sealed + verifySealed("WatchedSparseArray", ()->arraySnap.put(INDEX_A, leafB)); + } + // Recreate the snapshot since the test corrupted it. + { + final WatchedSparseArray<Leaf> arraySnap = array.snapshot(); + // Verify that elements are also snapshots + final Leaf arraySnapElement = arraySnap.valueAt(0); + verifySealed("ArraySnapshotElement", ()->arraySnapElement.tick()); + } + } + + @Test + public void testWatchedSparseBooleanArray() { + WatchableTester tester; // Test WatchedSparseBooleanArray - WatchedSparseBooleanArray sb = new WatchedSparseBooleanArray(); - tester = new Tester(sb, "WatchedSparseBooleanArray"); + WatchedSparseBooleanArray array = new WatchedSparseBooleanArray(); + tester = new WatchableTester(array, "WatchedSparseBooleanArray"); tester.verify(0, "Initial array - no registration"); - sb.put(INDEX_A, true); + array.put(INDEX_A, true); tester.verify(0, "Updates with no registration"); tester.register(); tester.verify(0, "Updates with no registration"); - sb.put(INDEX_B, true); + array.put(INDEX_B, true); tester.verify(1, "Updates with registration"); - sb.put(INDEX_B, true); + array.put(INDEX_B, true); tester.verify(1, "Null update"); - sb.put(INDEX_B, false); - sb.put(INDEX_C, true); + array.put(INDEX_B, false); + array.put(INDEX_C, true); tester.verify(3, "Updates with registration"); // Special methods - sb.put(INDEX_C, true); + array.put(INDEX_C, true); tester.verify(3, "Added true, no change"); - sb.setValueAt(sb.indexOfKey(INDEX_C), false); + array.setValueAt(array.indexOfKey(INDEX_C), false); tester.verify(4, "Replaced true with false"); - sb.append(INDEX_D, true); + array.append(INDEX_D, true); tester.verify(5, "Append true"); - sb = null; + // Snapshot + { + WatchedSparseBooleanArray arraySnap = array.snapshot(); + tester.verify(5, "Generate snapshot"); + // Verify that the snapshot is a proper copy of the source. + assertEquals("WatchedSparseBooleanArray snap same size", + array.size(), arraySnap.size()); + for (int i = 0; i < array.size(); i++) { + assertEquals("WatchedSparseArray element copy", + array.valueAt(i), arraySnap.valueAt(i)); + } + array.put(INDEX_D, false); + tester.verify(6, "Tick after snapshot"); + // Verify that the array is sealed + verifySealed("WatchedSparseBooleanArray", ()->arraySnap.put(INDEX_D, false)); + } } } diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java index 8c2038b59a0f..b3116d970725 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java @@ -37,7 +37,6 @@ import android.os.UserHandle; import android.os.UserManager; import android.util.IntArray; import android.util.TypedXmlPullParser; -import android.util.TypedXmlSerializer; import android.util.Xml; import com.android.server.UiServiceTestCase; @@ -47,7 +46,6 @@ import org.junit.Before; import org.junit.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -import org.xmlpull.v1.XmlPullParser; import java.io.BufferedInputStream; import java.io.ByteArrayInputStream; @@ -144,24 +142,24 @@ public class NotificationAssistantsTest extends UiServiceTestCase { ComponentName component1 = ComponentName.unflattenFromString("package/Component1"); ComponentName component2 = ComponentName.unflattenFromString("package/Component2"); mAssistants.setPackageOrComponentEnabled(component1.flattenToString(), mZero.id, true, - true); + true, true); verify(mNm, never()).setNotificationAssistantAccessGrantedForUserInternal( - any(ComponentName.class), eq(mZero.id), anyBoolean()); + any(ComponentName.class), eq(mZero.id), anyBoolean(), anyBoolean()); mAssistants.setPackageOrComponentEnabled(component2.flattenToString(), mZero.id, true, - true); + true, true); verify(mNm, times(1)).setNotificationAssistantAccessGrantedForUserInternal( - component1, mZero.id, false); + component1, mZero.id, false, true); } @Test public void testSetPackageOrComponentEnabled_samePackage() throws Exception { ComponentName component1 = ComponentName.unflattenFromString("package/Component1"); mAssistants.setPackageOrComponentEnabled(component1.flattenToString(), mZero.id, true, - true); + true, true); mAssistants.setPackageOrComponentEnabled(component1.flattenToString(), mZero.id, true, - true); + true, true); verify(mNm, never()).setNotificationAssistantAccessGrantedForUserInternal( - any(ComponentName.class), eq(mZero.id), anyBoolean()); + any(ComponentName.class), eq(mZero.id), anyBoolean(), anyBoolean()); } } diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index 60832374ac28..5cf529a239dc 100755 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -172,7 +172,6 @@ import com.android.internal.config.sysui.SystemUiDeviceConfigFlags; import com.android.internal.logging.InstanceIdSequence; import com.android.internal.logging.InstanceIdSequenceFake; import com.android.internal.statusbar.NotificationVisibility; -import com.android.internal.util.FastXmlSerializer; import com.android.server.DeviceIdleInternal; import com.android.server.LocalServices; import com.android.server.SystemService; @@ -196,8 +195,6 @@ import org.mockito.InOrder; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.mockito.stubbing.Answer; -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlSerializer; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; @@ -357,12 +354,14 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { @Override protected void setNotificationAssistantAccessGrantedForUserInternal( - ComponentName assistant, int userId, boolean granted) { + ComponentName assistant, int userId, boolean granted, boolean userSet) { if (mNotificationAssistantAccessGrantedCallback != null) { - mNotificationAssistantAccessGrantedCallback.onGranted(assistant, userId, granted); + mNotificationAssistantAccessGrantedCallback.onGranted(assistant, userId, granted, + userSet); return; } - super.setNotificationAssistantAccessGrantedForUserInternal(assistant, userId, granted); + super.setNotificationAssistantAccessGrantedForUserInternal(assistant, userId, granted, + userSet); } @Override @@ -376,7 +375,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } interface NotificationAssistantAccessGrantedCallback { - void onGranted(ComponentName assistant, int userId, boolean granted); + void onGranted(ComponentName assistant, int userId, boolean granted, boolean userSet); } } @@ -899,7 +898,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mService.setDefaultAssistantForUser(userId); verify(mAssistants).setPackageOrComponentEnabled( - eq(testComponent), eq(userId), eq(true), eq(true)); + eq(testComponent), eq(userId), eq(true), eq(true), eq(false)); } @Test @@ -2909,7 +2908,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { verify(mContext, times(1)).sendBroadcastAsUser(any(), eq(user), any()); verify(mAssistants, times(1)).setPackageOrComponentEnabled( - c.flattenToString(), user.getIdentifier(), true, true); + c.flattenToString(), user.getIdentifier(), true, true, true); verify(mAssistants).setUserSet(10, true); verify(mConditionProviders, times(1)).setPackageOrComponentEnabled( c.flattenToString(), user.getIdentifier(), false, true); @@ -2953,7 +2952,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { verify(mConditionProviders, times(1)).setPackageOrComponentEnabled( c.getPackageName(), user.getIdentifier(), true, true); verify(mAssistants, never()).setPackageOrComponentEnabled( - any(), anyInt(), anyBoolean(), anyBoolean()); + any(), anyInt(), anyBoolean(), anyBoolean(), anyBoolean()); verify(mListeners, never()).setPackageOrComponentEnabled( any(), anyInt(), anyBoolean(), anyBoolean()); } @@ -2968,7 +2967,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { verify(mConditionProviders, times(1)).setPackageOrComponentEnabled( c.flattenToString(), 0, false, true, true); verify(mAssistants, never()).setPackageOrComponentEnabled( - any(), anyInt(), anyBoolean(), anyBoolean()); + any(), anyInt(), anyBoolean(), anyBoolean(), anyBoolean()); } @Test @@ -2983,7 +2982,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mBinderService.setNotificationAssistantAccessGranted(c, true); verify(mAssistants, times(1)).setPackageOrComponentEnabled( - c.flattenToString(), 0, true, true); + c.flattenToString(), 0, true, true, true); verify(mConditionProviders, times(1)).setPackageOrComponentEnabled( c.flattenToString(), 0, false, true); verify(mListeners, never()).setPackageOrComponentEnabled( @@ -3005,9 +3004,9 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mBinderService.setNotificationAssistantAccessGranted(c, true); verify(mAssistants, times(1)).setPackageOrComponentEnabled( - c.flattenToString(), 0, true, true); + c.flattenToString(), 0, true, true, true); verify(mAssistants, times(1)).setPackageOrComponentEnabled( - c.flattenToString(), 10, true, true); + c.flattenToString(), 10, true, true, true); verify(mConditionProviders, times(1)).setPackageOrComponentEnabled( c.flattenToString(), 0, false, true); verify(mConditionProviders, times(1)).setPackageOrComponentEnabled( @@ -3031,7 +3030,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mBinderService.setNotificationAssistantAccessGranted(null, true); verify(mAssistants, times(1)).setPackageOrComponentEnabled( - c.flattenToString(), 0, true, false); + c.flattenToString(), 0, true, false, true); verify(mConditionProviders, times(1)).setPackageOrComponentEnabled( c.flattenToString(), 0, false, false); verify(mListeners, never()).setPackageOrComponentEnabled( @@ -3055,7 +3054,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { null, user.getIdentifier(), true); verify(mAssistants, times(1)).setPackageOrComponentEnabled( - c.flattenToString(), user.getIdentifier(), true, false); + c.flattenToString(), user.getIdentifier(), true, false, true); verify(mAssistants).setUserSet(10, true); verify(mConditionProviders, times(1)).setPackageOrComponentEnabled( c.flattenToString(), user.getIdentifier(), false, false); @@ -3084,9 +3083,9 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { null, user.getIdentifier(), true); verify(mAssistants, times(1)).setPackageOrComponentEnabled( - c.flattenToString(), user.getIdentifier(), true, false); + c.flattenToString(), user.getIdentifier(), true, false, true); verify(mAssistants, times(1)).setPackageOrComponentEnabled( - c.flattenToString(), ui10.id, true, false); + c.flattenToString(), ui10.id, true, false, true); verify(mAssistants).setUserSet(0, true); verify(mAssistants).setUserSet(10, true); verify(mConditionProviders, times(1)).setPackageOrComponentEnabled( @@ -3106,7 +3105,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { verify(mConditionProviders, times(1)).setPackageOrComponentEnabled( c.getPackageName(), 0, true, true); verify(mAssistants, never()).setPackageOrComponentEnabled( - any(), anyInt(), anyBoolean(), anyBoolean()); + any(), anyInt(), anyBoolean(), anyBoolean(), anyBoolean()); verify(mListeners, never()).setPackageOrComponentEnabled( any(), anyInt(), anyBoolean(), anyBoolean()); } @@ -3191,7 +3190,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { verify(mConditionProviders, times(1)).setPackageOrComponentEnabled( c.flattenToString(), 0, false, true); verify(mAssistants, times(1)).setPackageOrComponentEnabled( - c.flattenToString(), 0, true, true); + c.flattenToString(), 0, true, true, true); } @Test @@ -3207,7 +3206,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { verify(mConditionProviders, times(1)).setPackageOrComponentEnabled( c.getPackageName(), 0, true, true); verify(mAssistants, never()).setPackageOrComponentEnabled( - any(), anyInt(), anyBoolean(), anyBoolean()); + any(), anyInt(), anyBoolean(), anyBoolean(), anyBoolean()); } @Test @@ -5419,7 +5418,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mService.setDefaultAssistantForUser(0); verify(mNotificationAssistantAccessGrantedCallback) - .onGranted(eq(xmlConfig), eq(0), eq(true)); + .onGranted(eq(xmlConfig), eq(0), eq(true), eq(false)); } @Test @@ -5441,7 +5440,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mService.setDefaultAssistantForUser(0); verify(mNotificationAssistantAccessGrantedCallback) - .onGranted(eq(deviceConfig), eq(0), eq(true)); + .onGranted(eq(deviceConfig), eq(0), eq(true), eq(false)); } @Test @@ -5464,7 +5463,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mService.setDefaultAssistantForUser(0); verify(mNotificationAssistantAccessGrantedCallback) - .onGranted(eq(xmlConfig), eq(0), eq(true)); + .onGranted(eq(xmlConfig), eq(0), eq(true), eq(false)); } @Test diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java index 28c31843de57..2f34f708a562 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java @@ -384,6 +384,7 @@ public class ActivityRecordTests extends WindowTestsBase { .build(); final Task task = activity.getTask(); activity.setState(DESTROYED, "Testing"); + clearInvocations(mAtm.getLifecycleManager()); final Configuration newConfig = new Configuration(task.getConfiguration()); newConfig.orientation = newConfig.orientation == ORIENTATION_PORTRAIT @@ -408,6 +409,7 @@ public class ActivityRecordTests extends WindowTestsBase { activity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(), activity.getConfiguration())); + clearInvocations(mAtm.getLifecycleManager()); final Configuration newConfig = new Configuration(activity.getConfiguration()); final int shortSide = Math.min(newConfig.screenWidthDp, newConfig.screenHeightDp); final int longSide = Math.max(newConfig.screenWidthDp, newConfig.screenHeightDp); @@ -596,6 +598,7 @@ public class ActivityRecordTests extends WindowTestsBase { final Task stack = new TaskBuilder(mSupervisor).setCreateActivity(true).build(); try { + clearInvocations(mAtm.getLifecycleManager()); doReturn(false).when(stack).isTranslucent(any()); assertTrue(task.shouldBeVisible(null /* starting */)); diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java index 8e6b6fab19eb..266ce5ba0e68 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java @@ -20,6 +20,7 @@ import static android.os.Process.INVALID_UID; import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; +import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG; import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR; import static android.view.WindowManager.LayoutParams.TYPE_PRESENTATION; import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR; @@ -82,7 +83,7 @@ public class DisplayAreaPolicyBuilderTest { private TestWindowManagerPolicy mPolicy = new TestWindowManagerPolicy(null, null); private WindowManagerService mWms; private RootDisplayArea mRoot; - private DisplayArea<WindowContainer> mImeContainer; + private DisplayArea.Tokens mImeContainer; private DisplayContent mDisplayContent; private TaskDisplayArea mDefaultTaskDisplayArea; private List<TaskDisplayArea> mTaskDisplayAreaList; @@ -95,7 +96,7 @@ public class DisplayAreaPolicyBuilderTest { public void setup() { mWms = mSystemServices.getWindowManagerService(); mRoot = new SurfacelessDisplayAreaRoot(mWms); - mImeContainer = new DisplayArea<>(mWms, ABOVE_TASKS, "Ime"); + mImeContainer = new DisplayArea.Tokens(mWms, ABOVE_TASKS, "ImeContainer"); mDisplayContent = mock(DisplayContent.class); mDefaultTaskDisplayArea = new TaskDisplayArea(mDisplayContent, mWms, "Tasks", FEATURE_DEFAULT_TASK_CONTAINER); @@ -148,6 +149,10 @@ public class DisplayAreaPolicyBuilderTest { // The IME is below both foo and bar. assertThat(fooDescendantMatcher.matches(mImeContainer)).isTrue(); assertThat(barDescendantMatcher.matches(mImeContainer)).isTrue(); + assertThat(policy.findAreaForToken(tokenOfType(TYPE_INPUT_METHOD))) + .isEqualTo(mImeContainer); + assertThat(policy.findAreaForToken(tokenOfType(TYPE_INPUT_METHOD_DIALOG))) + .isEqualTo(mImeContainer); List<DisplayArea<?>> actualOrder = collectLeafAreas(mRoot); Map<DisplayArea<?>, Set<Integer>> zSets = calculateZSets(policy, mImeContainer, @@ -547,7 +552,7 @@ public class DisplayAreaPolicyBuilderTest { private Map<DisplayArea<?>, Set<Integer>> calculateZSets( DisplayAreaPolicyBuilder.Result policy, - DisplayArea<WindowContainer> ime, + DisplayArea.Tokens ime, DisplayArea<Task> tasks) { Map<DisplayArea<?>, Set<Integer>> zSets = new HashMap<>(); int[] types = {TYPE_STATUS_BAR, TYPE_NAVIGATION_BAR, TYPE_PRESENTATION, diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyTests.java index 5a47493c12cd..496b2b744712 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyTests.java @@ -69,7 +69,7 @@ public class DisplayAreaPolicyTests { WindowManagerService wms = mSystemServices.getWindowManagerService(); mRoot = new SurfacelessDisplayAreaRoot(wms); spyOn(mRoot); - DisplayArea<WindowContainer> ime = new DisplayArea<>(wms, ABOVE_TASKS, "Ime"); + DisplayArea.Tokens ime = new DisplayArea.Tokens(wms, ABOVE_TASKS, "Ime"); DisplayContent displayContent = mock(DisplayContent.class); doReturn(true).when(displayContent).isTrusted(); mTaskDisplayArea1 = new TaskDisplayArea(displayContent, wms, "Tasks1", @@ -143,7 +143,7 @@ public class DisplayAreaPolicyTests { FEATURE_VENDOR_FIRST + 5); final TaskDisplayArea taskDisplayArea5 = new TaskDisplayArea(displayContent, wms, "Tasks5", FEATURE_VENDOR_FIRST + 6); - final DisplayArea<WindowContainer> ime = new DisplayArea<>(wms, ABOVE_TASKS, "Ime"); + final DisplayArea.Tokens ime = new DisplayArea.Tokens(wms, ABOVE_TASKS, "Ime"); final DisplayAreaPolicy policy = new DisplayAreaPolicyBuilder() .setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(root) .setImeContainer(ime) diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaProviderTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaProviderTest.java index 351426ae78b2..2d289808dd26 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaProviderTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaProviderTest.java @@ -81,7 +81,7 @@ public class DisplayAreaProviderTest { @Override public DisplayAreaPolicy instantiate(WindowManagerService wmService, DisplayContent content, - RootDisplayArea root, DisplayArea<? extends WindowContainer> imeContainer) { + RootDisplayArea root, DisplayArea.Tokens imeContainer) { throw new RuntimeException("test stub"); } } diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsProviderTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsProviderTests.java index 554160ccbb82..7c4f7db48022 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsProviderTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsProviderTests.java @@ -16,7 +16,9 @@ package com.android.server.wm; +import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; +import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.view.WindowManager.DISPLAY_IME_POLICY_LOCAL; import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; @@ -28,7 +30,6 @@ import static org.junit.Assert.assertTrue; import android.annotation.Nullable; import android.platform.test.annotations.Presubmit; import android.util.TypedXmlPullParser; -import android.util.TypedXmlSerializer; import android.util.Xml; import android.view.Display; import android.view.DisplayAddress; @@ -69,7 +70,8 @@ public class DisplayWindowSettingsProviderTests extends WindowTestsBase { private static final File TEST_FOLDER = getInstrumentation().getTargetContext().getCacheDir(); - private TestStorage mBaseSettingsStorage; + private TestStorage mDefaultVendorSettingsStorage; + private TestStorage mSecondaryVendorSettingsStorage; private TestStorage mOverrideSettingsStorage; private DisplayContent mPrimaryDisplay; @@ -79,7 +81,8 @@ public class DisplayWindowSettingsProviderTests extends WindowTestsBase { public void setUp() throws Exception { deleteRecursively(TEST_FOLDER); - mBaseSettingsStorage = new TestStorage(); + mDefaultVendorSettingsStorage = new TestStorage(); + mSecondaryVendorSettingsStorage = new TestStorage(); mOverrideSettingsStorage = new TestStorage(); mPrimaryDisplay = mWm.getDefaultDisplayContentLocked(); @@ -122,7 +125,7 @@ public class DisplayWindowSettingsProviderTests extends WindowTestsBase { // Update settings with new value, should trigger write to injector. DisplayWindowSettingsProvider provider = new DisplayWindowSettingsProvider( - mBaseSettingsStorage, mOverrideSettingsStorage); + mDefaultVendorSettingsStorage, mOverrideSettingsStorage); SettingsEntry overrideSettings = provider.getOverrideSettings(mPrimaryDisplayInfo); overrideSettings.mForcedDensity = 200; provider.updateOverrideSettings(mPrimaryDisplayInfo, overrideSettings); @@ -162,12 +165,55 @@ public class DisplayWindowSettingsProviderTests extends WindowTestsBase { } @Test + public void testReadingDisplaySettingsFromStorage_secondayVendorDisplaySettingsLocation() { + final String displayIdentifier = mSecondaryDisplay.getDisplayInfo().uniqueId; + prepareSecondaryDisplaySettings(displayIdentifier); + + final DisplayWindowSettingsProvider provider = + new DisplayWindowSettingsProvider(mDefaultVendorSettingsStorage, + mOverrideSettingsStorage); + + // Expected settings should be empty because the default is to read from the primary vendor + // settings location. + SettingsEntry expectedSettings = new SettingsEntry(); + assertEquals(expectedSettings, provider.getSettings(mPrimaryDisplay.getDisplayInfo())); + + // Now switch to secondary vendor settings and assert proper settings. + provider.setBaseSettingsStorage(mSecondaryVendorSettingsStorage); + expectedSettings.mWindowingMode = WINDOWING_MODE_FULLSCREEN; + assertEquals(expectedSettings, provider.getSettings(mPrimaryDisplay.getDisplayInfo())); + + // Switch back to primary and assert settings are empty again. + provider.setBaseSettingsStorage(mDefaultVendorSettingsStorage); + expectedSettings.mWindowingMode = WINDOWING_MODE_UNDEFINED; + assertEquals(expectedSettings, provider.getSettings(mPrimaryDisplay.getDisplayInfo())); + } + + @Test + public void testReadingDisplaySettingsFromStorage_overrideSettingsTakePrecedenceOverVendor() { + final String displayIdentifier = mSecondaryDisplay.getDisplayInfo().uniqueId; + prepareOverrideDisplaySettings(displayIdentifier); + prepareSecondaryDisplaySettings(displayIdentifier); + + final DisplayWindowSettingsProvider provider = + new DisplayWindowSettingsProvider(mDefaultVendorSettingsStorage, + mOverrideSettingsStorage); + provider.setBaseSettingsStorage(mSecondaryVendorSettingsStorage); + + // The windowing mode should be set to WINDOWING_MODE_PINNED because the override settings + // take precedence over the vendor provided settings. + SettingsEntry expectedSettings = new SettingsEntry(); + expectedSettings.mWindowingMode = WINDOWING_MODE_PINNED; + assertEquals(expectedSettings, provider.getSettings(mPrimaryDisplay.getDisplayInfo())); + } + + @Test public void testWritingDisplaySettingsToStorage() throws Exception { final DisplayInfo secondaryDisplayInfo = mSecondaryDisplay.getDisplayInfo(); // Write some settings to storage. DisplayWindowSettingsProvider provider = new DisplayWindowSettingsProvider( - mBaseSettingsStorage, mOverrideSettingsStorage); + mDefaultVendorSettingsStorage, mOverrideSettingsStorage); SettingsEntry overrideSettings = provider.getOverrideSettings(secondaryDisplayInfo); overrideSettings.mShouldShowSystemDecors = true; overrideSettings.mImePolicy = DISPLAY_IME_POLICY_LOCAL; @@ -195,7 +241,7 @@ public class DisplayWindowSettingsProviderTests extends WindowTestsBase { // Write some settings to storage. DisplayWindowSettingsProvider provider = new DisplayWindowSettingsProvider( - mBaseSettingsStorage, mOverrideSettingsStorage); + mDefaultVendorSettingsStorage, mOverrideSettingsStorage); SettingsEntry overrideSettings = provider.getOverrideSettings(secondaryDisplayInfo); overrideSettings.mShouldShowSystemDecors = true; overrideSettings.mImePolicy = DISPLAY_IME_POLICY_LOCAL; @@ -236,10 +282,29 @@ public class DisplayWindowSettingsProviderTests extends WindowTestsBase { mOverrideSettingsStorage.setReadStream(is); } + /** + * Prepares display settings and stores in {@link #mSecondaryVendorSettingsStorage}. Uses + * provided display identifier and stores windowingMode=WINDOWING_MODE_FULLSCREEN. + */ + private void prepareSecondaryDisplaySettings(String displayIdentifier) { + String contents = "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n" + + "<display-settings>\n"; + if (displayIdentifier != null) { + contents += " <display\n" + + " name=\"" + displayIdentifier + "\"\n" + + " windowingMode=\"" + WINDOWING_MODE_FULLSCREEN + "\"/>\n"; + } + contents += "</display-settings>\n"; + + final InputStream is = new ByteArrayInputStream(contents.getBytes(StandardCharsets.UTF_8)); + mSecondaryVendorSettingsStorage.setReadStream(is); + } + private void readAndAssertExpectedSettings(DisplayContent displayContent, SettingsEntry expectedSettings) { final DisplayWindowSettingsProvider provider = - new DisplayWindowSettingsProvider(mBaseSettingsStorage, mOverrideSettingsStorage); + new DisplayWindowSettingsProvider(mDefaultVendorSettingsStorage, + mOverrideSettingsStorage); assertEquals(expectedSettings, provider.getSettings(displayContent.getDisplayInfo())); } diff --git a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java index 536ef48065a5..83e3d22345e1 100644 --- a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java +++ b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java @@ -299,6 +299,7 @@ public class SystemServicesTestRule implements TestRule { doNothing().when(mWmService.mRoot).ensureActivitiesVisible(any(), anyInt(), anyBoolean(), anyBoolean(), anyBoolean()); spyOn(mWmService.mDisplayWindowSettings); + spyOn(mWmService.mDisplayWindowSettingsProvider); // Setup factory classes to prevent calls to native code. mTransaction = spy(StubTransaction.class); diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerSettingsTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerSettingsTests.java index ba144dd367d9..4e1b3510bdaa 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerSettingsTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerSettingsTests.java @@ -20,7 +20,7 @@ import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDO import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_SIZECOMPAT_FREEFORM; import static android.provider.Settings.Global.DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS; import static android.provider.Settings.Global.DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES; -import static android.provider.Settings.Global.DEVELOPMENT_IGNORE_VENDOR_DISPLAY_SETTINGS; +import static android.provider.Settings.Global.DEVELOPMENT_WM_DISPLAY_SETTINGS_PATH; import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; @@ -58,8 +58,8 @@ public class WindowManagerSettingsTests extends WindowTestsBase { @Test public void testForceDesktopModeOnExternalDisplays() { - try (SettingsSession forceDesktopModeSession = new - SettingsSession(DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS)) { + try (BoolSettingsSession forceDesktopModeSession = new + BoolSettingsSession(DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS)) { final boolean forceDesktopMode = !forceDesktopModeSession.getSetting(); final Uri forceDesktopModeUri = forceDesktopModeSession.setSetting(forceDesktopMode); mWm.mSettingsObserver.onChange(false, forceDesktopModeUri); @@ -70,8 +70,8 @@ public class WindowManagerSettingsTests extends WindowTestsBase { @Test public void testFreeformWindow() { - try (SettingsSession freeformWindowSession = new - SettingsSession(DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT)) { + try (BoolSettingsSession freeformWindowSession = new + BoolSettingsSession(DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT)) { final boolean curFreeformWindow = freeformWindowSession.getSetting(); final boolean newFreeformWindow = !curFreeformWindow; final Uri freeformWindowUri = freeformWindowSession.setSetting(newFreeformWindow); @@ -84,8 +84,8 @@ public class WindowManagerSettingsTests extends WindowTestsBase { @Test public void testFreeformWindow_valueChanged_updatesDisplaySettings() { - try (SettingsSession freeformWindowSession = new - SettingsSession(DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT)) { + try (BoolSettingsSession freeformWindowSession = new + BoolSettingsSession(DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT)) { final boolean curFreeformWindow = freeformWindowSession.getSetting(); final boolean newFreeformWindow = !curFreeformWindow; final Uri freeformWindowUri = freeformWindowSession.setSetting(newFreeformWindow); @@ -106,8 +106,8 @@ public class WindowManagerSettingsTests extends WindowTestsBase { @Test public void testForceResizableMode() { - try (SettingsSession forceResizableSession = new - SettingsSession(DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES)) { + try (BoolSettingsSession forceResizableSession = new + BoolSettingsSession(DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES)) { final boolean forceResizableMode = !forceResizableSession.getSetting(); final Uri forceResizableUri = forceResizableSession.setSetting(forceResizableMode); @@ -119,8 +119,8 @@ public class WindowManagerSettingsTests extends WindowTestsBase { @Test public void testEnableSizeCompatFreeform() { - try (SettingsSession enableSizeCompatFreeformSession = new - SettingsSession(DEVELOPMENT_ENABLE_SIZECOMPAT_FREEFORM)) { + try (BoolSettingsSession enableSizeCompatFreeformSession = new + BoolSettingsSession(DEVELOPMENT_ENABLE_SIZECOMPAT_FREEFORM)) { final boolean enableSizeCompatFreeform = !enableSizeCompatFreeformSession.getSetting(); final Uri enableSizeCompatFreeformUri = @@ -132,21 +132,22 @@ public class WindowManagerSettingsTests extends WindowTestsBase { } @Test - public void testEnabledIgnoreVendorDisplaySettings() { - try (SettingsSession ignoreVendorDisplaySettingsSession = new - SettingsSession(DEVELOPMENT_IGNORE_VENDOR_DISPLAY_SETTINGS)) { - final boolean ignoreVendorDisplaySettings = - !ignoreVendorDisplaySettingsSession.getSetting(); - final Uri ignoreVendorDisplaySettingUri = - ignoreVendorDisplaySettingsSession.setSetting(ignoreVendorDisplaySettings); + public void testChangeBaseDisplaySettingsPath() { + try (StringSettingsSession baseDisplaySettingsPathSession = new + StringSettingsSession(DEVELOPMENT_WM_DISPLAY_SETTINGS_PATH)) { + final String path = baseDisplaySettingsPathSession.getSetting() + "-test"; + final Uri baseDisplaySettingsPathUri = baseDisplaySettingsPathSession.setSetting(path); clearInvocations(mWm.mRoot); clearInvocations(mWm.mDisplayWindowSettings); + clearInvocations(mWm.mDisplayWindowSettingsProvider); - mWm.mSettingsObserver.onChange(false /* selfChange */, ignoreVendorDisplaySettingUri); + mWm.mSettingsObserver.onChange(false /* selfChange */, baseDisplaySettingsPathUri); - assertEquals(mWm.mDisplayWindowSettingsProvider.getVendorSettingsIgnored(), - ignoreVendorDisplaySettings); + ArgumentCaptor<String> pathCaptor = ArgumentCaptor.forClass(String.class); + verify(mWm.mDisplayWindowSettingsProvider).setBaseSettingsFilePath( + pathCaptor.capture()); + assertEquals(path, pathCaptor.getValue()); ArgumentCaptor<DisplayContent> captor = ArgumentCaptor.forClass(DisplayContent.class); @@ -161,14 +162,14 @@ public class WindowManagerSettingsTests extends WindowTestsBase { } } - private class SettingsSession implements AutoCloseable { + private class BoolSettingsSession implements AutoCloseable { private static final int SETTING_VALUE_OFF = 0; private static final int SETTING_VALUE_ON = 1; private final String mSettingName; private final int mInitialValue; - SettingsSession(String name) { + BoolSettingsSession(String name) { mSettingName = name; mInitialValue = getSetting() ? SETTING_VALUE_ON : SETTING_VALUE_OFF; } @@ -192,4 +193,32 @@ public class WindowManagerSettingsTests extends WindowTestsBase { setSetting(mInitialValue == SETTING_VALUE_ON); } } + + private class StringSettingsSession implements AutoCloseable { + private final String mSettingName; + private final String mInitialValue; + + StringSettingsSession(String name) { + mSettingName = name; + mInitialValue = getSetting(); + } + + String getSetting() { + return Settings.Global.getString(getContentResolver(), mSettingName); + } + + Uri setSetting(String value) { + Settings.Global.putString(getContentResolver(), mSettingName, value); + return Settings.Global.getUriFor(mSettingName); + } + + private ContentResolver getContentResolver() { + return getInstrumentation().getTargetContext().getContentResolver(); + } + + @Override + public void close() { + setSetting(mInitialValue); + } + } } diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java index 4c49c6e4f0b9..14a62d1a4ff0 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java @@ -27,6 +27,8 @@ import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; import static android.os.Process.SYSTEM_UID; import static android.view.View.VISIBLE; +import static android.view.WindowManager.DISPLAY_IME_POLICY_FALLBACK_DISPLAY; +import static android.view.WindowManager.DISPLAY_IME_POLICY_LOCAL; import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW; import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW; @@ -40,8 +42,6 @@ import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR; import static android.view.WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE; import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR; import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; -import static android.view.WindowManager.DISPLAY_IME_POLICY_LOCAL; -import static android.view.WindowManager.DISPLAY_IME_POLICY_FALLBACK_DISPLAY; import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; @@ -64,7 +64,6 @@ import android.content.Context; import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; -import android.content.res.Configuration; import android.hardware.display.DisplayManager; import android.os.Build; import android.os.Bundle; @@ -831,13 +830,14 @@ class WindowTestsBase extends SystemServiceTestsBase { if (mLaunchTaskBehind) { options = ActivityOptions.makeTaskLaunchBehind(); } + final ActivityRecord activity = new ActivityRecord.Builder(mService) + .setLaunchedFromPid(mLaunchedFromPid) + .setLaunchedFromUid(mLaunchedFromUid) + .setIntent(intent) + .setActivityInfo(aInfo) + .setActivityOptions(options) + .build(); - final ActivityRecord activity = new ActivityRecord(mService, null /* caller */, - mLaunchedFromPid /* launchedFromPid */, mLaunchedFromUid /* launchedFromUid */, - null, null, intent, null, aInfo /*aInfo*/, new Configuration(), - null /* resultTo */, null /* resultWho */, 0 /* reqCode */, - false /*componentSpecified*/, false /* rootVoiceInteraction */, - mService.mTaskSupervisor, options, null /* sourceRecord */); spyOn(activity); if (mTask != null) { // fullscreen value is normally read from resources in ctor, so for testing we need diff --git a/telecomm/java/android/telecom/BluetoothCallQualityReport.java b/telecomm/java/android/telecom/BluetoothCallQualityReport.java new file mode 100644 index 000000000000..10339a818205 --- /dev/null +++ b/telecomm/java/android/telecom/BluetoothCallQualityReport.java @@ -0,0 +1,256 @@ +/* + * 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.telecom; + +import android.annotation.ElapsedRealtimeLong; +import android.annotation.IntRange; +import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.os.Bundle; +import android.os.Parcel; +import android.os.Parcelable; + +/** + * This class represents the quality report that bluetooth framework sends + * whenever there's a bad voice quality is detected from their side. + * It is sent as part of a call event via {@link Call#sendCallEvent(String, Bundle)} + * associated with extra EXTRA_BLUETOOTH_CALL_QUALITY_REPORT. + * Note that this report will be sent only during an active voice/voip call. + * @hide + */ +@SystemApi +public final class BluetoothCallQualityReport implements Parcelable { + + /** + * Event that is sent via {@link Call#sendCallEvent(String, Bundle)} for a call quality report + */ + public static final String EVENT_BLUETOOTH_CALL_QUALITY_REPORT = + "android.telecom.event.BLUETOOTH_CALL_QUALITY_REPORT"; + + /** + * Extra key sent with {@link Call#sendCallEvent(String, Bundle)} + */ + public static final String EXTRA_BLUETOOTH_CALL_QUALITY_REPORT = + "android.telecom.extra.BLUETOOTH_CALL_QUALITY_REPORT"; + + private final long mSentTimestampMillis; + private final boolean mChoppyVoice; + private final int mRssiDbm; + private final int mSnrDb; + private final int mRetransmittedPacketsCount; + private final int mPacketsNotReceivedCount; + private final int mNegativeAcknowledgementCount; + + /** + * @return Time in milliseconds since the epoch. Designates when report was sent. + * Used to determine whether this report arrived too late to be useful. + */ + public @ElapsedRealtimeLong long getSentTimestampMillis() { + return mSentTimestampMillis; + } + + /** + * @return {@code true} if bluetooth hardware detects voice is choppy + */ + public boolean isChoppyVoice() { + return mChoppyVoice; + } + + /** + * @return Received Signal Strength Indication (RSSI) value in dBm. + * This value shall be an absolute received signal strength value. + */ + public @IntRange(from = -127, to = 20) int getRssiDbm() { + return mRssiDbm; + } + + /** + * @return Signal-to-Noise Ratio (SNR) value in dB. + * The controller shall provide the average SNR of all the channels currently used by the link. + */ + public int getSnrDb() { + return mSnrDb; + } + + /** + * @return The number of retransmissions since the last event. + * This count shall be reset after it is reported. + */ + public @IntRange(from = 0) int getRetransmittedPacketsCount() { + return mRetransmittedPacketsCount; + } + + /** + * @return No RX count since the last event. + * The count increases when no packet is received at the scheduled time slot or the received + * packet is corrupted. + * This count shall be reset after it is reported. + */ + public @IntRange(from = 0) int getPacketsNotReceivedCount() { + return mPacketsNotReceivedCount; + } + + /** + * @return NAK (Negative Acknowledge) count since the last event. + * This count shall be reset after it is reported. + */ + public @IntRange(from = 0) int getNegativeAcknowledgementCount() { + return mNegativeAcknowledgementCount; + } + + // + // Parcelable implementation + // + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel out, int flags) { + out.writeLong(mSentTimestampMillis); + out.writeBoolean(mChoppyVoice); + out.writeInt(mRssiDbm); + out.writeInt(mSnrDb); + out.writeInt(mRetransmittedPacketsCount); + out.writeInt(mPacketsNotReceivedCount); + out.writeInt(mNegativeAcknowledgementCount); + } + + public static final @android.annotation.NonNull Creator<BluetoothCallQualityReport> CREATOR = + new Creator<BluetoothCallQualityReport>() { + @Override + public BluetoothCallQualityReport createFromParcel(Parcel in) { + return new BluetoothCallQualityReport(in); + } + + @Override + public BluetoothCallQualityReport[] newArray(int size) { + return new BluetoothCallQualityReport[size]; + } + }; + + /** + * Builder class for {@link ConnectionRequest} + */ + public static final class Builder { + private long mSentTimestampMillis; + private boolean mChoppyVoice; + private int mRssiDbm; + private int mSnrDb; + private int mRetransmittedPacketsCount; + private int mPacketsNotReceivedCount; + private int mNegativeAcknowledgementCount; + + public Builder() { } + + /** + * Set the time when report was sent in milliseconds since the epoch. + * @param sentTimestampMillis + */ + public @NonNull Builder setSentTimestampMillis(long sentTimestampMillis) { + mSentTimestampMillis = sentTimestampMillis; + return this; + } + + /** + * Set if bluetooth hardware detects voice is choppy + * @param choppyVoice + */ + public @NonNull Builder setChoppyVoice(boolean choppyVoice) { + mChoppyVoice = choppyVoice; + return this; + } + + /** + * Set Received Signal Strength Indication (RSSI) value in dBm. + * @param rssiDbm + */ + public @NonNull Builder setRssiDbm(int rssiDbm) { + mRssiDbm = rssiDbm; + return this; + } + + /** + * Set Signal-to-Noise Ratio (SNR) value in dB. + * @param snrDb + */ + public @NonNull Builder setSnrDb(int snrDb) { + mSnrDb = snrDb; + return this; + } + + /** + * Set The number of retransmissions since the last event. + * @param retransmittedPacketsCount + */ + public @NonNull Builder setRetransmittedPacketsCount( + int retransmittedPacketsCount) { + mRetransmittedPacketsCount = retransmittedPacketsCount; + return this; + } + + /** + * Set No RX count since the last event. + * @param packetsNotReceivedCount + */ + public @NonNull Builder setPacketsNotReceivedCount( + int packetsNotReceivedCount) { + mPacketsNotReceivedCount = packetsNotReceivedCount; + return this; + } + + /** + * Set NAK (Negative Acknowledge) count since the last event. + * @param negativeAcknowledgementCount + */ + public @NonNull Builder setNegativeAcknowledgementCount( + int negativeAcknowledgementCount) { + mNegativeAcknowledgementCount = negativeAcknowledgementCount; + return this; + } + + /** + * Build the {@link BluetoothCallQualityReport} + * @return Result of the builder + */ + public @NonNull BluetoothCallQualityReport build() { + return new BluetoothCallQualityReport(this); + } + } + + private BluetoothCallQualityReport(Parcel in) { + mSentTimestampMillis = in.readLong(); + mChoppyVoice = in.readBoolean(); + mRssiDbm = in.readInt(); + mSnrDb = in.readInt(); + mRetransmittedPacketsCount = in.readInt(); + mPacketsNotReceivedCount = in.readInt(); + mNegativeAcknowledgementCount = in.readInt(); + } + + private BluetoothCallQualityReport(Builder builder) { + mSentTimestampMillis = builder.mSentTimestampMillis; + mChoppyVoice = builder.mChoppyVoice; + mRssiDbm = builder.mRssiDbm; + mSnrDb = builder.mSnrDb; + mRetransmittedPacketsCount = builder.mRetransmittedPacketsCount; + mPacketsNotReceivedCount = builder.mPacketsNotReceivedCount; + mNegativeAcknowledgementCount = builder.mNegativeAcknowledgementCount; + } +} diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index baee4736b527..0939bf02a039 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -4012,6 +4012,17 @@ public class CarrierConfigManager { "default_preferred_apn_name_string"; /** + * Indicates if the carrier supports call composer. + */ + public static final String KEY_SUPPORTS_CALL_COMPOSER_BOOL = "supports_call_composer_bool"; + + /** + * Indicates the carrier server url that serves the call composer picture. + */ + public static final String KEY_CALL_COMPOSER_PICTURE_SERVER_URL_STRING = + "call_composer_picture_server_url_string"; + + /** * For Android 11, provide a temporary solution for OEMs to use the lower of the two MTU values * for IPv4 and IPv6 if both are sent. * TODO: remove in later release @@ -4563,6 +4574,8 @@ public class CarrierConfigManager { sDefaults.putStringArray(KEY_MISSED_INCOMING_CALL_SMS_PATTERN_STRING_ARRAY, new String[0]); sDefaults.putBoolean(KEY_DISABLE_DUN_APN_WHILE_ROAMING_WITH_PRESET_APN_BOOL, false); sDefaults.putString(KEY_DEFAULT_PREFERRED_APN_NAME_STRING, ""); + sDefaults.putBoolean(KEY_SUPPORTS_CALL_COMPOSER_BOOL, false); + sDefaults.putString(KEY_CALL_COMPOSER_PICTURE_SERVER_URL_STRING, ""); sDefaults.putBoolean(KEY_USE_LOWER_MTU_VALUE_IF_BOTH_RECEIVED, false); } diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index d963970a3d2b..886ec33af2b8 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -11694,18 +11694,6 @@ public class TelephonyManager { } /** - * In this mode, modem will not send specified indications when screen is off. - * @hide - */ - public static final int INDICATION_UPDATE_MODE_NORMAL = 1; - - /** - * In this mode, modem will still send specified indications when screen is off. - * @hide - */ - public static final int INDICATION_UPDATE_MODE_IGNORE_SCREEN_OFF = 2; - - /** * The indication for signal strength update. * @hide */ diff --git a/telephony/java/android/telephony/ims/aidl/ISubscribeResponseCallback.aidl b/telephony/java/android/telephony/ims/aidl/ISubscribeResponseCallback.aidl index 4deaba1b7a49..a14199365b07 100644 --- a/telephony/java/android/telephony/ims/aidl/ISubscribeResponseCallback.aidl +++ b/telephony/java/android/telephony/ims/aidl/ISubscribeResponseCallback.aidl @@ -32,5 +32,5 @@ oneway interface ISubscribeResponseCallback { void onNetworkResponse(int code, in String reason); void onNotifyCapabilitiesUpdate(in List<String> pidfXmls); void onResourceTerminated(in List<RcsContactTerminatedReason> uriTerminatedReason); - void onTerminated(in String reason, in String retryAfter); + void onTerminated(in String reason, long retryAfterMilliseconds); } diff --git a/telephony/java/android/telephony/ims/aidl/RcsSubscribeResponseAidlWrapper.java b/telephony/java/android/telephony/ims/aidl/RcsSubscribeResponseAidlWrapper.java index 37588ed98585..1fb339c0cf89 100644 --- a/telephony/java/android/telephony/ims/aidl/RcsSubscribeResponseAidlWrapper.java +++ b/telephony/java/android/telephony/ims/aidl/RcsSubscribeResponseAidlWrapper.java @@ -86,9 +86,9 @@ public class RcsSubscribeResponseAidlWrapper implements SubscribeResponseCallbac } @Override - public void onTerminated(String reason, String retryAfter) throws ImsException { + public void onTerminated(String reason, long retryAfterMilliseconds) throws ImsException { try { - mResponseBinder.onTerminated(reason, retryAfter); + mResponseBinder.onTerminated(reason, retryAfterMilliseconds); } catch (RemoteException e) { } } diff --git a/telephony/java/android/telephony/ims/feature/MmTelFeature.java b/telephony/java/android/telephony/ims/feature/MmTelFeature.java index e8234178be9d..9a3f592480d1 100644 --- a/telephony/java/android/telephony/ims/feature/MmTelFeature.java +++ b/telephony/java/android/telephony/ims/feature/MmTelFeature.java @@ -231,8 +231,9 @@ public class MmTelFeature extends ImsFeature { * The capabilities that are used in MmTelFeature are defined as * {@link MmTelCapabilities#CAPABILITY_TYPE_VOICE}, * {@link MmTelCapabilities#CAPABILITY_TYPE_VIDEO}, - * {@link MmTelCapabilities#CAPABILITY_TYPE_UT}, and - * {@link MmTelCapabilities#CAPABILITY_TYPE_SMS}. + * {@link MmTelCapabilities#CAPABILITY_TYPE_UT}, + * {@link MmTelCapabilities#CAPABILITY_TYPE_SMS}, and + * {@link MmTelCapabilities#CAPABILITY_TYPE_CALL_COMPOSER}. * * The capabilities of this MmTelFeature will be set by the framework. */ @@ -275,7 +276,8 @@ public class MmTelFeature extends ImsFeature { CAPABILITY_TYPE_VOICE, CAPABILITY_TYPE_VIDEO, CAPABILITY_TYPE_UT, - CAPABILITY_TYPE_SMS + CAPABILITY_TYPE_SMS, + CAPABILITY_TYPE_CALL_COMPOSER }) @Retention(RetentionPolicy.SOURCE) public @interface MmTelCapability {} @@ -301,6 +303,11 @@ public class MmTelFeature extends ImsFeature { public static final int CAPABILITY_TYPE_SMS = 1 << 3; /** + * This MmTelFeature supports Call Composer (section 2.4 of RC.20) + */ + public static final int CAPABILITY_TYPE_CALL_COMPOSER = 1 << 4; + + /** * @hide */ @Override @@ -343,6 +350,8 @@ public class MmTelFeature extends ImsFeature { builder.append(isCapable(CAPABILITY_TYPE_UT)); builder.append(" SMS: "); builder.append(isCapable(CAPABILITY_TYPE_SMS)); + builder.append(" CALL_COMPOSER: "); + builder.append(isCapable(CAPABILITY_TYPE_CALL_COMPOSER)); builder.append("]"); return builder.toString(); } diff --git a/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java b/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java index b5704bfb3569..3a0fb6edb2fb 100644 --- a/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java +++ b/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java @@ -250,7 +250,7 @@ public class RcsCapabilityExchangeImplBase { * This allows the framework to know that there will no longer be any * capability updates for the requested operationToken. */ - void onTerminated(String reason, String retryAfter) throws ImsException; + void onTerminated(String reason, long retryAfterMilliseconds) throws ImsException; } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt index a20f96d17278..67deca4fe387 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt @@ -16,6 +16,7 @@ package com.android.server.wm.flicker.close +import androidx.test.filters.FlakyTest import android.view.Surface import androidx.test.filters.RequiresDevice import androidx.test.platform.app.InstrumentationRegistry @@ -54,6 +55,7 @@ import org.junit.runners.Parameterized @RequiresDevice @RunWith(Parameterized::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) +@FlakyTest(bugId = 174635878) class CloseAppBackButtonTest( testName: String, flickerSpec: Flicker diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt index 4bbb38c4d71a..252ce2a32bf0 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt @@ -16,6 +16,7 @@ package com.android.server.wm.flicker.close +import androidx.test.filters.FlakyTest import android.view.Surface import androidx.test.filters.RequiresDevice import androidx.test.platform.app.InstrumentationRegistry @@ -54,6 +55,7 @@ import org.junit.runners.Parameterized @RequiresDevice @RunWith(Parameterized::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) +@FlakyTest(bugId = 174635878) class CloseAppHomeButtonTest( testName: String, flickerSpec: Flicker diff --git a/tests/Input/Android.bp b/tests/Input/Android.bp index a8aab2a899fb..a72b07c45dd8 100644 --- a/tests/Input/Android.bp +++ b/tests/Input/Android.bp @@ -6,6 +6,7 @@ android_test { static_libs: [ "androidx.test.ext.junit", "androidx.test.rules", + "truth-prebuilt", "ub-uiautomator", ], test_suites: ["device-tests"], diff --git a/tests/Input/src/com/android/test/input/ViewFrameInfoTest.kt b/tests/Input/src/com/android/test/input/ViewFrameInfoTest.kt new file mode 100644 index 000000000000..f919a3eaf271 --- /dev/null +++ b/tests/Input/src/com/android/test/input/ViewFrameInfoTest.kt @@ -0,0 +1,78 @@ +/* + * 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.test.input + +import android.graphics.FrameInfo +import android.os.SystemClock +import android.view.ViewFrameInfo +import com.google.common.truth.Truth.assertThat +import org.junit.Before +import org.junit.Test + +class ViewFrameInfoTest { + companion object { + private const val TAG = "ViewFrameInfoTest" + } + private val mViewFrameInfo = ViewFrameInfo() + private var mTimeStarted: Long = 0 + + @Before + fun setUp() { + mViewFrameInfo.reset() + mViewFrameInfo.updateOldestInputEvent(10) + mViewFrameInfo.updateNewestInputEvent(20) + mViewFrameInfo.flags = mViewFrameInfo.flags or FrameInfo.FLAG_WINDOW_LAYOUT_CHANGED + mTimeStarted = SystemClock.uptimeNanos() + mViewFrameInfo.markDrawStart() + } + + @Test + fun testPopulateFields() { + assertThat(mViewFrameInfo.drawStart).isGreaterThan(mTimeStarted) + assertThat(mViewFrameInfo.oldestInputEventTime).isEqualTo(10) + assertThat(mViewFrameInfo.newestInputEventTime).isEqualTo(20) + assertThat(mViewFrameInfo.flags).isEqualTo(FrameInfo.FLAG_WINDOW_LAYOUT_CHANGED) + } + + @Test + fun testReset() { + mViewFrameInfo.reset() + // Ensure that the original object is reset correctly + assertThat(mViewFrameInfo.drawStart).isEqualTo(0) + assertThat(mViewFrameInfo.oldestInputEventTime).isEqualTo(0) + assertThat(mViewFrameInfo.newestInputEventTime).isEqualTo(0) + assertThat(mViewFrameInfo.flags).isEqualTo(0) + } + + @Test + fun testUpdateFrameInfoFromViewFrameInfo() { + val frameInfo = FrameInfo() + // By default, all values should be zero + assertThat(frameInfo.frameInfo[FrameInfo.OLDEST_INPUT_EVENT]).isEqualTo(0) + assertThat(frameInfo.frameInfo[FrameInfo.NEWEST_INPUT_EVENT]).isEqualTo(0) + assertThat(frameInfo.frameInfo[FrameInfo.FLAGS]).isEqualTo(0) + assertThat(frameInfo.frameInfo[FrameInfo.DRAW_START]).isEqualTo(0) + + // The values inside FrameInfo should match those from ViewFrameInfo after we update them + mViewFrameInfo.populateFrameInfo(frameInfo) + assertThat(frameInfo.frameInfo[FrameInfo.OLDEST_INPUT_EVENT]).isEqualTo(10) + assertThat(frameInfo.frameInfo[FrameInfo.NEWEST_INPUT_EVENT]).isEqualTo(20) + assertThat(frameInfo.frameInfo[FrameInfo.FLAGS]).isEqualTo( + FrameInfo.FLAG_WINDOW_LAYOUT_CHANGED) + assertThat(frameInfo.frameInfo[FrameInfo.DRAW_START]).isGreaterThan(mTimeStarted) + } +}
\ No newline at end of file diff --git a/tests/StagedInstallTest/Android.bp b/tests/StagedInstallTest/Android.bp index f55d4d474bfb..3a40696d1add 100644 --- a/tests/StagedInstallTest/Android.bp +++ b/tests/StagedInstallTest/Android.bp @@ -31,7 +31,6 @@ java_test_host { "testng", "compatibility-tradefed", "frameworks-base-hostutils", - "module_test_util", "cts-install-lib-host", ], data: [ diff --git a/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java b/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java index 27ccbc78cf96..9e1ea2e04528 100644 --- a/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java +++ b/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java @@ -29,7 +29,6 @@ import android.cts.install.lib.host.InstallUtilsHost; import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper; import com.android.ddmlib.Log; import com.android.tests.rollback.host.AbandonSessionsRule; -import com.android.tests.util.ModuleTestUtils; import com.android.tradefed.device.DeviceNotAvailableException; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test; @@ -60,7 +59,6 @@ public class StagedInstallInternalTest extends BaseHostJUnit4Test { private static final String APK_A = "TestAppAv1.apk"; private static final String APK_IN_APEX_TESTAPEX_NAME = "com.android.apex.apkrollback.test"; - private final ModuleTestUtils mTestUtils = new ModuleTestUtils(this); private final InstallUtilsHost mHostUtils = new InstallUtilsHost(this); /** @@ -161,7 +159,7 @@ public class StagedInstallInternalTest extends BaseHostJUnit4Test { assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported()); - final File apexFile = mTestUtils.getTestFile(SHIM_V2); + final File apexFile = mHostUtils.getTestFile(SHIM_V2); final String output = getDevice().executeAdbCommand("install", "--staged", "--staged-ready-timeout", "60000", apexFile.getAbsolutePath()); assertThat(output).contains("Reboot device to apply staged session"); @@ -176,7 +174,7 @@ public class StagedInstallInternalTest extends BaseHostJUnit4Test { assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported()); - final File apexFile = mTestUtils.getTestFile(SHIM_V2); + final File apexFile = mHostUtils.getTestFile(SHIM_V2); final String output = getDevice().executeAdbCommand("install", "--staged", apexFile.getAbsolutePath()); assertThat(output).contains("Reboot device to apply staged session"); @@ -191,7 +189,7 @@ public class StagedInstallInternalTest extends BaseHostJUnit4Test { assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported()); - final File apexFile = mTestUtils.getTestFile(SHIM_V2); + final File apexFile = mHostUtils.getTestFile(SHIM_V2); final String output = getDevice().executeAdbCommand("install", "--staged", "--staged-ready-timeout", "0", apexFile.getAbsolutePath()); assertThat(output).doesNotContain("Reboot device to apply staged session"); @@ -207,7 +205,7 @@ public class StagedInstallInternalTest extends BaseHostJUnit4Test { assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported()); - final File apexFile = mTestUtils.getTestFile(SHIM_V2); + final File apexFile = mHostUtils.getTestFile(SHIM_V2); String output = getDevice().executeAdbCommand("install", "--staged", "--enable-rollback", apexFile.getAbsolutePath()); assertThat(output).contains("Reboot device to apply staged session"); @@ -224,8 +222,8 @@ public class StagedInstallInternalTest extends BaseHostJUnit4Test { assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported()); - final File apexFile = mTestUtils.getTestFile(SHIM_V2); - final File apkFile = mTestUtils.getTestFile(APK_A); + final File apexFile = mHostUtils.getTestFile(SHIM_V2); + final File apkFile = mHostUtils.getTestFile(APK_A); final String output = getDevice().executeAdbCommand("install-multi-package", apexFile.getAbsolutePath(), apkFile.getAbsolutePath()); assertThat(output).contains("Created parent session"); diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java index ba87dc50e246..5d4573716145 100644 --- a/tests/net/java/com/android/server/ConnectivityServiceTest.java +++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java @@ -56,8 +56,10 @@ import static android.net.NetworkCapabilities.NET_CAPABILITY_IA; import static android.net.NetworkCapabilities.NET_CAPABILITY_IMS; import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET; import static android.net.NetworkCapabilities.NET_CAPABILITY_MMS; +import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED; +import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN; import static android.net.NetworkCapabilities.NET_CAPABILITY_PARTIAL_CONNECTIVITY; @@ -5398,6 +5400,106 @@ public class ConnectivityServiceTest { } @Test + public void testApplyUnderlyingCapabilities() throws Exception { + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + mCellNetworkAgent.connect(false /* validated */); + mWiFiNetworkAgent.connect(false /* validated */); + + final NetworkCapabilities cellNc = new NetworkCapabilities() + .addTransportType(TRANSPORT_CELLULAR) + .addCapability(NET_CAPABILITY_INTERNET) + .addCapability(NET_CAPABILITY_NOT_CONGESTED) + .setLinkDownstreamBandwidthKbps(10); + final NetworkCapabilities wifiNc = new NetworkCapabilities() + .addTransportType(TRANSPORT_WIFI) + .addCapability(NET_CAPABILITY_INTERNET) + .addCapability(NET_CAPABILITY_NOT_METERED) + .addCapability(NET_CAPABILITY_NOT_ROAMING) + .addCapability(NET_CAPABILITY_NOT_CONGESTED) + .addCapability(NET_CAPABILITY_NOT_SUSPENDED) + .setLinkUpstreamBandwidthKbps(20); + mCellNetworkAgent.setNetworkCapabilities(cellNc, true /* sendToConnectivityService */); + mWiFiNetworkAgent.setNetworkCapabilities(wifiNc, true /* sendToConnectivityService */); + waitForIdle(); + + final Network mobile = mCellNetworkAgent.getNetwork(); + final Network wifi = mWiFiNetworkAgent.getNetwork(); + + final NetworkCapabilities initialCaps = new NetworkCapabilities(); + initialCaps.addCapability(NET_CAPABILITY_INTERNET); + initialCaps.removeCapability(NET_CAPABILITY_NOT_VPN); + + final NetworkCapabilities withNoUnderlying = new NetworkCapabilities(); + withNoUnderlying.addCapability(NET_CAPABILITY_INTERNET); + withNoUnderlying.addCapability(NET_CAPABILITY_NOT_CONGESTED); + withNoUnderlying.addCapability(NET_CAPABILITY_NOT_ROAMING); + withNoUnderlying.addCapability(NET_CAPABILITY_NOT_SUSPENDED); + withNoUnderlying.addTransportType(TRANSPORT_VPN); + withNoUnderlying.removeCapability(NET_CAPABILITY_NOT_VPN); + + final NetworkCapabilities withMobileUnderlying = new NetworkCapabilities(withNoUnderlying); + withMobileUnderlying.addTransportType(TRANSPORT_CELLULAR); + withMobileUnderlying.removeCapability(NET_CAPABILITY_NOT_ROAMING); + withMobileUnderlying.removeCapability(NET_CAPABILITY_NOT_SUSPENDED); + withMobileUnderlying.setLinkDownstreamBandwidthKbps(10); + + final NetworkCapabilities withWifiUnderlying = new NetworkCapabilities(withNoUnderlying); + withWifiUnderlying.addTransportType(TRANSPORT_WIFI); + withWifiUnderlying.addCapability(NET_CAPABILITY_NOT_METERED); + withWifiUnderlying.setLinkUpstreamBandwidthKbps(20); + + final NetworkCapabilities withWifiAndMobileUnderlying = + new NetworkCapabilities(withNoUnderlying); + withWifiAndMobileUnderlying.addTransportType(TRANSPORT_CELLULAR); + withWifiAndMobileUnderlying.addTransportType(TRANSPORT_WIFI); + withWifiAndMobileUnderlying.removeCapability(NET_CAPABILITY_NOT_METERED); + withWifiAndMobileUnderlying.removeCapability(NET_CAPABILITY_NOT_ROAMING); + withWifiAndMobileUnderlying.setLinkDownstreamBandwidthKbps(10); + withWifiAndMobileUnderlying.setLinkUpstreamBandwidthKbps(20); + + NetworkCapabilities caps = new NetworkCapabilities(initialCaps); + final boolean notDeclaredMetered = false; + mService.applyUnderlyingCapabilities(new Network[]{}, caps, notDeclaredMetered); + assertEquals(withNoUnderlying, caps); + + caps = new NetworkCapabilities(initialCaps); + mService.applyUnderlyingCapabilities(new Network[]{null}, caps, notDeclaredMetered); + assertEquals(withNoUnderlying, caps); + + caps = new NetworkCapabilities(initialCaps); + mService.applyUnderlyingCapabilities(new Network[]{mobile}, caps, notDeclaredMetered); + assertEquals(withMobileUnderlying, caps); + + mService.applyUnderlyingCapabilities(new Network[]{wifi}, caps, notDeclaredMetered); + assertEquals(withWifiUnderlying, caps); + + final boolean isDeclaredMetered = true; + withWifiUnderlying.removeCapability(NET_CAPABILITY_NOT_METERED); + caps = new NetworkCapabilities(initialCaps); + mService.applyUnderlyingCapabilities(new Network[]{wifi}, caps, isDeclaredMetered); + assertEquals(withWifiUnderlying, caps); + + caps = new NetworkCapabilities(initialCaps); + mService.applyUnderlyingCapabilities(new Network[]{mobile, wifi}, caps, isDeclaredMetered); + assertEquals(withWifiAndMobileUnderlying, caps); + + withWifiUnderlying.addCapability(NET_CAPABILITY_NOT_METERED); + caps = new NetworkCapabilities(initialCaps); + mService.applyUnderlyingCapabilities(new Network[]{null, mobile, null, wifi}, + caps, notDeclaredMetered); + assertEquals(withWifiAndMobileUnderlying, caps); + + caps = new NetworkCapabilities(initialCaps); + mService.applyUnderlyingCapabilities(new Network[]{null, mobile, null, wifi}, + caps, notDeclaredMetered); + assertEquals(withWifiAndMobileUnderlying, caps); + + mService.applyUnderlyingCapabilities(null, caps, notDeclaredMetered); + assertEquals(withWifiUnderlying, caps); + } + + @Test public void testVpnConnectDisconnectUnderlyingNetwork() throws Exception { final TestNetworkCallback callback = new TestNetworkCallback(); final NetworkRequest request = new NetworkRequest.Builder() @@ -5947,17 +6049,28 @@ public class ConnectivityServiceTest { && caps.hasTransport(TRANSPORT_VPN) && caps.hasTransport(TRANSPORT_WIFI)); + // Change the VPN's capabilities somehow (specifically, disconnect wifi). + mWiFiNetworkAgent.disconnect(); + callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); + callback.expectCapabilitiesThat(mMockVpn, (caps) + -> caps.getUids().size() == 2 + && caps.getUids().contains(new UidRange(uid, uid)) + && caps.getUids().contains(UidRange.createForUser(restrictedUserId)) + && caps.hasTransport(TRANSPORT_VPN) + && !caps.hasTransport(TRANSPORT_WIFI)); + // Send a USER_REMOVED broadcast and expect to lose the UID range for the restricted user. final Intent removedIntent = new Intent(ACTION_USER_REMOVED); removedIntent.putExtra(Intent.EXTRA_USER_HANDLE, restrictedUserId); handler.post(() -> mServiceContext.sendBroadcast(removedIntent)); - // Expect that the VPN gains the UID range for the restricted user. + // Expect that the VPN gains the UID range for the restricted user, and that the capability + // change made just before that (i.e., loss of TRANSPORT_WIFI) is preserved. callback.expectCapabilitiesThat(mMockVpn, (caps) -> caps.getUids().size() == 1 && caps.getUids().contains(new UidRange(uid, uid)) && caps.hasTransport(TRANSPORT_VPN) - && caps.hasTransport(TRANSPORT_WIFI)); + && !caps.hasTransport(TRANSPORT_WIFI)); } @Test diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/tests/net/java/com/android/server/connectivity/VpnTest.java index d0db55f2bb13..337507ac1d46 100644 --- a/tests/net/java/com/android/server/connectivity/VpnTest.java +++ b/tests/net/java/com/android/server/connectivity/VpnTest.java @@ -21,15 +21,6 @@ import static android.content.pm.UserInfo.FLAG_MANAGED_PROFILE; import static android.content.pm.UserInfo.FLAG_PRIMARY; import static android.content.pm.UserInfo.FLAG_RESTRICTED; import static android.net.ConnectivityManager.NetworkCallback; -import static android.net.NetworkCapabilities.LINK_BANDWIDTH_UNSPECIFIED; -import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET; -import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED; -import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED; -import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING; -import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED; -import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; -import static android.net.NetworkCapabilities.TRANSPORT_VPN; -import static android.net.NetworkCapabilities.TRANSPORT_WIFI; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; @@ -621,102 +612,6 @@ public class VpnTest { order.verify(mNotificationManager).cancel(anyString(), anyInt()); } - @Test - public void testCapabilities() { - setMockedUsers(primaryUser); - - final Network mobile = new Network(1); - final Network wifi = new Network(2); - - final Map<Network, NetworkCapabilities> networks = new HashMap<>(); - networks.put( - mobile, - new NetworkCapabilities() - .addTransportType(TRANSPORT_CELLULAR) - .addCapability(NET_CAPABILITY_INTERNET) - .addCapability(NET_CAPABILITY_NOT_CONGESTED) - .setLinkDownstreamBandwidthKbps(10)); - networks.put( - wifi, - new NetworkCapabilities() - .addTransportType(TRANSPORT_WIFI) - .addCapability(NET_CAPABILITY_INTERNET) - .addCapability(NET_CAPABILITY_NOT_METERED) - .addCapability(NET_CAPABILITY_NOT_ROAMING) - .addCapability(NET_CAPABILITY_NOT_CONGESTED) - .addCapability(NET_CAPABILITY_NOT_SUSPENDED) - .setLinkUpstreamBandwidthKbps(20)); - setMockedNetworks(networks); - - final NetworkCapabilities caps = new NetworkCapabilities(); - - Vpn.applyUnderlyingCapabilities( - mConnectivityManager, new Network[] {}, caps, false /* isAlwaysMetered */); - assertTrue(caps.hasTransport(TRANSPORT_VPN)); - assertFalse(caps.hasTransport(TRANSPORT_CELLULAR)); - assertFalse(caps.hasTransport(TRANSPORT_WIFI)); - assertEquals(LINK_BANDWIDTH_UNSPECIFIED, caps.getLinkDownstreamBandwidthKbps()); - assertEquals(LINK_BANDWIDTH_UNSPECIFIED, caps.getLinkUpstreamBandwidthKbps()); - assertFalse(caps.hasCapability(NET_CAPABILITY_NOT_METERED)); - assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_ROAMING)); - assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_CONGESTED)); - assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED)); - - Vpn.applyUnderlyingCapabilities( - mConnectivityManager, - new Network[] {mobile}, - caps, - false /* isAlwaysMetered */); - assertTrue(caps.hasTransport(TRANSPORT_VPN)); - assertTrue(caps.hasTransport(TRANSPORT_CELLULAR)); - assertFalse(caps.hasTransport(TRANSPORT_WIFI)); - assertEquals(10, caps.getLinkDownstreamBandwidthKbps()); - assertEquals(LINK_BANDWIDTH_UNSPECIFIED, caps.getLinkUpstreamBandwidthKbps()); - assertFalse(caps.hasCapability(NET_CAPABILITY_NOT_METERED)); - assertFalse(caps.hasCapability(NET_CAPABILITY_NOT_ROAMING)); - assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_CONGESTED)); - assertFalse(caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED)); - - Vpn.applyUnderlyingCapabilities( - mConnectivityManager, new Network[] {wifi}, caps, false /* isAlwaysMetered */); - assertTrue(caps.hasTransport(TRANSPORT_VPN)); - assertFalse(caps.hasTransport(TRANSPORT_CELLULAR)); - assertTrue(caps.hasTransport(TRANSPORT_WIFI)); - assertEquals(LINK_BANDWIDTH_UNSPECIFIED, caps.getLinkDownstreamBandwidthKbps()); - assertEquals(20, caps.getLinkUpstreamBandwidthKbps()); - assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_METERED)); - assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_ROAMING)); - assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_CONGESTED)); - assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED)); - - Vpn.applyUnderlyingCapabilities( - mConnectivityManager, new Network[] {wifi}, caps, true /* isAlwaysMetered */); - assertTrue(caps.hasTransport(TRANSPORT_VPN)); - assertFalse(caps.hasTransport(TRANSPORT_CELLULAR)); - assertTrue(caps.hasTransport(TRANSPORT_WIFI)); - assertEquals(LINK_BANDWIDTH_UNSPECIFIED, caps.getLinkDownstreamBandwidthKbps()); - assertEquals(20, caps.getLinkUpstreamBandwidthKbps()); - assertFalse(caps.hasCapability(NET_CAPABILITY_NOT_METERED)); - assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_ROAMING)); - assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_CONGESTED)); - assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED)); - - Vpn.applyUnderlyingCapabilities( - mConnectivityManager, - new Network[] {mobile, wifi}, - caps, - false /* isAlwaysMetered */); - assertTrue(caps.hasTransport(TRANSPORT_VPN)); - assertTrue(caps.hasTransport(TRANSPORT_CELLULAR)); - assertTrue(caps.hasTransport(TRANSPORT_WIFI)); - assertEquals(10, caps.getLinkDownstreamBandwidthKbps()); - assertEquals(20, caps.getLinkUpstreamBandwidthKbps()); - assertFalse(caps.hasCapability(NET_CAPABILITY_NOT_METERED)); - assertFalse(caps.hasCapability(NET_CAPABILITY_NOT_ROAMING)); - assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_CONGESTED)); - assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED)); - } - /** * The profile name should NOT change between releases for backwards compatibility * diff --git a/wifi/Android.bp b/wifi/Android.bp index 52e3840c126e..7a9ca6012be5 100644 --- a/wifi/Android.bp +++ b/wifi/Android.bp @@ -147,7 +147,7 @@ java_sdk_library { // defaults for tests that need to build against framework-wifi's @hide APIs java_defaults { name: "framework-wifi-test-defaults", - sdk_version: "core_platform", // tests can use @CorePlatformApi's + sdk_version: "core_current", libs: [ // order matters: classes in framework-wifi are resolved before framework, meaning // @hide APIs in framework-wifi are resolved before @SystemApi stubs in framework diff --git a/wifi/tests/Android.bp b/wifi/tests/Android.bp index 7272e146f4f8..7eba0a73be31 100644 --- a/wifi/tests/Android.bp +++ b/wifi/tests/Android.bp @@ -20,6 +20,9 @@ android_test { defaults: ["framework-wifi-test-defaults"], + min_sdk_version: "30", + target_sdk_version: "30", + srcs: ["**/*.java"], jacoco: { |